From 74cc038c57e1d5c81bfa70122051174916d455af Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 18 Mar 2017 02:09:33 -0400 Subject: [PATCH] Add layer and type subfields below structure field (see #2087) --- css/80_app.css | 41 ++++--- data/presets/README.md | 1 + data/presets/fields.json | 2 +- data/presets/fields/structure.json | 2 +- data/presets/schema/field.json | 1 + modules/ui/fields/index.js | 7 +- modules/ui/fields/radio.js | 177 +++++++++++++++++++++++++++-- 7 files changed, 201 insertions(+), 30 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index af3e8280d..480b74adc 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1077,6 +1077,7 @@ button.save.has-count .count::before { .inspector-hover .checkselect label:last-of-type, .inspector-hover .preset-input-wrap .label, .inspector-hover .form-field-multicombo, +.inspector-hover .structure-extras-wrap, .inspector-hover input, .inspector-hover label { background: #ececec; @@ -1195,21 +1196,29 @@ button.save.has-count .count::before { width: 50%; } -/* preset form access */ - .preset-input-wrap .label { height: 30px; background: #F6F6F6; padding: 5px 10px; } + +/* preset form access */ +/* preset form cycleway */ + +.form-field-structure .structure-extras-wrap li, +.form-field-cycleway .preset-input-wrap li, .form-field-access .preset-input-wrap li { - border-bottom: 1px solid #CCC; + border-bottom: 1px solid #ccc; } +.form-field-structure .structure-extras-wrap li:last-child, +.form-field-cycleway .preset-input-wrap li:last-child, .form-field-access .preset-input-wrap li:last-child { border-bottom: 0; } - +.structure-input-type-wrap input, +.structure-input-layer-wrap input, +.preset-input-cycleway-wrap input, .preset-input-access-wrap input { border-radius: 0; border-width: 0; @@ -1220,6 +1229,16 @@ button.save.has-count .count::before { border-bottom-right-radius: 4px; } +.structure-extras-wrap { + padding: 10px 10px; + background: #fff; +} +.structure-extras-wrap ul { + border: 1px solid #ccc; + border-radius: 4px; +} + + /* preset form multicombo */ .form-field-multicombo { @@ -1277,20 +1296,6 @@ button.save.has-count .count::before { border-radius: 4px !important; } -/* preset form cycleway */ - -.form-field-cycleway .preset-input-wrap li { - border-bottom: 1px solid #CCC; -} -.form-field-cycleway .preset-input-wrap li:last-child { - border-bottom: 0; -} - -.preset-input-cycleway-wrap input { - border-radius: 0; - border-width: 0; - border-left-width: 1px; -} /* preset form numbers */ diff --git a/data/presets/README.md b/data/presets/README.md index e45f202ed..c154d4182 100644 --- a/data/presets/README.md +++ b/data/presets/README.md @@ -121,6 +121,7 @@ The complete JSON schema for fields can be found in [`data/presets/schema/field. **Radio Buttons** * `radio` - Multiple choice radio button field +* `structureRadio` - Multiple choice structure radio button field, with extra input for bridge/tunnel level **Special** * `access` - Block of dropdowns for defining the `access=*` tags on a highway diff --git a/data/presets/fields.json b/data/presets/fields.json index d55f912b7..e68af6cc1 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -1583,7 +1583,7 @@ } }, "structure": { - "type": "radio", + "type": "structureRadio", "keys": [ "bridge", "tunnel", diff --git a/data/presets/fields/structure.json b/data/presets/fields/structure.json index 6d9642819..095400a9e 100644 --- a/data/presets/fields/structure.json +++ b/data/presets/fields/structure.json @@ -1,5 +1,5 @@ { - "type": "radio", + "type": "structureRadio", "keys": [ "bridge", "tunnel", diff --git a/data/presets/schema/field.json b/data/presets/schema/field.json index 92698150b..504bdc442 100644 --- a/data/presets/schema/field.json +++ b/data/presets/schema/field.json @@ -65,6 +65,7 @@ "radio", "restrictions", "semiCombo", + "structureRadio", "tel", "textarea", "text", diff --git a/modules/ui/fields/index.js b/modules/ui/fields/index.js index b92f2bf06..27950023a 100644 --- a/modules/ui/fields/index.js +++ b/modules/ui/fields/index.js @@ -34,13 +34,17 @@ import { uiFieldUrl } from './input'; +import { + uiFieldRadio, + uiFieldStructureRadio +} from './radio'; + import { uiFieldAccess } from './access'; import { uiFieldAddress } from './address'; import { uiFieldCycleway } from './cycleway'; import { uiFieldLanes } from './lanes'; import { uiFieldLocalized } from './localized'; import { uiFieldMaxspeed } from './maxspeed'; -import { uiFieldRadio } from './radio'; import { uiFieldRestrictions } from './restrictions'; import { uiFieldTextarea } from './textarea'; import { uiFieldWikipedia } from './wikipedia'; @@ -63,6 +67,7 @@ export var uiFields = { radio: uiFieldRadio, restrictions: uiFieldRestrictions, semiCombo: uiFieldSemiCombo, + structureRadio: uiFieldStructureRadio, tel: uiFieldTel, text: uiFieldText, textarea: uiFieldTextarea, diff --git a/modules/ui/fields/radio.js b/modules/ui/fields/radio.js index 3017df23c..a7020a1c2 100644 --- a/modules/ui/fields/radio.js +++ b/modules/ui/fields/radio.js @@ -1,19 +1,29 @@ import * as d3 from 'd3'; import { t } from '../../util/locale'; -import { utilRebind } from '../../util/rebind'; +import { + utilGetSetValue, + utilNoAuto, + utilRebind +} from '../../util'; + + +export { uiFieldRadio as uiFieldStructureRadio }; export function uiFieldRadio(field) { var dispatch = d3.dispatch('change'), placeholder = d3.select(null), + wrap = d3.select(null), labels = d3.select(null), - radios = d3.select(null); + radios = d3.select(null), + typeInput = d3.select(null), + layerInput = d3.select(null); function radio(selection) { selection.classed('preset-radio', true); - var wrap = selection.selectAll('.preset-input-wrap') + wrap = selection.selectAll('.preset-input-wrap') .data([0]); var enter = wrap.enter() @@ -36,34 +46,174 @@ export function uiFieldRadio(field) { enter = labels.enter() .append('label'); - enter.append('input') + 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') + enter + .append('span') .text(function(d) { return field.t('options.' + d, { 'default': d }); }); labels = labels .merge(enter); radios = labels.selectAll('input') - .on('change', change); + .on('change', changeRadio); } - function change() { + function structureExtras(selection, tags) { + function checked(d) { + return !!(tags[d] && tags[d] !== 'no'); + } + + var extrasWrap = selection.selectAll('.structure-extras-wrap') + .data([0]); + + extrasWrap = extrasWrap.enter() + .append('div') + .attr('class', 'structure-extras-wrap') + .merge(extrasWrap); + + var list = extrasWrap.selectAll('ul') + .data([0]); + + list = list.enter() + .append('ul') + .merge(list); + + + // Type + var typeItem = list.selectAll('.structure-type-item') + .data([0]); + + var typeEnter = typeItem.enter() + .append('li') + .attr('class', 'cf structure-type-item'); + + typeEnter + .append('span') + .attr('class', 'col6 label structure-label-type') + .attr('for', 'structure-input-type') + .text('Type'); + + typeEnter + .append('div') + .attr('class', 'col6 structure-input-type-wrap') + .append('input') + .attr('type', 'text') + .attr('class', 'structure-input-type') + .attr('placeholder', t('inspector.unknown')) + .call(utilNoAuto); + + typeItem = typeItem + .merge(typeEnter); + + typeInput = typeItem.selectAll('.structure-input-type') + .on('change', changeRadio) + .on('blur', changeRadio); + + + // Layer + var showLayer = checked('bridge') || checked('tunnel'); + var layerItem = list.selectAll('.structure-layer-item') + .data(showLayer ? [0] : []); + + layerItem.exit() + .remove(); + + var layerEnter = layerItem.enter() + .append('li') + .attr('class', 'cf structure-layer-item'); + + layerEnter + .append('span') + .attr('class', 'col6 label structure-label-layer') + .attr('for', 'structure-input-layer') + .text('Layer'); + + layerEnter + .append('div') + .attr('class', 'col6 structure-input-layer-wrap') + .append('input') + .attr('type', 'text') + .attr('class', 'structure-input-layer') + .attr('placeholder', '0') + .call(utilNoAuto); + + var spin = layerEnter + .append('div') + .attr('class', 'spin-control'); + + spin + .append('button') + .datum(1) + .attr('class', 'increment') + .attr('tabindex', -1); + + spin + .append('button') + .datum(-1) + .attr('class', 'decrement') + .attr('tabindex', -1); + + layerItem = layerItem + .merge(layerEnter); + + layerInput = layerItem.selectAll('.structure-input-layer') + .on('change', changeLayer) + .on('blur', changeLayer); + + layerItem.selectAll('button') + .on('click', function(d) { + d3.event.preventDefault(); + var num = parseInt(layerInput.node().value || 0, 10); + if (!isNaN(num)) layerInput.node().value = num + d; + changeLayer(); + }); + + } + + + function changeLayer() { + var t = { layer: layerInput.node().value || undefined }; + dispatch.call('change', this, t); + } + + + function changeRadio() { + function checked(d) { + return !!(t[d] && t[d] !== 'no'); + } + var t = {}; - if (field.key) t[field.key] = undefined; + if (field.key) { + t[field.key] = undefined; + } + radios.each(function(d) { var active = d3.select(this).property('checked'); if (field.key) { if (active) t[field.key] = d; } else { - t[d] = active ? 'yes' : undefined; + var val = utilGetSetValue(typeInput) || 'yes'; + t[d] = active ? val : undefined; } }); + + if (field.type === 'structureRadio') { + if (checked('bridge')) { + t.layer = '1'; + } else if (checked('tunnel')) { + t.layer = '-1'; + } else { + t.layer = undefined; + } + } + dispatch.call('change', this, t); } @@ -80,10 +230,19 @@ export function uiFieldRadio(field) { labels.classed('active', checked); radios.property('checked', checked); var selection = radios.filter(function() { return this.checked; }); + var typeVal = ''; + if (selection.empty()) { placeholder.text(t('inspector.none')); } else { placeholder.text(selection.attr('value')); + typeVal = tags[selection.datum()]; + } + + if (field.type === 'structureRadio') { + wrap.call(structureExtras, tags); + utilGetSetValue(typeInput, typeVal || ''); + utilGetSetValue(layerInput, tags.layer || ''); } };