modules/ui/fields/radio.js (226 lines of code) (raw):
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';
import { t } from '../../util/locale';
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.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice(); // shallow copy
var typeField;
var layerField;
var _oldType = {};
var _entity;
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');
enter
.append('input')
.attr('type', 'radio')
.attr('name', field.id)
.attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
.attr('checked', false);
enter
.append('span')
.text(function(d) { return field.t('options.' + d, { 'default': d }); });
labels = labels
.merge(enter);
radios = labels.selectAll('input')
.on('change', changeRadio);
}
function structureExtras(selection, tags) {
var selected = selectedKey() || tags.layer !== undefined;
var type = context.presets().field(selected);
var layer = context.presets().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, _entity, { 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('span')
.attr('class', 'label structure-label-type')
.attr('for', 'preset-input-' + selected)
.text(t('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, _entity, { 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('span')
.attr('class', 'label structure-label-layer')
.attr('for', 'preset-input-layer')
.text(t('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 checked(d) {
if (field.key) {
return tags[field.key] === d;
} else {
return !!(tags[d] && tags[d].toLowerCase() !== 'no');
}
}
labels.classed('active', checked);
radios.property('checked', checked);
var selection = radios.filter(function() { return this.checked; });
if (selection.empty()) {
placeholder.text(t('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.entity = function(val) {
if (!arguments.length) return _entity;
_entity = val;
_oldType = {};
return radio;
};
return utilRebind(radio, dispatch, 'on');
}