Make name and brand fields readonly on suggestion presets

(re #5515)
This commit is contained in:
Bryan Housel
2018-11-23 23:44:52 -05:00
parent ea9e3008a5
commit ae063e9616
3 changed files with 187 additions and 138 deletions
+30 -18
View File
@@ -353,13 +353,6 @@ button:hover {
background-color: #ececec;
}
button[disabled],
button.disabled {
background-color: rgba(255,255,255,.25);
color: rgba(0,0,0,.4);
cursor: auto;
}
button.active {
background: #7092ff;
}
@@ -380,6 +373,15 @@ 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);
cursor: not-allowed;
}
.joined button {
border-radius: 0;
border-right: 1px solid rgba(0,0,0,.5);
@@ -1235,9 +1237,10 @@ a.hide-toggle {
.form-label {
position: relative;
font-weight: bold;
color: #333;
background: #f6f6f6;
border: 1px solid #cfcfcf;
padding: 5px 0 5px 10px;
background: #f6f6f6;
display: block;
border-radius: 4px 4px 0 0;
overflow: hidden;
@@ -1774,15 +1777,15 @@ input[type=number] {
/* Field - Localized Name
------------------------------------------------------- */
.form-field .localized-main {
.localized-main {
padding-right: 12%;
}
[dir='rtl'] .form-field .localized-main {
[dir='rtl'] .localized-main {
padding-left: 12%;
padding-right: 10px;
}
.form-field .button-input-action {
button.localized-add {
position: relative;
right: 1px;
width: 32px;
@@ -1794,22 +1797,22 @@ input[type=number] {
height: 30px;
vertical-align: top;
}
[dir='rtl'] .form-field .button-input-action {
[dir='rtl'] button.localized-add {
margin-left: 0;
margin-right: -32px;
border-right-width: 1px;
border-radius: 0 0 0 4px;
}
.form-field .localized-wrap {
.localized-wrap {
padding: 0 10px;
}
.form-field .localized-wrap .entry {
.localized-wrap .entry {
position: relative;
overflow: hidden;
}
.form-field .localized-wrap .entry::before {
.localized-wrap .entry::before {
content: "";
display: block;
position: absolute;
@@ -1822,18 +1825,27 @@ input[type=number] {
margin: auto;
}
.form-field .localized-wrap .entry .localized-lang {
.localized-wrap .entry .localized-lang {
border-radius: 0;
border-top-width: 0;
}
.form-field .localized-wrap .entry .localized-value {
.localized-wrap .entry .localized-value {
border-top-width: 0;
border-radius: 0 0 4px 4px;
}
.form-field .localized-wrap .form-label button {
.localized-wrap .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] {
color: #777;
background-color: #eee;
cursor: not-allowed;
}
/* Field - Address
------------------------------------------------------- */
+21 -6
View File
@@ -23,10 +23,13 @@ export function uiFieldText(field, context) {
var dispatch = d3_dispatch('change');
var nominatim = services.geocoder;
var input;
var entity;
var _entity;
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;
input = selection.selectAll('input')
@@ -41,12 +44,14 @@ export function uiFieldText(field, context) {
.merge(input);
input
.property('disabled', !!isSuggestion)
.on('input', change(true))
.on('blur', change())
.on('change', change());
if (field.type === 'tel' && nominatim && entity) {
var center = entity.extent(context.graph()).center();
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)
@@ -86,6 +91,16 @@ export function uiFieldText(field, context) {
input.node().value = parsed(input.node().value) + d;
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; });
}
}
}
@@ -124,9 +139,9 @@ export function uiFieldText(field, context) {
}
i.entity = function(_) {
if (!arguments.length) return entity;
entity = _;
i.entity = function(val) {
if (!arguments.length) return _entity;
_entity = val;
return i;
};
+136 -114
View File
@@ -28,11 +28,15 @@ export function uiFieldLocalized(field, context) {
var wikipedia = services.wikipedia;
var input = d3_select(null);
var localizedInputs = d3_select(null);
var wikiTitles;
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';
input = selection.selectAll('.localized-main')
.data([0]);
@@ -46,58 +50,60 @@ export function uiFieldLocalized(field, context) {
.call(utilNoAuto)
.merge(input);
var presets = context.presets();
var preset = presets.match(_entity, context.graph());
var isSuggestion = preset.suggestion;
if (field.id === 'name' && isSuggestion) {
// field.keys = Object.keys(preset.removeTags)
} else if (field.id === 'name' && !isSuggestion) {
var isFallback = preset.isFallback();
if (preset && field.id === 'name') {
var pTag = preset.id.split('/', 2);
var pKey = pTag[0];
var pValue = pTag[1];
var allSuggestions = presets.collection.filter(function(p) {
return p.suggestion === true;
});
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; });
// This code attempts to determine if the matched preset is the
// kind of preset that even can benefit from name suggestions..
// - true = shops, cafes, hotels, etc. (also generic and fallback presets)
// - false = churches, parks, hospitals, etc. (things not in the index)
var goodSuggestions = allSuggestions.filter(function(s) {
if (isFallback) return true;
var sTag = s.id.split('/', 2);
var sKey = sTag[0];
var sValue = sTag[1];
return pKey === sKey && (!pValue || pValue === sValue);
});
} else {
// Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
var allSuggestions = presets.collection.filter(function(p) {
return p.suggestion === true;
});
// Show the suggestions.. If the user picks one, change the tags..
if (allSuggestions.length && goodSuggestions.length) {
input
.call(d3_combobox()
.container(context.container())
.fetcher(suggestNames(preset, allSuggestions))
.minItems(1)
.on('accept', function(d) {
var tags = _entity.tags;
var geometry = _entity.geometry(context.graph());
var removed = preset.unsetTags(tags, geometry);
for (var k in tags) {
tags[k] = removed[k]; // set removed tags to `undefined`
}
tags = d.suggestion.setTags(tags, geometry);
dispatch.call('change', this, tags);
})
);
// This code attempts to determine if the matched preset is the
// kind of preset that even can benefit from name suggestions..
// - true = shops, cafes, hotels, etc. (also generic and fallback presets)
// - false = churches, parks, hospitals, etc. (things not in the index)
var isFallback = preset.isFallback();
var goodSuggestions = allSuggestions.filter(function(s) {
if (isFallback) return true;
var sTag = s.id.split('/', 2);
var sKey = sTag[0];
var sValue = sTag[1];
return pKey === sKey && (!pValue || pValue === sValue);
});
// Show the suggestions.. If the user picks one, change the tags..
if (allSuggestions.length && goodSuggestions.length) {
input
.call(d3_combobox()
.container(context.container())
.fetcher(suggestNames(preset, allSuggestions))
.minItems(1)
.on('accept', function(d) {
var tags = _entity.tags;
var geometry = _entity.geometry(context.graph());
var removed = preset.unsetTags(tags, geometry);
for (var k in tags) {
tags[k] = removed[k]; // set removed tags to `undefined`
}
tags = d.suggestion.setTags(tags, geometry);
dispatch.call('change', this, tags);
})
);
}
}
}
input
.property('disabled', isSuggestion)
.property('disabled', !!isSuggestion)
.on('input', change(true))
.on('blur', change())
.on('change', change());
@@ -117,73 +123,82 @@ export function uiFieldLocalized(field, context) {
.merge(translateButton);
translateButton
.property('disabled', !!isSuggestion)
.on('click', addNew);
localizedInputs = selection.selectAll('.localized-wrap')
.data([0]);
localizedInputs = localizedInputs.enter().append('div')
localizedInputs = localizedInputs.enter()
.append('div')
.attr('class', 'localized-wrap')
.merge(localizedInputs);
}
localizedInputs.selectAll('button, input')
.property('disabled', !!isSuggestion);
function suggestNames(preset, suggestions) {
var pTag = preset.id.split('/', 2);
var pKey = pTag[0];
var pValue = pTag[1];
function suggestNames(preset, suggestions) {
var pTag = preset.id.split('/', 2);
var pKey = pTag[0];
var pValue = pTag[1];
return function(value, callback) {
var results = [];
if (value && value.length > 2) {
for (var i = 0; i < suggestions.length; i++) {
var s = suggestions[i];
var sTag = s.id.split('/', 2);
var sKey = sTag[0];
var sValue = sTag[1];
var name = s.name();
var dist = utilEditDistance(value, name.substring(0, value.length));
var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
return function(value, callback) {
var results = [];
if (value && value.length > 2) {
for (var i = 0; i < suggestions.length; i++) {
var s = suggestions[i];
var sTag = s.id.split('/', 2);
var sKey = sTag[0];
var sValue = sTag[1];
var name = s.name();
var dist = utilEditDistance(value, name.substring(0, value.length));
var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
if (dist < 1 || (matchesPreset && dist < 3)) {
var obj = {
title: name,
value: name,
suggestion: s,
dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
};
results.push(obj);
if (dist < 1 || (matchesPreset && dist < 3)) {
var obj = {
title: name,
value: name,
suggestion: s,
dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
};
results.push(obj);
}
}
results.sort(function(a, b) { return a.dist - b.dist; });
}
results.sort(function(a, b) { return a.dist - b.dist; });
}
results = results.slice(0, 10);
callback(results);
};
}
function addNew() {
d3_event.preventDefault();
var data = localizedInputs.selectAll('div.entry').data();
var defaultLang = utilDetect().locale.toLowerCase().split('-')[0];
var langExists = _find(data, function(datum) { return datum.lang === defaultLang;});
var isLangEn = defaultLang.indexOf('en') > -1;
if (isLangEn || langExists) {
defaultLang = '';
results = results.slice(0, 10);
callback(results);
};
}
data.push({ lang: defaultLang, value: '' });
localizedInputs.call(render, data);
}
function change(onInput) {
return function() {
var t = {};
t[field.key] = utilGetSetValue(d3_select(this)) || undefined;
dispatch.call('change', this, t, onInput);
};
function addNew() {
d3_event.preventDefault();
if (isSuggestion) return;
var data = localizedInputs.selectAll('div.entry').data();
var defaultLang = utilDetect().locale.toLowerCase().split('-')[0];
var langExists = _find(data, function(datum) { return datum.lang === defaultLang;});
var isLangEn = defaultLang.indexOf('en') > -1;
if (isLangEn || langExists) {
defaultLang = '';
}
data.push({ lang: defaultLang, value: '' });
localizedInputs
.call(renderMultilingual, data);
}
function change(onInput) {
return function() {
var t = {};
t[field.key] = utilGetSetValue(d3_select(this)) || undefined;
dispatch.call('change', this, t, onInput);
};
}
}
@@ -211,8 +226,8 @@ export function uiFieldLocalized(field, context) {
if (lang && value) {
t[key(lang)] = value;
} else if (lang && wikiTitles && wikiTitles[d.lang]) {
t[key(lang)] = wikiTitles[d.lang];
} else if (lang && _wikiTitles && _wikiTitles[d.lang]) {
t[key(lang)] = _wikiTitles[d.lang];
}
d.lang = lang;
@@ -241,16 +256,20 @@ export function uiFieldLocalized(field, context) {
}
function render(selection, data) {
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; });
wraps.exit()
.transition()
.duration(200)
.style('max-height','0px')
.style('max-height', '0px')
.style('opacity', '0')
.style('top','-10px')
.style('top', '-10px')
.remove();
var innerWrap = wraps.enter()
@@ -267,24 +286,26 @@ export function uiFieldLocalized(field, context) {
var label = wrap
.append('label')
.attr('class','form-label')
.attr('class', 'form-label')
.text(t('translate.localized_translation_label'))
.attr('for','localized-lang');
.attr('for', 'localized-lang');
label
.append('button')
.attr('class', 'minor remove')
.on('click', function(d){
.property('disabled', !!isSuggestion)
.on('click', function(d) {
if (isSuggestion) return;
d3_event.preventDefault();
var t = {};
t[key(d.lang)] = undefined;
dispatch.call('change', this, t);
d3_select(this.parentNode.parentNode)
.style('top','0')
.style('max-height','240px')
.style('top', '0')
.style('max-height', '240px')
.transition()
.style('opacity', '0')
.style('max-height','0px')
.style('max-height', '0px')
.remove();
})
.call(svgIcon('#iD-operation-delete'));
@@ -293,18 +314,20 @@ export function uiFieldLocalized(field, context) {
.append('input')
.attr('class', 'localized-lang')
.attr('type', 'text')
.attr('placeholder',t('translate.localized_translation_language'))
.attr('placeholder', t('translate.localized_translation_language'))
.property('disabled', !!isSuggestion)
.on('blur', changeLang)
.on('change', changeLang)
.call(langcombo);
wrap
.append('input')
.on('blur', changeValue)
.on('change', changeValue)
.attr('type', 'text')
.attr('placeholder', t('translate.localized_translation_name'))
.attr('class', 'localized-value');
.attr('class', 'localized-value')
.property('disabled', !!isSuggestion)
.on('blur', changeValue)
.on('change', changeValue);
});
innerWrap
@@ -337,13 +360,11 @@ export function uiFieldLocalized(field, context) {
localized.tags = function(tags) {
// Fetch translations from wikipedia
if (tags.wikipedia && !wikiTitles) {
wikiTitles = {};
if (tags.wikipedia && !_wikiTitles) {
_wikiTitles = {};
var wm = tags.wikipedia.match(/([^:]+):(.+)/);
if (wm && wm[0] && wm[1]) {
wikipedia.translations(wm[1], wm[2], function(d) {
wikiTitles = d;
});
wikipedia.translations(wm[1], wm[2], function(d) { _wikiTitles = d; });
}
}
@@ -357,7 +378,8 @@ export function uiFieldLocalized(field, context) {
}
}
localizedInputs.call(render, postfixed.reverse());
localizedInputs
.call(renderMultilingual, postfixed.reverse());
};