From 69a25fd6aa6b4002d16667067943245d79f3338e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 20 May 2019 17:50:50 -0400 Subject: [PATCH] Move locking code from input and localized up to uiField Also adjust styles some more for wikidata tagged items --- css/80_app.css | 8 +++-- modules/ui/field.js | 51 +++++++++++++++++++++++++---- modules/ui/fields/input.js | 41 +++-------------------- modules/ui/fields/localized.js | 56 +++++++++----------------------- test/spec/ui/fields/localized.js | 1 + 5 files changed, 70 insertions(+), 87 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 7850c7182..2c958b1bf 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1557,11 +1557,13 @@ button.preset-favorite-button.active .icon { } .field-label .modified-icon, -.field-label .remove-icon { +.field-label .remove-icon, +.field-label .remove-icon-multilingual { display: none; } -.modified .field-label .modified-icon, -.present .field-label .remove-icon { +.modified:not(.locked) .field-label .modified-icon, +.present:not(.locked) .field-label .remove-icon, +.present:not(.locked) .field-label .remove-icon-multilingual { display: inline-block; } diff --git a/modules/ui/field.js b/modules/ui/field.js index 7ba6c9743..d38fa9450 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -4,6 +4,7 @@ import { event as d3_event, select as d3_select } from 'd3-selection'; import { t } from '../util/locale'; import { textDirection } from '../util/locale'; import { svgIcon } from '../svg/icon'; +import { tooltip } from '../util/tooltip'; import { uiFieldHelp } from './field_help'; import { uiFields } from './fields'; import { uiTagReference } from './tag_reference'; @@ -25,6 +26,12 @@ export function uiField(context, presetField, entity, options) { var _state = ''; var _tags = {}; + var _locked = false; + var _lockedTip = tooltip() + .title(t('inspector.lock.suggestion', { label: field.label })) + .placement('bottom'); + + field.keys = field.keys || [field.key]; // only create the fields that are actually being shown @@ -77,7 +84,7 @@ export function uiField(context, presetField, entity, options) { function revert(d) { d3_event.stopPropagation(); d3_event.preventDefault(); - if (!entity) return false; + if (!entity || _locked) return; var original = context.graph().base().entities[entity.id]; var t = {}; @@ -92,6 +99,7 @@ export function uiField(context, presetField, entity, options) { function remove(d) { d3_event.stopPropagation(); d3_event.preventDefault(); + if (_locked) return; var t = {}; d.keys.forEach(function(key) { @@ -162,9 +170,9 @@ export function uiField(context, presetField, entity, options) { .on('click', revert); container - .classed('modified', isModified()) - .classed('present', isPresent()) .each(function(d) { + var selection = d3_select(this); + if (!d.impl) { createField(); } @@ -189,12 +197,12 @@ export function uiField(context, presetField, entity, options) { } } - d3_select(this) + selection .call(d.impl); // add field help components if (help) { - d3_select(this) + selection .call(help.body) .select('.field-label') .call(help.button); @@ -202,7 +210,7 @@ export function uiField(context, presetField, entity, options) { // add tag reference components if (reference) { - d3_select(this) + selection .call(reference.body) .select('.field-label') .call(reference.button); @@ -210,6 +218,29 @@ export function uiField(context, presetField, entity, options) { d.impl.tags(_tags); }); + + + container + .classed('locked', _locked) + .classed('modified', isModified()) + .classed('present', isPresent()); + + + // show a tip and lock icon if the field is locked + var annotation = container.selectAll('.field-label .label-textannotation'); + var icon = annotation.selectAll('.icon') + .data(_locked ? [0]: []); + + icon.exit() + .remove(); + + icon.enter() + .append('svg') + .attr('class', 'icon') + .append('use') + .attr('xlink:href', '#fas-lock'); + + container.call(_locked ? _lockedTip : _lockedTip.destroy); }; @@ -227,6 +258,13 @@ export function uiField(context, presetField, entity, options) { }; + field.locked = function(val) { + if (!arguments.length) return _locked; + _locked = val; + return field; + }; + + field.show = function() { _show = true; if (!field.impl) { @@ -239,7 +277,6 @@ export function uiField(context, presetField, entity, options) { } }; - // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown field.isShown = function() { return _show || isPresent(); diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 1da4bc639..7e2f12c80 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -21,18 +21,11 @@ export function uiFieldText(field, context) { var nominatim = services.geocoder; var input = d3_select(null); var _entity; - var _brandTip; - - if (field.id === 'brand') { - _brandTip = tooltip() - .title(t('inspector.lock.suggestion', { label: field.label })) - .placement('bottom'); - } - function i(selection) { var preset = _entity && context.presets().match(_entity, context.graph()); - var isSuggestion = preset && preset.suggestion && field.id === 'brand'; + var isLocked = preset && preset.suggestion && field.id === 'brand'; + field.locked(isLocked); var wrap = selection.selectAll('.form-field-input-wrap') .data([0]); @@ -57,8 +50,8 @@ export function uiFieldText(field, context) { .merge(input); input - .classed('disabled', !!isSuggestion) - .attr('readonly', isSuggestion || null) + .classed('disabled', !!isLocked) + .attr('readonly', isLocked || null) .on('input', change(true)) .on('blur', change()) .on('change', change()); @@ -99,32 +92,6 @@ export function uiFieldText(field, context) { input.node().value = vals.join(';'); change()(); }); - - } else if (preset && field.id === 'brand') { - var pTag = preset.id.split('/', 2); - var pKey = pTag[0]; - if (isSuggestion) { - // A "suggestion" preset (brand name) - // Put suggestion keys in `field.keys` so delete button can remove them all. - field.keys = Object.keys(preset.removeTags) - .filter(function(k) { return k !== pKey && k !== 'name'; }); - } - - selection.call(isSuggestion ? _brandTip : _brandTip.destroy); - - // add a label annotation - var annotation = selection.selectAll('.field-label .label-textannotation'); - var icon = annotation.selectAll('.icon') - .data(isSuggestion ? [0]: []); - - icon.exit() - .remove(); - - icon.enter() - .append('svg') - .attr('class', 'icon') - .append('use') - .attr('xlink:href', '#fas-lock'); } } diff --git a/modules/ui/fields/localized.js b/modules/ui/fields/localized.js index 009eb8bb3..20afe4df1 100644 --- a/modules/ui/fields/localized.js +++ b/modules/ui/fields/localized.js @@ -32,10 +32,6 @@ export function uiFieldLocalized(field, context) { var _selection = d3_select(null); var _multilingual = []; - var _isLocked = false; - var _brandTip = tooltip() - .title(t('inspector.lock.suggestion', { label: field.label })) - .placement('bottom'); var _buttonTip = tooltip() .title(t('translate.translate')) .placement('left'); @@ -45,13 +41,13 @@ export function uiFieldLocalized(field, context) { function calcLocked() { if (!_entity) { // the original entity - _isLocked = false; + field.locked(false); return; } var latest = context.hasEntity(_entity.id); if (!latest) { // get current entity, possibly edited - _isLocked = false; + field.locked(false); return; } @@ -62,8 +58,10 @@ export function uiFieldLocalized(field, context) { var showsBrand = preset && preset.fields .filter(function(d) { return d.id === 'brand'; }).length; - _isLocked = !!(field.id === 'name' && hasOriginalName && + var isLocked = !!(field.id === 'name' && hasOriginalName && (hasWikidata || (isSuggestion && !showsBrand))); + + field.locked(isLocked); } @@ -82,6 +80,7 @@ export function uiFieldLocalized(field, context) { function localized(selection) { _selection = selection; calcLocked(); + var isLocked = field.locked(); var entity = _entity && context.hasEntity(_entity.id); // get latest var preset = entity && context.presets().match(entity, context.graph()); @@ -112,13 +111,7 @@ export function uiFieldLocalized(field, context) { var pKey = pTag[0]; var pValue = pTag[1]; - if (preset.suggestion) { - // A "suggestion" preset (brand name) - // Put suggestion keys in `field.keys` so delete button can remove them all. - field.keys = Object.keys(preset.removeTags) - .filter(function(k) { return k !== pKey; }); - - } else { + if (!preset.suggestion) { // Not a suggestion preset - Add a suggestions dropdown if it makes sense to. // This code attempts to determine if the matched preset is the // kind of preset that even can benefit from name suggestions.. @@ -147,8 +140,8 @@ export function uiFieldLocalized(field, context) { } input - .classed('disabled', !!_isLocked) - .attr('readonly', _isLocked || null) + .classed('disabled', !!isLocked) + .attr('readonly', isLocked || null) .on('input', change(true)) .on('blur', change()) .on('change', change()); @@ -165,8 +158,8 @@ export function uiFieldLocalized(field, context) { .merge(translateButton); translateButton - .classed('disabled', !!_isLocked) - .call(_isLocked ? _buttonTip.destroy : _buttonTip) + .classed('disabled', !!isLocked) + .call(isLocked ? _buttonTip.destroy : _buttonTip) .on('click', addNew); @@ -186,25 +179,8 @@ export function uiFieldLocalized(field, context) { .call(renderMultilingual); localizedInputs.selectAll('button, input') - .classed('disabled', !!_isLocked) - .attr('readonly', _isLocked || null); - - - _selection.call(_isLocked ? _brandTip : _brandTip.destroy); - - // add a label annotations if locked - var annotation = selection.selectAll('.field-label .label-textannotation'); - var icon = annotation.selectAll('.icon') - .data(_isLocked ? [0]: []); - - icon.exit() - .remove(); - - icon.enter() - .append('svg') - .attr('class', 'icon') - .append('use') - .attr('xlink:href', '#fas-lock'); + .classed('disabled', !!isLocked) + .attr('readonly', isLocked || null); @@ -308,7 +284,7 @@ export function uiFieldLocalized(field, context) { function addNew() { d3_event.preventDefault(); - if (_isLocked) return; + if (field.locked()) return; var defaultLang = utilDetect().locale.toLowerCase().split('-')[0]; var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; }); @@ -325,7 +301,7 @@ export function uiFieldLocalized(field, context) { function change(onInput) { return function() { - if (_isLocked) { + if (field.locked()) { d3_event.preventDefault(); return; } @@ -432,7 +408,7 @@ export function uiFieldLocalized(field, context) { .append('button') .attr('class', 'remove-icon-multilingual') .on('click', function(d){ - if (_isLocked) return; + if (field.locked()) return; d3_event.preventDefault(); var t = {}; t[key(d.lang)] = undefined; diff --git a/test/spec/ui/fields/localized.js b/test/spec/ui/fields/localized.js index 173d7e6f1..e4bd9316c 100644 --- a/test/spec/ui/fields/localized.js +++ b/test/spec/ui/fields/localized.js @@ -5,6 +5,7 @@ describe('iD.uiFieldLocalized', function() { context = iD.coreContext(); selection = d3.select(document.createElement('div')); field = iD.presetField('name', { key: 'name', type: 'localized' }); + field.locked = function() { return false; }; }); it('adds a blank set of fields when the + button is clicked', function() {