diff --git a/css/80_app.css b/css/80_app.css index ce2ba22353..1291ba4232 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1591,13 +1591,15 @@ input.date-selector { display: flex; flex-flow: row nowrap; } -.form-field ul.rows li.labeled-input > div { +.form-field ul.rows li.labeled-input > div, +.form-field ul.rows li.labeled-input-source > div { flex: 1 1 auto; width: 100%; border-radius: 0; line-height: 0.95rem; } -.form-field ul.rows li.labeled-input div > span{ +.form-field ul.rows li.labeled-input div > span, +.form-field ul.rows li.labeled-input-source div > span{ vertical-align: middle; } .form-field ul.rows li.labeled-input-source > div { @@ -1626,10 +1628,12 @@ input.date-selector { display: table; width: 100%; } -.form-field ul.rows.rows-table li.labeled-input { +.form-field ul.rows.rows-table li.labeled-input, +.form-field ul.rows.rows-table li.labeled-input-source { display: table-row; } -.form-field ul.rows.rows-table li.labeled-input > div:first-child { +.form-field ul.rows.rows-table li.labeled-input > div:first-child, +.form-field ul.rows.rows-table li.labeled-input-source > div:first-child { display: table-cell; width: auto; max-width: 170px; @@ -1637,7 +1641,8 @@ input.date-selector { text-overflow: ellipsis; overflow: hidden; } -.form-field ul.rows.rows-table li.labeled-input > div:nth-child(2) { +.form-field ul.rows.rows-table li.labeled-input > div:nth-child(2), +.form-field ul.rows.rows-table li.labeled-input-source > div:nth-child(2) { display: table-cell; width: auto; } diff --git a/data/core.yaml b/data/core.yaml index 9f0ee1d970..b24ad289df 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -802,6 +802,7 @@ en: field_source_label: Source for {field_name} field_source_placeholder: URL, newspaper article, book... source: + main_input: General source name: Name of the source url: https://xyz.com date: Date the source was created diff --git a/modules/ui/fields/sources.js b/modules/ui/fields/sources.js index 1a2ed45dd2..78054ae769 100644 --- a/modules/ui/fields/sources.js +++ b/modules/ui/fields/sources.js @@ -1,57 +1,26 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; import { select as d3_select } from 'd3-selection'; -import { svgIcon } from '../../svg'; -import { uiTooltip } from '../tooltip'; -import { uiCombobox } from '../combobox'; -import { uiFieldMultiCombo } from './combo'; -import { t, localizer } from '../../core/localizer'; -import { utilGetSetValue, utilNoAuto, utilRebind, utilUniqueDomId } from '../../util'; - +import { t } from '../../core/localizer'; +import { utilGetSetValue, utilNoAuto, utilRebind } from '../../util'; export function uiFieldSources(field, context) { let dispatch = d3_dispatch('change'); let items = d3_select(null); - let enter = d3_select(null); let _tags = {}; let _selection = d3_select(null); let _pendingChange; const mainKey = 'source'; - const sourceHeader = 'source:'; - - const possibleSourceSubkeys = [{key:'name', value:'Name'}, {key:'url', value:'URL'}, {key:'date', value:'Date'}] + const sourceHeader = mainKey + ':'; - function addSubkey() { - if(sourceSubkeys.length < possibleSourceSubkeys.length){ - var newKey = possibleSourceSubkeys.filter((k) => sourceSubkeys.map(e => e.key).indexOf(k.key) === -1)[0]; - _tags[sourceHeader + newKey.key] = ''; - scheduleChange(); - _selection.call(sources); - } - } - - function getKeyFromTitle(title) { - return possibleSourceSubkeys.filter((e) => e.value == title)[0].key; - } + // Pre-selected subkey values to show + const possibleSourceSubkeys = [{key:'name', value:'Name'}, {key:'url', value:'URL'}, {key:'date', value:'Date'}]; function scheduleChange() { - // Delay change in case this change is blurring an edited combo. - #5878 - if (!_pendingChange) return; - window.setTimeout(function() { - dispatch.call('change', this, _pendingChange); - _pendingChange = null; - _selection.call(sources); - }, 20); - } - - function removeTag(d3_event, d) { - _pendingChange = _pendingChange || {}; - key = sourceHeader + d.key; - _pendingChange[key] = undefined; - delete _tags[key]; - scheduleChange(); + dispatch.call('change', this, _pendingChange); + _pendingChange = null; _selection.call(sources); } @@ -63,75 +32,50 @@ export function uiFieldSources(field, context) { _pendingChange = _pendingChange || {}; - _pendingChange[key] = context.cleanTagValue(this.value); - _tags[key] = context.cleanTagValue(this.value); - scheduleChange(); - } - - function keyChange(d3_event, d) { - var kOld = sourceHeader + d.key; - var kNew = sourceHeader + getKeyFromTitle(context.cleanTagKey(this.value.trim())); - - _pendingChange = _pendingChange || {}; + var value = context.cleanTagValue(this.value); - if (kOld && _tags[kOld]) { - if (kOld === kNew) return; - // a tag key was renamed - _pendingChange[kNew] = _tags[kOld]; - _tags[kNew] = _tags[kOld]; - _pendingChange[kOld] = undefined; - _tags[kOld] = undefined; - } else { - // a new tag was added - let row = this.parentNode; - let inputVal = d3_select(row).selectAll('input.value'); - let vNew = context.cleanTagValue(utilGetSetValue(inputVal)); - _pendingChange[kNew] = vNew; - utilGetSetValue(inputVal, vNew); - } - var newDatum = possibleSourceSubkeys.filter((e) => e.key == kNew.slice(7))[0]; - d.key = newDatum.key; - d.value = newDatum.value; + _pendingChange[key] = value === '' ? undefined : value; + _tags[key] = value === '' ? undefined : value; scheduleChange(); } - function mainChange(d3_event, d) { + function mainChange() { _pendingChange = _pendingChange || {}; - _pendingChange[mainKey] = context.cleanTagValue(this.value); + var value = context.cleanTagValue(this.value); + _pendingChange[mainKey] = value === '' ? undefined : value; + _tags[mainKey] = value === '' ? undefined : value; scheduleChange(); } function sources(selection) { _selection = selection; - sourceSubkeys = _tags ? Object.keys(_tags) - .filter((key, value) => (key.indexOf(sourceHeader) === 0)) - .map((tag) => { - var data = possibleSourceSubkeys.filter((e) => e.key == tag.slice(7))[0]; - return { - value: data.value, - key: data.key - }; - }) - : - []; - var wrap = selection.selectAll('.form-field-input-wrap') .data([0]); + selection.exit() + .style('top', '0') + .style('max-height', '240px') + .transition() + .duration(200) + .style('opacity', '0') + .style('max-height', '0px') + .remove(); + wrap = wrap.enter() .append('div') .attr('class', 'form-field-input-wrap form-field-input-' + field.type) .merge(wrap); - mainInput = wrap.selectAll('input') + // source key + wrap.selectAll('input') .data([0]) .enter() .append('input') + .attr('class', 'main-value') .attr('type', 'text') - .attr('placeholder', 'Unknown') + .attr('placeholder', t('inspector.source.main_input')) .call(utilNoAuto) - .call(utilGetSetValue, function(d) {return _tags[mainKey]}) .on('change', mainChange) .on('blur', mainChange); @@ -145,84 +89,38 @@ export function uiFieldSources(field, context) { list = list.merge(list); - list.selectAll('li.labeled-input').remove(); - - items = list.selectAll('li.labeled-input') - .data(sourceSubkeys); + // source:*= keys + items = list.selectAll('li.labeled-input-source') + .data(possibleSourceSubkeys); - items.exit() - .remove(); - - enter = items.enter() + items = items.enter() .append('li') - .attr('class', 'labeled-input labeled-input-source'); + .attr('class', 'labeled-input-source'); - enter - .append('input') - .attr('type', 'text') - .call(utilNoAuto) - .each(function(d) { - var key = d3_select(this); - let combo = uiCombobox(context, 'tag-key'); - combo.fetcher(function(value, callback) { - var keyString = utilGetSetValue(key); - var data = possibleSourceSubkeys.filter((key) => - sourceSubkeys - .map((e) => e.key) - .indexOf(key.key) === -1); - callback(data); - }); - combo.minItems(1); - key.call(combo) - .on('change', keyChange) - .on('blur', keyChange) - .call(utilGetSetValue, function(d) { return d.value; }); - }); - - enter + items .append('input') .attr('type', 'text') .attr('class', 'value') - .attr('placeholder', function(d) { - return t('inspector.source.' + d.key) - }) .call(utilNoAuto) - .call(utilGetSetValue, function(d) { return _tags[sourceHeader + d.key]; }) + .call(utilGetSetValue, function(d) { + return _tags[sourceHeader + d.key]; + }) .on('change', valueChange) .on('blur', valueChange); - enter - .append('button') - .attr('class', 'form-field-button remove') - .attr('title', t('icons.remove')) - .call(svgIcon('#iD-operation-delete')) - .on('click', removeTag); - - // Container for the Add button - if(sourceSubkeys.length < possibleSourceSubkeys.length){ - - var addRowEnter = wrap.selectAll('.add-row') - .data([0]) - .enter() - .append('div') - .attr('class', 'add-row'); - - addRowEnter - .append('button') - .attr('class', 'add-tag add-subkey') - .attr('aria-label', t('inspector.add_to_tag')) - .call(svgIcon('#iD-icon-plus', 'light')) - .call(uiTooltip() - .title(() => t.append('inspector.add_to_tag')) - .placement(localizer.textDirection() === 'ltr' ? 'right' : 'left')) - .on('click', addSubkey); - - } - else { - _selection.selectAll('.add-row').remove(); - } - + items.exit() + .remove(); + utilGetSetValue(_selection.selectAll('.value'), function(d) { + return _tags[sourceHeader + d.key]; + }) + .attr('placeholder', function(d) { + return t('inspector.source.' + d.key); + }); + + utilGetSetValue(_selection.selectAll('.main-value'), function() { + return _tags[mainKey]; + }); } sources.tags = function(tags){ @@ -230,7 +128,7 @@ export function uiFieldSources(field, context) { _tags = tags; _selection.call(sources); - } + }; return utilRebind(sources, dispatch, 'on'); } diff --git a/modules/ui/source_subfield.js b/modules/ui/source_subfield.js index d79dbbcf6b..0d18bbde8e 100644 --- a/modules/ui/source_subfield.js +++ b/modules/ui/source_subfield.js @@ -4,7 +4,6 @@ import { import { t } from '../core/localizer'; import { svgIcon } from '../svg/icon'; -import { uiTooltip } from './tooltip'; import { utilGetSetValue, utilUniqueDomId } from '../util'; export function uiSourceSubfield(context, field, tags, dispatch) {