Merge branch 'develop' into lang-attributes

# Conflicts:
#	modules/ui/fields/combo.js
This commit is contained in:
Quincy Morgan
2020-09-17 09:07:18 -04:00
25 changed files with 280 additions and 223 deletions
+103 -101
View File
@@ -20,20 +20,18 @@ export {
export function uiFieldCombo(field, context) {
var dispatch = d3_dispatch('change');
var taginfo = services.taginfo;
var isMulti = (field.type === 'multiCombo');
var isNetwork = (field.type === 'networkCombo');
var isSemi = (field.type === 'semiCombo');
var optstrings = field.strings && field.strings.options;
var optarray = field.options;
var snake_case = (field.snake_case || (field.snake_case === undefined));
var caseSensitive = field.caseSensitive;
var combobox = uiCombobox(context, 'combo-' + field.safeid)
.caseSensitive(caseSensitive)
.minItems(isMulti || isSemi ? 1 : 2);
var container = d3_select(null);
var inputWrap = d3_select(null);
var input = d3_select(null);
var _isMulti = (field.type === 'multiCombo');
var _isNetwork = (field.type === 'networkCombo');
var _isSemi = (field.type === 'semiCombo');
var _optstrings = field.strings && field.strings.options;
var _optarray = field.options;
var _snake_case = (field.snake_case || (field.snake_case === undefined));
var _combobox = uiCombobox(context, 'combo-' + field.safeid)
.caseSensitive(field.caseSensitive)
.minItems(_isMulti || _isSemi ? 1 : 2);
var _container = d3_select(null);
var _inputWrap = d3_select(null);
var _input = d3_select(null);
var _comboData = [];
var _multiData = [];
var _entityIDs = [];
@@ -49,7 +47,7 @@ export function uiFieldCombo(field, context) {
// ensure multiCombo field.key ends with a ':'
if (isMulti && /[^:]$/.test(field.key)) {
if (_isMulti && field.key && /[^:]$/.test(field.key)) {
field.key += ':';
}
@@ -74,7 +72,7 @@ export function uiFieldCombo(field, context) {
function tagValue(dval) {
dval = clean(dval || '');
if (optstrings) {
if (_optstrings) {
var found = _comboData.find(function(o) {
return o.key && clean(o.value) === dval;
});
@@ -87,7 +85,7 @@ export function uiFieldCombo(field, context) {
return 'yes';
}
return (snake_case ? snake(dval) : dval) || undefined;
return (_snake_case ? snake(dval) : dval) || undefined;
}
@@ -96,7 +94,7 @@ export function uiFieldCombo(field, context) {
function displayValue(tval) {
tval = tval || '';
if (optstrings) {
if (_optstrings) {
var found = _comboData.find(function(o) {
return o.key === tval && o.value;
});
@@ -109,7 +107,7 @@ export function uiFieldCombo(field, context) {
return '';
}
return snake_case ? unsnake(tval) : tval;
return _snake_case ? unsnake(tval) : tval;
}
@@ -128,28 +126,28 @@ export function uiFieldCombo(field, context) {
function initCombo(selection, attachTo) {
if (optstrings) {
if (_optstrings) {
selection.attr('readonly', 'readonly');
selection.call(combobox, attachTo);
selection.call(_combobox, attachTo);
setStaticValues(setPlaceholder);
} else if (optarray) {
selection.call(combobox, attachTo);
} else if (_optarray) {
selection.call(_combobox, attachTo);
setStaticValues(setPlaceholder);
} else if (taginfo) {
selection.call(combobox.fetcher(setTaginfoValues), attachTo);
} else if (services.taginfo) {
selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
setTaginfoValues('', setPlaceholder);
}
}
function setStaticValues(callback) {
if (!(optstrings || optarray)) return;
if (!(_optstrings || _optarray)) return;
if (optstrings) {
_comboData = Object.keys(optstrings).map(function(k) {
var v = field.t('options.' + k, { 'default': optstrings[k], html: false });
if (_optstrings) {
_comboData = Object.keys(_optstrings).map(function(k) {
var v = field.t('options.' + k, { 'default': _optstrings[k], html: false });
return {
key: k,
value: v,
@@ -157,9 +155,9 @@ export function uiFieldCombo(field, context) {
};
});
} else if (optarray) {
_comboData = optarray.map(function(k) {
var v = snake_case ? unsnake(k) : k;
} else if (_optarray) {
_comboData = _optarray.map(function(k) {
var v = _snake_case ? unsnake(k) : k;
return {
key: k,
value: v,
@@ -168,15 +166,15 @@ export function uiFieldCombo(field, context) {
});
}
combobox.data(objectDifference(_comboData, _multiData));
_combobox.data(objectDifference(_comboData, _multiData));
if (callback) callback(_comboData);
}
function setTaginfoValues(q, callback) {
var fn = isMulti ? 'multikeys' : 'values';
var query = (isMulti ? field.key : '') + q;
var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
var fn = _isMulti ? 'multikeys' : 'values';
var query = (_isMulti ? field.key : '') + q;
var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
if (hasCountryPrefix) {
query = _countryCode + ':';
}
@@ -191,7 +189,7 @@ export function uiFieldCombo(field, context) {
params.geometry = context.graph().geometry(_entityIDs[0]);
}
taginfo[fn](params, function(err, data) {
services.taginfo[fn](params, function(err, data) {
if (err) return;
data = data.filter(function(d) {
@@ -220,16 +218,16 @@ export function uiFieldCombo(field, context) {
}
// hide the caret if there are no suggestions
container.classed('empty-combobox', data.length === 0);
_container.classed('empty-combobox', data.length === 0);
_comboData = data.map(function(d) {
var k = d.value;
if (isMulti) k = k.replace(field.key, '');
var v = snake_case ? unsnake(k) : k;
if (_isMulti) k = k.replace(field.key, '');
var v = _snake_case ? unsnake(k) : k;
return {
key: k,
value: v,
title: isMulti ? v : d.title
title: _isMulti ? v : d.title
};
});
@@ -241,7 +239,7 @@ export function uiFieldCombo(field, context) {
function setPlaceholder(values) {
if (isMulti || isSemi) {
if (_isMulti || _isSemi) {
_staticPlaceholder = field.placeholder() || t('inspector.add', { html: false });
} else {
var vals = values
@@ -257,13 +255,13 @@ export function uiFieldCombo(field, context) {
}
var ph;
if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
ph = t('inspector.multiple_values', { html: false });
} else {
ph = _staticPlaceholder;
}
container.selectAll('input')
_container.selectAll('input')
.attr('placeholder', ph);
}
@@ -272,17 +270,17 @@ export function uiFieldCombo(field, context) {
var t = {};
var val;
if (isMulti || isSemi) {
val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
container.classed('active', false);
utilGetSetValue(input, '');
if (_isMulti || _isSemi) {
val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
_container.classed('active', false);
utilGetSetValue(_input, '');
var vals = val.split(';').filter(Boolean);
if (!vals.length) return;
if (isMulti) {
if (_isMulti) {
utilArrayUniq(vals).forEach(function(v) {
var key = field.key + v;
var key = (field.key || '') + v;
if (_tags) {
// don't set a multicombo value to 'yes' if it already has a non-'no' value
// e.g. `language:de=main`
@@ -294,16 +292,16 @@ export function uiFieldCombo(field, context) {
t[key] = 'yes';
});
} else if (isSemi) {
} else if (_isSemi) {
var arr = _multiData.map(function(d) { return d.key; });
arr = arr.concat(vals);
t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
}
window.setTimeout(function() { input.node().focus(); }, 10);
window.setTimeout(function() { _input.node().focus(); }, 10);
} else {
var rawValue = utilGetSetValue(input);
var rawValue = utilGetSetValue(_input);
// don't override multiple values with blank string
if (!rawValue && Array.isArray(_tags[field.key])) return;
@@ -319,9 +317,9 @@ export function uiFieldCombo(field, context) {
function removeMultikey(d) {
d3_event.stopPropagation();
var t = {};
if (isMulti) {
if (_isMulti) {
t[d.key] = undefined;
} else if (isSemi) {
} else if (_isSemi) {
var arr = _multiData.map(function(md) {
return md.key === d.key ? null : md.key;
}).filter(Boolean);
@@ -334,17 +332,17 @@ export function uiFieldCombo(field, context) {
function combo(selection) {
container = selection.selectAll('.form-field-input-wrap')
_container = selection.selectAll('.form-field-input-wrap')
.data([0]);
var type = (isMulti || isSemi) ? 'multicombo': 'combo';
container = container.enter()
var type = (_isMulti || _isSemi) ? 'multicombo': 'combo';
_container = _container.enter()
.append('div')
.attr('class', 'form-field-input-wrap form-field-input-' + type)
.merge(container);
.merge(_container);
if (isMulti || isSemi) {
container = container.selectAll('.chiplist')
if (_isMulti || _isSemi) {
_container = _container.selectAll('.chiplist')
.data([0]);
var listClass = 'chiplist';
@@ -355,67 +353,67 @@ export function uiFieldCombo(field, context) {
listClass += ' full-line-chips';
}
container = container.enter()
_container = _container.enter()
.append('ul')
.attr('class', listClass)
.on('click', function() {
window.setTimeout(function() { input.node().focus(); }, 10);
window.setTimeout(function() { _input.node().focus(); }, 10);
})
.merge(container);
.merge(_container);
inputWrap = container.selectAll('.input-wrap')
_inputWrap = _container.selectAll('.input-wrap')
.data([0]);
inputWrap = inputWrap.enter()
_inputWrap = _inputWrap.enter()
.append('li')
.attr('class', 'input-wrap')
.merge(inputWrap);
.merge(_inputWrap);
input = inputWrap.selectAll('input')
_input = _inputWrap.selectAll('input')
.data([0]);
} else {
input = container.selectAll('input')
_input = _container.selectAll('input')
.data([0]);
}
input = input.enter()
_input = _input.enter()
.append('input')
.attr('type', 'text')
.attr('id', field.domId)
.call(utilNoAuto)
.call(initCombo, selection)
.merge(input);
.merge(_input);
if (isNetwork) {
if (_isNetwork) {
var extent = combinedEntityExtent();
var countryCode = extent && countryCoder.iso1A2Code(extent.center());
_countryCode = countryCode && countryCode.toLowerCase();
}
input
_input
.on('change', change)
.on('blur', change);
input
_input
.on('keydown.field', function() {
switch (d3_event.keyCode) {
case 13: // ↩ Return
input.node().blur(); // blurring also enters the value
_input.node().blur(); // blurring also enters the value
d3_event.stopPropagation();
break;
}
});
if (isMulti || isSemi) {
combobox
if (_isMulti || _isSemi) {
_combobox
.on('accept', function() {
input.node().blur();
input.node().focus();
_input.node().blur();
_input.node().focus();
});
input
.on('focus', function() { container.classed('active', true); });
_input
.on('focus', function() { _container.classed('active', true); });
}
}
@@ -423,19 +421,19 @@ export function uiFieldCombo(field, context) {
combo.tags = function(tags) {
_tags = tags;
if (isMulti || isSemi) {
if (_isMulti || _isSemi) {
_multiData = [];
var maxLength;
if (isMulti) {
if (_isMulti) {
// Build _multiData array containing keys already set..
for (var k in tags) {
if (k.indexOf(field.key) !== 0) continue;
if ((field.key && k.indexOf(field.key) !== 0) || field.keys.indexOf(k) === -1) continue;
var v = tags[k];
if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) continue;
var suffix = k.substring(field.key.length);
var suffix = field.key ? k.substring(field.key.length) : k;
_multiData.push({
key: k,
value: displayValue(suffix),
@@ -443,13 +441,17 @@ export function uiFieldCombo(field, context) {
});
}
// Set keys for form-field modified (needed for undo and reset buttons)..
field.keys = _multiData.map(function(d) { return d.key; });
if (field.key) {
// Set keys for form-field modified (needed for undo and reset buttons)..
field.keys = _multiData.map(function(d) { return d.key; });
// limit the input length so it fits after prepending the key prefix
maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
// limit the input length so it fits after prepending the key prefix
maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
} else {
maxLength = context.maxCharsForTagKey();
}
} else if (isSemi) {
} else if (_isSemi) {
var allValues = [];
var commonValues;
@@ -492,23 +494,23 @@ export function uiFieldCombo(field, context) {
// a negative maxlength doesn't make sense
maxLength = Math.max(0, maxLength);
var allowDragAndDrop = isSemi // only semiCombo values are ordered
var allowDragAndDrop = _isSemi // only semiCombo values are ordered
&& !Array.isArray(tags[field.key]);
// Exclude existing multikeys from combo options..
var available = objectDifference(_comboData, _multiData);
combobox.data(available);
_combobox.data(available);
// Hide 'Add' button if this field uses fixed set of
// translateable optstrings and they're all currently used,
// translateable _optstrings and they're all currently used,
// or if the field is already at its character limit
var hideAdd = (optstrings && !available.length) || maxLength <= 0;
container.selectAll('.chiplist .input-wrap')
var hideAdd = (_optstrings && !available.length) || maxLength <= 0;
_container.selectAll('.chiplist .input-wrap')
.style('display', hideAdd ? 'none' : null);
// Render chips
var chips = container.selectAll('.chip')
var chips = _container.selectAll('.chip')
.data(_multiData);
chips.exit()
@@ -550,7 +552,7 @@ export function uiFieldCombo(field, context) {
return displayValue(val);
}).filter(Boolean);
utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : '')
.attr('title', isMixed ? mixedValues.join('\n') : undefined)
.attr('placeholder', isMixed ? t('inspector.multiple_values', { html: false }) : _staticPlaceholder || '')
.classed('mixed', isMixed);
@@ -585,7 +587,7 @@ export function uiFieldCombo(field, context) {
var draggedTagWidth = d3_select(this).node().offsetWidth;
if (field.key === 'destination') { // meaning tags are full width
container.selectAll('.chip')
_container.selectAll('.chip')
.style('transform', function(d2, index2) {
var node = d3_select(this).node();
@@ -607,7 +609,7 @@ export function uiFieldCombo(field, context) {
return null;
});
} else {
container.selectAll('.chip')
_container.selectAll('.chip')
.each(function(d2, index2) {
var node = d3_select(this).node();
@@ -650,7 +652,7 @@ export function uiFieldCombo(field, context) {
d3_select(this)
.classed('dragging', false);
container.selectAll('.chip')
_container.selectAll('.chip')
.style('transform', null);
if (typeof targetIndex === 'number') {
@@ -678,7 +680,7 @@ export function uiFieldCombo(field, context) {
combo.focus = function() {
input.node().focus();
_input.node().focus();
};