From f19a55fa13001ab34dfc8998a234823b2eb6a925 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 26 May 2023 20:35:12 +0200 Subject: [PATCH] Add support for icons on multiCombo/semiCombo fields (#9433) --- CHANGELOG.md | 2 ++ css/80_app.css | 36 ++++++++++++++++++++++++------- modules/ui/fields/combo.js | 43 +++++++++++++++++++++++++------------- test/spec/presets/field.js | 1 + 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9219d770b..5e61d4326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ _Breaking developer changes, which may affect downstream projects or sites that * Render "right-side" arrows for features with lifecycle prefixes ([#9493], thanks [@k-yle]) * Take regional variants of parent presets into account when resolving preset fields ([#9524]) * Render "right-side" arrows for `man_made=quay` features +* Add support icons also in `multiCombo` and `semiCombo` fields ([#9433]) #### :hammer: Development * Upgrade dependencies: `fortawesome` to v6.4, `which-polygon` to v2.2.1, `glob` to v9.2, `temaki` to v5.4, `marked` to v4.3, `core-js-bundle` to v3.30, `osm-auth` to v2.1 * Bundle `package-lock.json` file in repository for faster `clean-install` builds @@ -73,6 +74,7 @@ _Breaking developer changes, which may affect downstream projects or sites that [#8769]: https://github.com/openstreetmap/iD/pull/8769 [#7427]: https://github.com/openstreetmap/iD/issues/7427 [#9233]: https://github.com/openstreetmap/iD/issues/9233 +[#9433]: https://github.com/openstreetmap/iD/pull/9433 [#9482]: https://github.com/openstreetmap/iD/pull/9482 [#9483]: https://github.com/openstreetmap/iD/pull/9483 [#9492]: https://github.com/openstreetmap/iD/pull/9492 diff --git a/css/80_app.css b/css/80_app.css index 72c0545b6..9fbf2f341 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1640,10 +1640,14 @@ input.date-selector { display: none; } -.form-field-input-combo input.raw-value { +.form-field-input-combo input.raw-value, +.form-field-input-semicombo input.raw-value, +.form-field-input-multicombo input.raw-value { font-family: monospace; } -.form-field-input-combo input.known-value { +.form-field-input-combo input.known-value, +.form-field-input-semicombo input.known-value, +.form-field-input-multicombo input.known-value { color: #7092ff; } @@ -1706,7 +1710,7 @@ input.date-selector { font-style: italic; } -.form-field-input-multicombo li.chip span { +.form-field-input-multicombo li.chip > span { display: block; flex: 1 1 auto; overflow: hidden; @@ -1745,7 +1749,9 @@ input.date-selector { width: auto; } -.form-field-input-combo .tag-value-icon { +.form-field-input-combo .tag-value-icon, +.form-field-input-semicombo .input-wrap .tag-value-icon, +.form-field-input-multicombo .input-wrap .tag-value-icon { display: inline-block; position: relative; height: 24px; @@ -1756,7 +1762,9 @@ input.date-selector { z-index: 1; padding-left: 11px; } -.ideditor[dir='rtl'] .form-field-input-combo .tag-value-icon { +.ideditor[dir='rtl'] .form-field-input-combo .tag-value-icon, +.ideditor[dir='rtl'] .form-field-input-semicombo .input-wrap .tag-value-icon, +.ideditor[dir='rtl'] .form-field-input-multicombo .input-wrap .tag-value-icon { margin-right: 0; margin-left: -30px; padding-left: 0; @@ -1767,16 +1775,30 @@ input.date-selector { height: 21px; margin: auto; } -.ideditor[dir='ltr'] .form-field-input-combo .tag-value-icon + input { +.ideditor[dir='ltr'] .form-field-input-combo .tag-value-icon + input, +.ideditor[dir='ltr'] .form-field-input-semicombo .input-wrap .tag-value-icon + input, +.ideditor[dir='ltr'] .form-field-input-multicombo .input-wrap .tag-value-icon + input { padding-left: 40px; } -.ideditor[dir='rtl'] .form-field-input-combo .tag-value-icon + input { +.ideditor[dir='rtl'] .form-field-input-combo .tag-value-icon + input, +.ideditor[dir='rtl'] .form-field-input-semicombo .input-wrap .tag-value-icon + input, +.ideditor[dir='rtl'] .form-field-input-multicombo .input-wrap .tag-value-icon + input { padding-right: 40px; } .combobox-option .tag-value-icon { display: inline-block; width: 28px; } +.form-field-input-multicombo li.chip .tag-value-icon .icon { + margin: 0; + margin-right: 6px; + display: inline-block; + vertical-align: center; +} +.ideditor[dir='rtl'] .form-field-input-multicombo li.chip .tag-value-icon .icon { + margin-right: 6px; + margin-left: 0; +} /* Field - Text / Numeric diff --git a/modules/ui/fields/combo.js b/modules/ui/fields/combo.js index 06e86ae4a..78066bd70 100644 --- a/modules/ui/fields/combo.js +++ b/modules/ui/fields/combo.js @@ -543,11 +543,15 @@ export function uiFieldCombo(field, context) { function updateIcon(value) { value = tagValue(value); + let container = _container; + if (field.type === 'multiCombo' || field.type === 'semiCombo') { + container = _container.select('.input-wrap'); + } const iconsField = field.resolveReference('iconsCrossReference'); if (iconsField.icons) { - _container.selectAll('.tag-value-icon').remove(); + container.selectAll('.tag-value-icon').remove(); if (iconsField.icons[value]) { - _container.selectAll('.tag-value-icon') + container.selectAll('.tag-value-icon') .data([value]) .enter() .insert('div', 'input') @@ -561,6 +565,14 @@ export function uiFieldCombo(field, context) { _tags = tags; var stringsField = field.resolveReference('stringsCrossReference'); + var isMixed = Array.isArray(tags[field.key]); + var showsValue = value => !isMixed && value && !(field.type === 'typeCombo' && value === 'yes'); + var isRawValue = value => showsValue(value) + && !stringsField.hasTextForStringId(`options.${value}`) + && !stringsField.hasTextForStringId(`options.${value}.title`); + var isKnownValue = value => showsValue(value) && !isRawValue(value); + var isReadOnly = !_allowCustomValues; + if (_isMulti || _isSemi) { _multiData = []; @@ -578,7 +590,7 @@ export function uiFieldCombo(field, context) { _multiData.push({ key: k, value: displayValue(suffix), - display: renderValue(suffix), + display: addComboboxIcons(renderValue(suffix), suffix), state: typeof v === 'string' ? v.toLowerCase() : '', isMixed: Array.isArray(v) }); @@ -620,7 +632,7 @@ export function uiFieldCombo(field, context) { return { key: v, value: displayValue(v), - display: renderValue(v), + display: addComboboxIcons(renderValue(v), v), isMixed: !commonValues.includes(v) }; }); @@ -711,21 +723,14 @@ export function uiFieldCombo(field, context) { .attr('class', 'remove') .text('×'); + updateIcon(''); } else { - var isMixed = Array.isArray(tags[field.key]); - var mixedValues = isMixed && tags[field.key].map(function(val) { return displayValue(val); }).filter(Boolean); - var showsValue = !isMixed && tags[field.key] && !(field.type === 'typeCombo' && tags[field.key] === 'yes'); - var isRawValue = showsValue && !stringsField.hasTextForStringId(`options.${tags[field.key]}`) - && !stringsField.hasTextForStringId(`options.${tags[field.key]}.title`); - var isKnownValue = showsValue && !isRawValue; - - var isReadOnly = !_allowCustomValues || isKnownValue; - utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : '') + .data([tags[field.key]]) .classed('raw-value', isRawValue) .classed('known-value', isKnownValue) .attr('readonly', isReadOnly ? 'readonly' : undefined) @@ -734,7 +739,7 @@ export function uiFieldCombo(field, context) { .classed('mixed', isMixed) .on('keydown.deleteCapture', function(d3_event) { if (isReadOnly && - isKnownValue && + isKnownValue(tags[field.key]) && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) { @@ -755,6 +760,16 @@ export function uiFieldCombo(field, context) { _lengthIndicator.update(tags[field.key]); } } + + const refreshStyles = () => { + _input + .data([tagValue(utilGetSetValue(_input))]) + .classed('raw-value', isRawValue) + .classed('known-value', isKnownValue); + }; + _input.on('input.refreshStyles', refreshStyles); + _combobox.on('update.refreshStyles', refreshStyles); + refreshStyles(); }; function registerDragAndDrop(selection) { diff --git a/test/spec/presets/field.js b/test/spec/presets/field.js index f00236c36..6e3a957bd 100644 --- a/test/spec/presets/field.js +++ b/test/spec/presets/field.js @@ -53,6 +53,7 @@ describe('iD.presetField', function() { var context = iD.coreContext().assetPath('../dist/').init(); var uiField = iD.uiFieldCombo(field, context); + uiField(d3.select(document.createElement('div')).classed('form-field-input-wrap', true)); uiField.tags({k: 'v'}); expect(field.t.append).not.to.have.been.called; expect(other.t.append).to.have.been.called;