From 54811c863340f7f0a44b8d9232cf140572a402f0 Mon Sep 17 00:00:00 2001 From: pedromml Date: Tue, 4 Jun 2024 04:29:43 -0300 Subject: [PATCH] Separate logic in another file: uiSourceSubfield --- data/core.yaml | 2 +- modules/ui/field.js | 189 ++-------------------------------- modules/ui/index.js | 1 + modules/ui/source_subfield.js | 170 ++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 181 deletions(-) create mode 100644 modules/ui/source_subfield.js diff --git a/data/core.yaml b/data/core.yaml index a5edac7c94..ec41b0d1e3 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -800,7 +800,7 @@ en: edtf_placeholder: 1849~, 1804?, 189X, 1906/1908, 1814-23, 1960-05-01T13:00... field_source: Add Source field_source_label: Source - field_source_placeholder: https://... + field_source_placeholder: URL, newspaper article, book... max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." set_today: "Sets the value to today." background: diff --git a/modules/ui/field.js b/modules/ui/field.js index 6281c9cf83..b84cb056a9 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -9,7 +9,8 @@ import { geoExtent } from '../geo/extent'; import { uiFieldHelp } from './field_help'; import { uiFields } from './fields'; import { uiTagReference } from './tag_reference'; -import { utilGetSetValue, utilRebind, utilUniqueDomId } from '../util'; +import { utilRebind, utilUniqueDomId } from '../util'; +import { uiSourceSubfield } from './source_subfield'; export function uiField(context, presetField, entityIDs, options) { @@ -27,9 +28,6 @@ export function uiField(context, presetField, entityIDs, options) { var _show = options.show; var _state = ''; var _tags = {}; - let sourceInput = d3_select(null); - let _sourceValue; - let sourceKey = field.key + ':source'; options.source = field.source !== undefined ? field.source : true; @@ -129,150 +127,6 @@ export function uiField(context, presetField, entityIDs, options) { dispatch.call('change', d, t); } - - function renderSourceInput(selection) { - console.log('renderSourceInput'); - let entries = selection.selectAll('div.entry') - .data((typeof _sourceValue === 'string' || Array.isArray(_sourceValue)) ? [_sourceValue] : []); - - entries.exit() - .style('top', '0') - .style('max-height', '240px') - .transition() - .duration(200) - .style('opacity', '0') - .style('max-height', '0px') - .remove(); - - let entriesEnter = entries.enter() - .append('div') - .attr('class', 'entry') - .each(function() { - var wrap = d3_select(this); - - let domId = utilUniqueDomId('source-' + field.safeid); - let label = wrap - .append('label') - .attr('class', 'field-label') - .attr('for', domId); - - let text = label - .append('span') - .attr('class', 'label-text'); - - text - .append('span') - .attr('class', 'label-textvalue') - .call(t.append('inspector.field_source_label')); - - text - .append('span') - .attr('class', 'label-textannotation'); - - label - .append('button') - .attr('class', 'remove-icon-source') // 'remove-icon-edtf' - .attr('title', t('icons.remove')) - .on('click', function(d3_event) { - d3_event.preventDefault(); - - // remove the UI item manually - _sourceValue = undefined; - - if (sourceKey && sourceKey in _tags) { - delete _tags[sourceKey]; - // remove from entity tags - let t = {}; - t[sourceKey] = undefined; - dispatch.call('change', this, t); - return; - } - - renderSourceInput(selection); - }) - .call(svgIcon('#iD-operation-delete')); - - wrap - .append('input') - .attr('type', 'text') - .attr('class', 'field-source-value') - .on('blur', changeSourceValue) - .on('change', changeSourceValue); - }); - - entriesEnter - .style('margin-top', '0px') - .style('max-height', '0px') - .style('opacity', '0') - .transition() - .duration(200) - .style('margin-top', '10px') - .style('max-height', '240px') - .style('opacity', '1') - .on('end', function() { - d3_select(this) - .style('max-height', '') - .style('overflow', 'visible'); - }); - - entries = entries.merge(entriesEnter); - - entries.order(); - - // allow removing the entry UIs even if there isn't a tag to remove - entries.classed('present', true); - - utilGetSetValue(entries.select('.field-source-value'), function(d) { - return typeof d === 'string' ? d : ''; - }) - .attr('title', function(d) { - return Array.isArray(d) ? d.filter(Boolean).join('\n') : null; - }) - .attr('placeholder', function(d) { - return Array.isArray(d) ? t('inspector.multiple_values') : t('inspector.field_source_placeholder'); - }) - .classed('mixed', function(d) { - return Array.isArray(d); - }); - } - - function changeSourceValue(d3_event, d) { - console.log('changeSourceValue'); - let value = context.cleanTagValue(utilGetSetValue(d3_select(this))) || undefined; - console.log('sourceTagValue'); - console.log(value); - // don't override multiple values with blank string - if (!value && Array.isArray(d.value)) return; - - let t = {}; - t[sourceKey] = value; - d.value = value; - dispatch.call('change', this, t); - } - - function addSource(d3_event, d) { - console.log('addSource'); - d3_event.preventDefault(); - - if (typeof _sourceValue !== 'string' && !Array.isArray(_sourceValue)) { - _sourceValue = ''; - - sourceInput.call(renderSourceInput); - } - - } - - function calcSourceValue(tags) { - console.log('calcSourceValue'); - if (_sourceValue && !tags[sourceKey]) { - // Don't unset the variable based on deleted tags, since this makes the UI - // disappear unexpectedly when clearing values - #8164 - _sourceValue = ''; - } else { - _sourceValue = tags[sourceKey]; - } - } - field.render = function(selection) { var container = selection.selectAll('.form-field') .data([field]); @@ -319,26 +173,15 @@ export function uiField(context, presetField, entityIDs, options) { } } - if(options.source){ - let sourceButtonTip = uiTooltip() - .title(() => t.append('inspector.field_source')) - .placement('left'); - - labelEnter - .append('button') - .attr('class', 'source-icon') - .attr('title', 'source-button') - .call(sourceButtonTip) - .call(svgIcon('#iD-icon-note')); - } - - if (_tags && _sourceValue === undefined) { - calcSourceValue(_tags); - } + var sourceSubfield = uiSourceSubfield(field, _tags, dispatch); // Update container = container - .merge(enter); + .merge(enter); + + if(options.source){ + sourceSubfield.button(labelEnter, container); + } container.select('.field-label > .remove-icon') // propagate bound data .on('click', remove); @@ -346,9 +189,6 @@ export function uiField(context, presetField, entityIDs, options) { container.select('.field-label > .modified-icon') // propagate bound data .on('click', revert); - container.select('.field-label > .source-icon') // propagate bound data - .on('click', addSource); - container .each(function(d) { var selection = d3_select(this); @@ -427,16 +267,7 @@ export function uiField(context, presetField, entityIDs, options) { container.call(_locked ? _lockedTip : _lockedTip.destroy); if(options.source){ - sourceInput = selection.selectChild().selectAll('.field-source') - .data([0]); - - sourceInput = sourceInput.enter() - .append('div') - .attr('class', 'field-source') - .merge(sourceInput); - - sourceInput - .call(renderSourceInput); + sourceSubfield.body(selection); } }; @@ -460,8 +291,6 @@ export function uiField(context, presetField, entityIDs, options) { } } - calcSourceValue(_tags); - return field; }; diff --git a/modules/ui/index.js b/modules/ui/index.js index 2ffbdd11b0..a7a362bda6 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -49,6 +49,7 @@ export { uiPresetList } from './preset_list'; export { uiRestore } from './restore'; export { uiScale } from './scale'; export { uiSidebar } from './sidebar'; +export { uiSourceSubfield } from './source_subfield'; export { uiSourceSwitch } from './source_switch'; export { uiSpinner } from './spinner'; export { uiSplash } from './splash'; diff --git a/modules/ui/source_subfield.js b/modules/ui/source_subfield.js new file mode 100644 index 0000000000..cb27f95af5 --- /dev/null +++ b/modules/ui/source_subfield.js @@ -0,0 +1,170 @@ +import { + select as d3_select +} from 'd3-selection'; + +import { t } from '../core/localizer'; +import { svgIcon } from '../svg/icon'; +import { uiTooltip } from './tooltip'; +import { utilGetSetValue, utilUniqueDomId } from '../util'; + +export function uiSourceSubfield(field, tags, dispatch) { + + var sourceSubfield = {}; + + let sourceInput = d3_select(null); + let sourceKey = field.key + ':source'; + let _sourceValue = tags[sourceKey]; + + function renderSourceInput(selection) { + let entries = selection.selectAll('div.entry') + .data((typeof _sourceValue === 'string' || Array.isArray(_sourceValue)) ? [_sourceValue] : []); + + entries.exit() + .style('top', '0') + .style('max-height', '240px') + .transition() + .duration(200) + .style('opacity', '0') + .style('max-height', '0px') + .remove(); + + let entriesEnter = entries.enter() + .append('div') + .attr('class', 'entry') + .each(function() { + var wrap = d3_select(this); + + let domId = utilUniqueDomId('source-' + field.safeid); + let label = wrap + .append('label') + .attr('class', 'field-label') + .attr('for', domId); + + let text = label + .append('span') + .attr('class', 'label-text'); + + text + .append('span') + .attr('class', 'label-textvalue') + .call(t.append('inspector.field_source_label')); + + text + .append('span') + .attr('class', 'label-textannotation'); + + label + .append('button') + .attr('class', 'remove-icon-source') + .attr('title', t('icons.remove')) + .on('click', function(d3_event) { + d3_event.preventDefault(); + + // remove the UI item manually + _sourceValue = undefined; + + let t = {}; + t[sourceKey] = undefined; + dispatch.call('change', this, t); + + renderSourceInput(selection); + }) + .call(svgIcon('#iD-operation-delete')); + + wrap + .append('input') + .attr('type', 'text') + .attr('class', 'field-source-value') + .on('blur', changeSourceValue) + .on('change', changeSourceValue); + }); + + entriesEnter + .style('margin-top', '0px') + .style('max-height', '0px') + .style('opacity', '0') + .transition() + .duration(200) + .style('margin-top', '10px') + .style('max-height', '240px') + .style('opacity', '1') + .on('end', function() { + d3_select(this) + .style('max-height', '') + .style('overflow', 'visible'); + }); + + entries = entries.merge(entriesEnter); + + entries.order(); + + // allow removing the entry UIs even if there isn't a tag to remove + entries.classed('present', true); + + utilGetSetValue(entries.select('.field-source-value'), function(d) { + return typeof d === 'string' ? d : ''; + }) + .attr('title', function(d) { + return Array.isArray(d) ? d.filter(Boolean).join('\n') : null; + }) + .attr('placeholder', function(d) { + return Array.isArray(d) ? t('inspector.multiple_values') : t('inspector.field_source_placeholder'); + }) + .classed('mixed', function(d) { + return Array.isArray(d); + }); + } + + function changeSourceValue(d3_event, d) { + let value = context.cleanTagValue(utilGetSetValue(d3_select(this))) || undefined; + // don't override multiple values with blank string + if (!value && Array.isArray(d.value)) return; + + let t = {}; + t[sourceKey] = value; + d.value = value; + dispatch.call('change', this, t); + } + + function addSource(d3_event, d) { + d3_event.preventDefault(); + + if (typeof _sourceValue !== 'string' && !Array.isArray(_sourceValue)) { + _sourceValue = ''; + } + sourceInput.call(renderSourceInput); + + } + + sourceSubfield.button = function(labelEnter, container) { + let sourceButtonTip = uiTooltip() + .title(() => t.append('inspector.field_source')) + .placement('left'); + + labelEnter + .append('button') + .attr('class', 'source-icon') + .attr('title', 'source-button') + .call(sourceButtonTip) + .call(svgIcon('#fas-book')); + + container.select('.field-label > .source-icon') // propagate bound data + .on('click', addSource); + }; + + + sourceSubfield.body = function(selection) { + sourceInput = selection.selectChild().selectAll('.field-source') + .data([0]); + + sourceInput = sourceInput.enter() + .append('div') + .attr('class', 'field-source') + .merge(sourceInput); + + sourceInput + .call(renderSourceInput); + }; + + return sourceSubfield; +}