From f6f146f79bec84052c65d5a69b4d8b63416d4ad8 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 26 Nov 2018 15:13:40 -0500 Subject: [PATCH] Lock down some fields when a brand preset has been chosen --- css/80_app.css | 65 +++++++++++++----------------- data/core.yaml | 3 ++ dist/locales/en.json | 6 ++- modules/ui/fields/input.js | 26 +++++++++--- modules/ui/fields/localized.js | 73 ++++++++++++++++++++++------------ 5 files changed, 102 insertions(+), 71 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 0ca40e025..55da354dd 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -175,24 +175,18 @@ input[type=email] { padding: 5px 10px 5px 20px; } -.disabled, -textarea[disabled], -input[type=text][disabled], -input[type=search][disabled], -input[type=number][disabled], -input[type=url][disabled], -input[type=tel][disabled], -input[type=email][disabled] { - color: #777; - background-color: #eee; - cursor: not-allowed; -} - textarea:focus, input:focus { background-color: #f1f1f1; } +textarea.disabled, +input.disabled { + color: #777; + background-color: #eee; + cursor: not-allowed; +} + input[type="checkbox"], input[type="radio"] { float: left; @@ -217,18 +211,15 @@ input[type="radio"] { } /* tables */ - table { background-color: #fff; border-collapse: collapse; width: 100%; border-spacing: 0; } - table th { text-align: left; } - table.tags, table.tags td, table.tags th { border: 1px solid #ccc; padding: 4px; @@ -261,22 +252,18 @@ ul li { list-style: none;} .toggle-list > label:hover { background-color: #ececec; } - .toggle-list > label:not(:last-child) { border-bottom: 1px solid #ccc; } - .toggle-list > label:last-child { border-radius: 0 0 3px 3px; } - .toggle-list label > span { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } - .toggle-list > label.active { background: #e8ebff; } @@ -373,9 +360,7 @@ button.minor:hover { background-color: #f1f1f1; } -button[disabled], button.disabled, -button.minor[disabled], button.minor.disabled { background-color: rgba(255,255,255,.25); color: rgba(0,0,0,.4); @@ -408,15 +393,12 @@ button.minor.disabled { border-radius: 4px 0 0 4px; } + +/* Action buttons */ button.action { background: #7092ff; color: #fff; } -button[disabled].action, -button[disabled].action:hover { - background: #cccccc; - color: #888; -} button.action:focus, button.action:hover { background: #597be7; @@ -424,12 +406,18 @@ button.action:hover { button.secondary-action { background: #ececec; } - button.secondary-action:focus, button.secondary-action:hover { background: #cccccc; } +button[disabled].action, +button[disabled].action:hover { + background: #cccccc; + color: #888; + cursor: not-allowed; +} + /* Icons ------------------------------------------------------- */ @@ -1298,6 +1286,7 @@ a.hide-toggle { .form-field > input, .form-field > textarea, +.form-field .localized-input-wrap > input, .form-field .preset-input-wrap { border: 1px solid #ccc; min-height: 30px; @@ -1804,15 +1793,15 @@ button.localized-add { border-radius: 0 0 0 4px; } -.localized-wrap { +.localized-multilingual { padding: 0 10px; } -.localized-wrap .entry { +.localized-multilingual .entry { position: relative; overflow: hidden; } -.localized-wrap .entry::before { +.localized-multilingual .entry::before { content: ""; display: block; position: absolute; @@ -1825,22 +1814,22 @@ button.localized-add { margin: auto; } -.localized-wrap .entry .localized-lang { +.localized-multilingual .entry .localized-lang { border-radius: 0; border-top-width: 0; } -.localized-wrap .entry .localized-value { +.localized-multilingual .entry .localized-value { border-top-width: 0; border-radius: 0 0 4px 4px; } -.localized-wrap .form-label button { +.localized-multilingual .form-label button { border-top-right-radius: 3px; } -button.localized-add[disabled], -.localized-wrap .form-label button[disabled], -.localized-wrap .localized-lang[disabled], -.localized-wrap .localized-value[disabled] { +button.localized-add.disabled, +.localized-multilingual .form-label button.disabled, +.localized-multilingual .localized-lang.disabled, +.localized-multilingual .localized-value.disabled { color: #777; background-color: #eee; cursor: not-allowed; diff --git a/data/core.yaml b/data/core.yaml index 167a7aec2..2473a5e08 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -438,6 +438,9 @@ en: relation: Relation location: Location add_fields: "Add field:" + lock: + brand: 'This brand name is locked. You can delete it or edit the tags in the "All tags" section.' + official: 'This official name is locked. You can delete it or edit the tags in the "All tags" section.' background: title: Background description: Background settings diff --git a/dist/locales/en.json b/dist/locales/en.json index 3bb6d401f..fd8295b9d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -534,7 +534,11 @@ "way": "Way", "relation": "Relation", "location": "Location", - "add_fields": "Add field:" + "add_fields": "Add field:", + "lock": { + "brand": "This brand name is locked. You can delete it or edit the tags in the \"All tags\" section.", + "official": "This official name is locked. You can delete it or edit the tags in the \"All tags\" section." + } }, "background": { "title": "Background", diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index d4c0b2057..1bf14637a 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -1,9 +1,14 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { event as d3_event } from 'd3-selection'; +import { + select as d3_select, + event as d3_event +} from 'd3-selection'; import { t, textDirection } from '../../util/locale'; import { dataPhoneFormats } from '../../../data'; import { services } from '../../services'; +import { tooltip } from '../../util/tooltip'; + import { utilGetSetValue, utilNoAuto, @@ -22,15 +27,20 @@ export { export function uiFieldText(field, context) { var dispatch = d3_dispatch('change'); var nominatim = services.geocoder; - var input; + var input = d3_select(null); var _entity; + var _brandTip; + + if (field.id === 'brand') { + _brandTip = tooltip().title(t('inspector.lock.brand')).placement('bottom'); + } function i(selection) { var preset = _entity && context.presets().match(_entity, context.graph()); var isSuggestion = preset && preset.suggestion && field.id === 'brand'; - var fieldId = 'preset-input-' + field.safeid; + var fieldID = 'preset-input-' + field.safeid; input = selection.selectAll('input') .data([0]); @@ -38,23 +48,27 @@ export function uiFieldText(field, context) { input = input.enter() .append('input') .attr('type', field.type) - .attr('id', fieldId) + .attr('id', fieldID) .attr('placeholder', field.placeholder() || t('inspector.unknown')) .call(utilNoAuto) .merge(input); input - .property('disabled', !!isSuggestion) + .classed('disabled', !!isSuggestion) .on('input', change(true)) .on('blur', change()) .on('change', change()); + if (field.id === 'brand') { + selection.call(isSuggestion ? _brandTip : _brandTip.destroy); + } + if (field.type === 'tel' && nominatim && _entity) { var center = _entity.extent(context.graph()).center(); nominatim.countryCode(center, function (err, countryCode) { if (err || !dataPhoneFormats[countryCode]) return; - selection.selectAll('#' + fieldId) + selection.selectAll('#' + fieldID) .attr('placeholder', dataPhoneFormats[countryCode]); }); diff --git a/modules/ui/fields/localized.js b/modules/ui/fields/localized.js index f783a5210..a6cef6802 100644 --- a/modules/ui/fields/localized.js +++ b/modules/ui/fields/localized.js @@ -28,16 +28,38 @@ export function uiFieldLocalized(field, context) { var wikipedia = services.wikipedia; var input = d3_select(null); var localizedInputs = d3_select(null); + var _isLocked = false; + var _brandTip = tooltip().title(t('inspector.lock.brand')).placement('bottom'); + var _buttonTip = tooltip().title(t('translate.translate')).placement('left'); var _wikiTitles; var _entity; - function localized(selection) { - var presets = context.presets(); - var preset = _entity && presets.match(_entity, context.graph()); - var isSuggestion = preset && preset.suggestion && field.id === 'name'; + function checkLocked() { + var preset = _entity && context.presets().match(_entity, context.graph()); + var isSuggestion = preset && preset.suggestion; + var showsBrand = preset && preset.fields + .filter(function(d) { return d.id === 'brand'; }).length; + _isLocked = field.id === 'name' && isSuggestion && !showsBrand; + } - input = selection.selectAll('.localized-main') + + function localized(selection) { + checkLocked(); + var preset = _entity && context.presets().match(_entity, context.graph()); + + var wrap = selection.selectAll('.localized-input-wrap') + .data([0]); + + // enter/update + wrap = wrap.enter() + .append('div') + .attr('class', 'localized-input-wrap') + .merge(wrap) + .call(_isLocked ? _brandTip : _brandTip.destroy); + + + input = wrap.selectAll('.localized-main') .data([0]); // enter/update @@ -55,7 +77,7 @@ export function uiFieldLocalized(field, context) { var pKey = pTag[0]; var pValue = pTag[1]; - if (isSuggestion) { + 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) @@ -63,7 +85,7 @@ export function uiFieldLocalized(field, context) { } else { // Not a suggestion preset - Add a suggestions dropdown if it makes sense to. - var allSuggestions = presets.collection.filter(function(p) { + var allSuggestions = context.presets().collection.filter(function(p) { return p.suggestion === true; }); @@ -95,6 +117,7 @@ export function uiFieldLocalized(field, context) { tags[k] = removed[k]; // set removed tags to `undefined` } tags = d.suggestion.setTags(tags, geometry); + utilGetSetValue(input, tags.name); dispatch.call('change', this, tags); }) ); @@ -103,40 +126,41 @@ export function uiFieldLocalized(field, context) { } input - .property('disabled', !!isSuggestion) + .classed('disabled', !!_isLocked) + .attr('readonly', _isLocked) .on('input', change(true)) .on('blur', change()) .on('change', change()); - var translateButton = selection.selectAll('.localized-add') + var translateButton = wrap.selectAll('.localized-add') .data([0]); translateButton = translateButton.enter() .append('button') - .attr('class', 'button-input-action localized-add minor') + .attr('class', 'localized-add button-input-action minor') .attr('tabindex', -1) .call(svgIcon('#iD-icon-plus')) - .call(tooltip() - .title(t('translate.translate')) - .placement('left')) .merge(translateButton); translateButton - .property('disabled', !!isSuggestion) + .classed('disabled', !!_isLocked) + .call(_isLocked ? _buttonTip.destroy : _buttonTip) .on('click', addNew); - localizedInputs = selection.selectAll('.localized-wrap') + localizedInputs = selection.selectAll('.localized-multilingual') .data([0]); localizedInputs = localizedInputs.enter() .append('div') - .attr('class', 'localized-wrap') + .attr('class', 'localized-multilingual') .merge(localizedInputs); localizedInputs.selectAll('button, input') - .property('disabled', !!isSuggestion); + .classed('disabled', !!_isLocked) + .attr('readonly', _isLocked); + function suggestNames(preset, suggestions) { @@ -176,7 +200,7 @@ export function uiFieldLocalized(field, context) { function addNew() { d3_event.preventDefault(); - if (isSuggestion) return; + if (_isLocked) return; var data = localizedInputs.selectAll('div.entry').data(); var defaultLang = utilDetect().locale.toLowerCase().split('-')[0]; @@ -194,6 +218,10 @@ export function uiFieldLocalized(field, context) { function change(onInput) { return function() { + if (_isLocked) { + d3_event.preventDefault(); + return; + } var t = {}; t[field.key] = utilGetSetValue(d3_select(this)) || undefined; dispatch.call('change', this, t, onInput); @@ -257,10 +285,6 @@ export function uiFieldLocalized(field, context) { function renderMultilingual(selection, data) { - var presets = context.presets(); - var preset = _entity && presets.match(_entity, context.graph()); - var isSuggestion = preset && preset.suggestion && field.id === 'name'; - var wraps = selection.selectAll('div.entry') .data(data, function(d) { return d.lang; }); @@ -293,9 +317,8 @@ export function uiFieldLocalized(field, context) { label .append('button') .attr('class', 'minor remove') - .property('disabled', !!isSuggestion) .on('click', function(d) { - if (isSuggestion) return; + if (_isLocked) return; d3_event.preventDefault(); var t = {}; t[key(d.lang)] = undefined; @@ -315,7 +338,6 @@ export function uiFieldLocalized(field, context) { .attr('class', 'localized-lang') .attr('type', 'text') .attr('placeholder', t('translate.localized_translation_language')) - .property('disabled', !!isSuggestion) .on('blur', changeLang) .on('change', changeLang) .call(langcombo); @@ -325,7 +347,6 @@ export function uiFieldLocalized(field, context) { .attr('type', 'text') .attr('placeholder', t('translate.localized_translation_name')) .attr('class', 'localized-value') - .property('disabled', !!isSuggestion) .on('blur', changeValue) .on('change', changeValue); });