Add mechanism for fields to support editing during multiselection (re: #7276)

Add `utilCombinedTags` method and use it for the raw tag editor as well as fields
Pass `entityIDs` array into fields instead of single `entity` object
Give field revertion its own path separate from `change`
Add multiselection editing to fields in files: access, address, check, combo, cycleway, input, maxspeed, textarea, and wikidata
This commit is contained in:
Quincy Morgan
2020-01-30 13:53:29 -05:00
parent 1e21eea745
commit 1b331bb678
25 changed files with 808 additions and 339 deletions
+29 -11
View File
@@ -3,6 +3,7 @@ import { select as d3_select, event as d3_event } from 'd3-selection';
import * as countryCoder from '@ideditor/country-coder';
import { t, textDirection } from '../../util/locale';
import { geoExtent } from '../../geo';
import { utilGetSetValue, utilNoAuto, utilRebind } from '../../util';
import { svgIcon } from '../../svg/icon';
@@ -19,7 +20,8 @@ export function uiFieldText(field, context) {
var dispatch = d3_dispatch('change');
var input = d3_select(null);
var outlinkButton = d3_select(null);
var _entity;
var _entityIDs = [];
var _tags;
var _phoneFormats = {};
if (field.type === 'tel') {
@@ -29,7 +31,8 @@ export function uiFieldText(field, context) {
}
function i(selection) {
var preset = _entity && context.presets().match(_entity, context.graph());
var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
var preset = entity && context.presets().match(entity, context.graph());
var isLocked = preset && preset.suggestion && field.id === 'brand';
field.locked(isLocked);
@@ -50,7 +53,6 @@ export function uiFieldText(field, context) {
.append('input')
.attr('type', field.type === 'identifier' ? 'text' : field.type)
.attr('id', fieldID)
.attr('placeholder', field.placeholder() || t('inspector.unknown'))
.attr('maxlength', context.maxCharsForTagValue())
.classed(field.type, true)
.call(utilNoAuto)
@@ -64,9 +66,9 @@ export function uiFieldText(field, context) {
.on('change', change());
if (field.type === 'tel' && _entity) {
var center = _entity.extent(context.graph()).center();
var countryCode = countryCoder.iso1A2Code(center);
if (field.type === 'tel') {
var extent = combinedEntityExtent();
var countryCode = extent && countryCoder.iso1A2Code(extent.center());
var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
if (format) {
wrap.selectAll('#' + fieldID)
@@ -112,7 +114,6 @@ export function uiFieldText(field, context) {
.attr('tabindex', -1)
.call(svgIcon('#iD-icon-out-link'))
.attr('class', 'form-field-button foreign-id-permalink')
.classed('disabled', !validIdentifierValueForLink())
.attr('title', function() {
var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
if (domainResults.length >= 2 && domainResults[1]) {
@@ -161,6 +162,9 @@ export function uiFieldText(field, context) {
var t = {};
var val = utilGetSetValue(input).trim() || undefined;
// don't override multiple values with blank string
if (!val && Array.isArray(_tags[field.key])) return;
if (!onInput) {
if (field.type === 'number' && val !== undefined) {
var vals = val.split(';');
@@ -178,15 +182,22 @@ export function uiFieldText(field, context) {
}
i.entity = function(val) {
if (!arguments.length) return _entity;
_entity = val;
i.entityIDs = function(val) {
if (!arguments.length) return _entityIDs;
_entityIDs = val;
return i;
};
i.tags = function(tags) {
utilGetSetValue(input, tags[field.key] || '');
_tags = tags;
var isMixed = Array.isArray(tags[field.key]);
utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
.attr('title', isMixed ? tags[field.key].filter(Boolean).join('; ') : undefined)
.attr('placeholder', isMixed ? t('inspector.multiple_values') : (field.placeholder() || t('inspector.unknown')))
.classed('mixed', isMixed);
if (outlinkButton && !outlinkButton.empty()) {
var disabled = !validIdentifierValueForLink();
@@ -200,5 +211,12 @@ export function uiFieldText(field, context) {
if (node) node.focus();
};
function combinedEntityExtent() {
return _entityIDs && _entityIDs.length && _entityIDs.reduce(function(extent, entityID) {
var entity = context.graph().entity(entityID);
return extent.extend(entity.extent(context.graph()));
}, geoExtent());
}
return utilRebind(i, dispatch, 'on');
}