mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 17:23:02 +00:00
* Fields: Break long lines in combo dropdown instead of ellipsis Longer translations keys are not readable with ellipsis and the tooltip only shows the description (or fallback), not the title. This change makes long use line breaks with small line height to make the whole text visible. * Fields: Use table styles for rows to maximize available space The flex box solution gave a lot of space to the label. This solution minimizes that space to give more space to the values dropdown.
345 lines
9.7 KiB
JavaScript
345 lines
9.7 KiB
JavaScript
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
|
import { select as d3_select } from 'd3-selection';
|
|
|
|
import { presetManager } from '../../presets';
|
|
import { t } from '../../core/localizer';
|
|
import { uiField } from '../field';
|
|
import { utilArrayUnion, utilRebind } from '../../util';
|
|
|
|
|
|
export { uiFieldRadio as uiFieldStructureRadio };
|
|
|
|
|
|
export function uiFieldRadio(field, context) {
|
|
var dispatch = d3_dispatch('change');
|
|
var placeholder = d3_select(null);
|
|
var wrap = d3_select(null);
|
|
var labels = d3_select(null);
|
|
var radios = d3_select(null);
|
|
var radioData = (field.options || field.keys).slice(); // shallow copy
|
|
var typeField;
|
|
var layerField;
|
|
var _oldType = {};
|
|
var _entityIDs = [];
|
|
|
|
|
|
function selectedKey() {
|
|
var node = wrap.selectAll('.form-field-input-radio label.active input');
|
|
return !node.empty() && node.datum();
|
|
}
|
|
|
|
|
|
function radio(selection) {
|
|
selection.classed('preset-radio', true);
|
|
|
|
wrap = selection.selectAll('.form-field-input-wrap')
|
|
.data([0]);
|
|
|
|
var enter = wrap.enter()
|
|
.append('div')
|
|
.attr('class', 'form-field-input-wrap form-field-input-radio');
|
|
|
|
enter
|
|
.append('span')
|
|
.attr('class', 'placeholder');
|
|
|
|
wrap = wrap
|
|
.merge(enter);
|
|
|
|
|
|
placeholder = wrap.selectAll('.placeholder');
|
|
|
|
labels = wrap.selectAll('label')
|
|
.data(radioData);
|
|
|
|
enter = labels.enter()
|
|
.append('label');
|
|
|
|
var stringsField = field.resolveReference('stringsCrossReference');
|
|
enter
|
|
.append('input')
|
|
.attr('type', 'radio')
|
|
.attr('name', field.id)
|
|
.attr('value', function(d) { return stringsField.t('options.' + d, { 'default': d }); })
|
|
.attr('checked', false);
|
|
|
|
enter
|
|
.append('span')
|
|
.each(function(d) { stringsField.t.append('options.' + d, { 'default': d })(d3_select(this)); });
|
|
|
|
labels = labels
|
|
.merge(enter);
|
|
|
|
radios = labels.selectAll('input')
|
|
.on('change', changeRadio);
|
|
|
|
}
|
|
|
|
|
|
function structureExtras(selection, tags) {
|
|
var selected = selectedKey() || tags.layer !== undefined;
|
|
var type = presetManager.field(selected);
|
|
var layer = presetManager.field('layer');
|
|
var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
|
|
|
|
|
|
var extrasWrap = selection.selectAll('.structure-extras-wrap')
|
|
.data(selected ? [0] : []);
|
|
|
|
extrasWrap.exit()
|
|
.remove();
|
|
|
|
extrasWrap = extrasWrap.enter()
|
|
.append('div')
|
|
.attr('class', 'structure-extras-wrap')
|
|
.merge(extrasWrap);
|
|
|
|
var list = extrasWrap.selectAll('ul')
|
|
.data([0]);
|
|
|
|
list = list.enter()
|
|
.append('ul')
|
|
.attr('class', 'rows')
|
|
.merge(list);
|
|
|
|
|
|
// Type
|
|
if (type) {
|
|
if (!typeField || typeField.id !== selected) {
|
|
typeField = uiField(context, type, _entityIDs, { wrap: false })
|
|
.on('change', changeType);
|
|
}
|
|
typeField.tags(tags);
|
|
} else {
|
|
typeField = null;
|
|
}
|
|
|
|
var typeItem = list.selectAll('.structure-type-item')
|
|
.data(typeField ? [typeField] : [], function(d) { return d.id; });
|
|
|
|
// Exit
|
|
typeItem.exit()
|
|
.remove();
|
|
|
|
// Enter
|
|
var typeEnter = typeItem.enter()
|
|
.insert('li', ':first-child')
|
|
.attr('class', 'labeled-input structure-type-item');
|
|
|
|
typeEnter
|
|
.append('div')
|
|
.attr('class', 'label structure-label-type')
|
|
.attr('for', 'preset-input-' + selected)
|
|
.call(t.append('inspector.radio.structure.type'));
|
|
|
|
typeEnter
|
|
.append('div')
|
|
.attr('class', 'structure-input-type-wrap');
|
|
|
|
// Update
|
|
typeItem = typeItem
|
|
.merge(typeEnter);
|
|
|
|
if (typeField) {
|
|
typeItem.selectAll('.structure-input-type-wrap')
|
|
.call(typeField.render);
|
|
}
|
|
|
|
|
|
// Layer
|
|
if (layer && showLayer) {
|
|
if (!layerField) {
|
|
layerField = uiField(context, layer, _entityIDs, { wrap: false })
|
|
.on('change', changeLayer);
|
|
}
|
|
layerField.tags(tags);
|
|
field.keys = utilArrayUnion(field.keys, ['layer']);
|
|
} else {
|
|
layerField = null;
|
|
field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
|
|
}
|
|
|
|
var layerItem = list.selectAll('.structure-layer-item')
|
|
.data(layerField ? [layerField] : []);
|
|
|
|
// Exit
|
|
layerItem.exit()
|
|
.remove();
|
|
|
|
// Enter
|
|
var layerEnter = layerItem.enter()
|
|
.append('li')
|
|
.attr('class', 'labeled-input structure-layer-item');
|
|
|
|
layerEnter
|
|
.append('div')
|
|
.attr('class', 'label structure-label-layer')
|
|
.attr('for', 'preset-input-layer')
|
|
.call(t.append('inspector.radio.structure.layer'));
|
|
|
|
layerEnter
|
|
.append('div')
|
|
.attr('class', 'structure-input-layer-wrap');
|
|
|
|
// Update
|
|
layerItem = layerItem
|
|
.merge(layerEnter);
|
|
|
|
if (layerField) {
|
|
layerItem.selectAll('.structure-input-layer-wrap')
|
|
.call(layerField.render);
|
|
}
|
|
}
|
|
|
|
|
|
function changeType(t, onInput) {
|
|
var key = selectedKey();
|
|
if (!key) return;
|
|
|
|
var val = t[key];
|
|
if (val !== 'no') {
|
|
_oldType[key] = val;
|
|
}
|
|
|
|
if (field.type === 'structureRadio') {
|
|
// remove layer if it should not be set
|
|
if (val === 'no' ||
|
|
(key !== 'bridge' && key !== 'tunnel') ||
|
|
(key === 'tunnel' && val === 'building_passage')) {
|
|
t.layer = undefined;
|
|
}
|
|
// add layer if it should be set
|
|
if (t.layer === undefined) {
|
|
if (key === 'bridge' && val !== 'no') {
|
|
t.layer = '1';
|
|
}
|
|
if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
|
|
t.layer = '-1';
|
|
}
|
|
}
|
|
}
|
|
|
|
dispatch.call('change', this, t, onInput);
|
|
}
|
|
|
|
|
|
function changeLayer(t, onInput) {
|
|
if (t.layer === '0') {
|
|
t.layer = undefined;
|
|
}
|
|
dispatch.call('change', this, t, onInput);
|
|
}
|
|
|
|
|
|
function changeRadio() {
|
|
var t = {};
|
|
var activeKey;
|
|
|
|
if (field.key) {
|
|
t[field.key] = undefined;
|
|
}
|
|
|
|
radios.each(function(d) {
|
|
var active = d3_select(this).property('checked');
|
|
if (active) activeKey = d;
|
|
|
|
if (field.key) {
|
|
if (active) t[field.key] = d;
|
|
} else {
|
|
var val = _oldType[activeKey] || 'yes';
|
|
t[d] = active ? val : undefined;
|
|
}
|
|
});
|
|
|
|
if (field.type === 'structureRadio') {
|
|
if (activeKey === 'bridge') {
|
|
t.layer = '1';
|
|
} else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
|
|
t.layer = '-1';
|
|
} else {
|
|
t.layer = undefined;
|
|
}
|
|
}
|
|
|
|
dispatch.call('change', this, t);
|
|
}
|
|
|
|
|
|
radio.tags = function(tags) {
|
|
function isOptionChecked(d) {
|
|
if (field.key) {
|
|
return tags[field.key] === d;
|
|
}
|
|
return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
|
|
}
|
|
|
|
function isMixed(d) {
|
|
if (field.key) {
|
|
return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
|
|
}
|
|
return Array.isArray(tags[d]);
|
|
}
|
|
|
|
radios.property('checked', function(d) {
|
|
return isOptionChecked(d) &&
|
|
(field.key || field.options.filter(isOptionChecked).length === 1);
|
|
});
|
|
|
|
labels
|
|
.classed('active', function(d) {
|
|
if (field.key) {
|
|
return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
|
|
|| tags[field.key] === d;
|
|
}
|
|
return Array.isArray(tags[d]) && tags[d].some(v => typeof v === 'string' && v.toLowerCase() !== 'no') ||
|
|
!!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
|
|
})
|
|
.classed('mixed', isMixed)
|
|
.attr('title', function(d) {
|
|
return isMixed(d) ? t('inspector.unshared_value_tooltip') : null;
|
|
});
|
|
|
|
|
|
var selection = radios.filter(function() { return this.checked; });
|
|
|
|
if (selection.empty()) {
|
|
placeholder.text('');
|
|
placeholder.call(t.append('inspector.none'));
|
|
} else {
|
|
placeholder.text(selection.attr('value'));
|
|
_oldType[selection.datum()] = tags[selection.datum()];
|
|
}
|
|
|
|
if (field.type === 'structureRadio') {
|
|
// For waterways without a tunnel tag, set 'culvert' as
|
|
// the _oldType to default to if the user picks 'tunnel'
|
|
if (!!tags.waterway && !_oldType.tunnel) {
|
|
_oldType.tunnel = 'culvert';
|
|
}
|
|
|
|
wrap.call(structureExtras, tags);
|
|
}
|
|
};
|
|
|
|
|
|
radio.focus = function() {
|
|
radios.node().focus();
|
|
};
|
|
|
|
|
|
radio.entityIDs = function(val) {
|
|
if (!arguments.length) return _entityIDs;
|
|
_entityIDs = val;
|
|
_oldType = {};
|
|
return radio;
|
|
};
|
|
|
|
|
|
radio.isAllowed = function() {
|
|
return _entityIDs.length === 1;
|
|
};
|
|
|
|
|
|
return utilRebind(radio, dispatch, 'on');
|
|
}
|