diff --git a/css/80_app.css b/css/80_app.css index af299187e..79979118a 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -679,8 +679,6 @@ button.save.has-count .count::before { } - - .feature-list-pane .inspector-body { top: 120px; } @@ -1367,6 +1365,18 @@ input[type=number] { opacity: .5; } +.checkselect .reverser.button { + display: block; + float: right; + background: #7092ff; + border-radius: 2px; + padding: 0px 8px; + color: white; +} +.checkselect .reverser.button.hide { + display: none; +} + /* Preset form radio button */ .toggle-list button.remove { diff --git a/data/core.yaml b/data/core.yaml index 6d3e825eb..cc2807706 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -320,6 +320,7 @@ en: check: "yes": "Yes" "no": "No" + reverser: "Change Direction" add: Add none: None node: Node diff --git a/data/presets/README.md b/data/presets/README.md index e145a0ce4..3e9fb6ccf 100644 --- a/data/presets/README.md +++ b/data/presets/README.md @@ -115,6 +115,7 @@ The complete JSON schema for fields can be found in [`data/presets/schema/field. **Checkboxes** * `check` - 3-state checkbox: `yes`, `no`, unknown (no tag) * `defaultCheck` - 2-state checkbox where checked produces `yes` and unchecked produces no tag +* `onewayCheck` - 3-state checkbox for `oneway` fields, with extra button for direction switching **Radio Buttons** * `radio` - Multiple choice radio button field diff --git a/data/presets/fields.json b/data/presets/fields.json index d13bd5fa0..c51733d1b 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -1102,7 +1102,7 @@ }, "oneway_yes": { "key": "oneway", - "type": "check", + "type": "onewayCheck", "label": "One Way", "strings": { "options": { @@ -1114,7 +1114,7 @@ }, "oneway": { "key": "oneway", - "type": "check", + "type": "onewayCheck", "label": "One Way", "strings": { "options": { diff --git a/data/presets/fields/oneway.json b/data/presets/fields/oneway.json index 2b9b0fe02..851dfbfcf 100644 --- a/data/presets/fields/oneway.json +++ b/data/presets/fields/oneway.json @@ -1,6 +1,6 @@ { "key": "oneway", - "type": "check", + "type": "onewayCheck", "label": "One Way", "strings": { "options": { diff --git a/data/presets/fields/oneway_yes.json b/data/presets/fields/oneway_yes.json index 64b42b56a..cc978924e 100644 --- a/data/presets/fields/oneway_yes.json +++ b/data/presets/fields/oneway_yes.json @@ -1,6 +1,6 @@ { "key": "oneway", - "type": "check", + "type": "onewayCheck", "label": "One Way", "strings": { "options": { diff --git a/data/presets/schema/field.json b/data/presets/schema/field.json index d7102929d..3855b644f 100644 --- a/data/presets/schema/field.json +++ b/data/presets/schema/field.json @@ -61,6 +61,7 @@ "multiCombo", "networkCombo", "number", + "onewayCheck", "radio", "restrictions", "tel", diff --git a/dist/locales/en.json b/dist/locales/en.json index 6d304d38c..795204975 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -398,7 +398,8 @@ "edit": "Edit feature", "check": { "yes": "Yes", - "no": "No" + "no": "No", + "reverser": "Change Direction" }, "add": "Add", "none": "None", diff --git a/modules/ui/fields/check.js b/modules/ui/fields/check.js index 0c3f7085e..34c616b70 100644 --- a/modules/ui/fields/check.js +++ b/modules/ui/fields/check.js @@ -1,12 +1,14 @@ import * as d3 from 'd3'; import { utilRebind } from '../../util/rebind'; import { t } from '../../util/locale'; -import { osmOneWayTags } from '../../osm/index'; +import { actionReverse } from '../../actions'; +import { osmOneWayTags } from '../../osm'; export { uiFieldCheck as uiFieldDefaultCheck }; +export { uiFieldCheck as uiFieldOnewayCheck }; -export function uiFieldCheck(field) { +export function uiFieldCheck(field, context) { var dispatch = d3.dispatch('change'), options = field.strings && field.strings.options, values = [], @@ -14,7 +16,10 @@ export function uiFieldCheck(field) { input = d3.select(null), text = d3.select(null), label = d3.select(null), - entity, value; + reverser = d3.select(null), + impliedYes = (field.id === 'oneway_yes'), + entity, + value; if (options) { for (var k in options) { @@ -24,7 +29,7 @@ export function uiFieldCheck(field) { } else { values = [undefined, 'yes']; texts = [t('inspector.unknown'), t('inspector.check.yes')]; - if (field.type === 'check') { + if (field.type !== 'defaultCheck') { values.push('no'); texts.push(t('inspector.check.no')); } @@ -32,11 +37,12 @@ export function uiFieldCheck(field) { var check = function(selection) { - // hack: pretend oneway field is a oneway_yes field + // hack: pretend `oneway` field is a `oneway_yes` field // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841 if (field.id === 'oneway') { for (var key in entity.tags) { if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) { + impliedYes = true; texts[0] = t('presets.fields.oneway_yes.options.undefined'); break; } @@ -54,7 +60,7 @@ export function uiFieldCheck(field) { enter .append('input') - .property('indeterminate', field.type === 'check') + .property('indeterminate', field.type !== 'defaultCheck') .attr('type', 'checkbox') .attr('id', 'preset-input-' + field.id); @@ -63,9 +69,20 @@ export function uiFieldCheck(field) { .text(texts[0]) .attr('class', 'value'); + if (field.type === 'onewayCheck') { + var isHidden = !(value === 'yes' || (impliedYes && !value)); + enter + .append('a') + .attr('id', 'preset-input-' + field.id + '-reverser') + .attr('class', 'reverser button' + (isHidden ? ' hide' : '')) + .text(t('inspector.check.reverser')) + .attr('href', '#'); + } + label = label.merge(enter); input = label.selectAll('input'); text = label.selectAll('span.value'); + reverser = label.selectAll('.reverser'); input .on('click', function() { @@ -74,6 +91,16 @@ export function uiFieldCheck(field) { dispatch.call('change', this, t); d3.event.stopPropagation(); }); + + reverser + .on('click', function() { + d3.event.preventDefault(); + d3.event.stopPropagation(); + context.perform( + actionReverse(entity.id), + t('operations.reverse.annotation') + ); + }); }; @@ -86,10 +113,19 @@ export function uiFieldCheck(field) { check.tags = function(tags) { value = tags[field.key]; - input.property('indeterminate', field.type === 'check' && !value); - input.property('checked', value === 'yes'); - text.text(texts[values.indexOf(value)]); - label.classed('set', !!value); + + input + .property('indeterminate', field.type !== 'defaultCheck' && !value) + .property('checked', value === 'yes'); + + text + .text(texts[values.indexOf(value)]); + + label + .classed('set', !!value); + + reverser + .classed('hide', !(value === 'yes' || (impliedYes && !value))); }; diff --git a/modules/ui/fields/index.js b/modules/ui/fields/index.js index 716c716ec..77d55bccd 100644 --- a/modules/ui/fields/index.js +++ b/modules/ui/fields/index.js @@ -14,7 +14,8 @@ export * from './wikipedia'; import { uiFieldCheck, - uiFieldDefaultCheck + uiFieldDefaultCheck, + uiFieldOnewayCheck } from './check'; import { @@ -47,22 +48,23 @@ export var uiFields = { access: uiFieldAccess, address: uiFieldAddress, check: uiFieldCheck, - defaultCheck: uiFieldDefaultCheck, combo: uiFieldCombo, - typeCombo: uiFieldTypeCombo, + cycleway: uiFieldCycleway, + defaultCheck: uiFieldDefaultCheck, + email: uiFieldEmail, + lanes: uiFieldLanes, + localized: uiFieldLocalized, + maxspeed: uiFieldMaxspeed, multiCombo: uiFieldMultiCombo, networkCombo: uiFieldNetworkCombo, - cycleway: uiFieldCycleway, - text: uiFieldText, - url: uiFieldUrl, number: uiFieldNumber, - email: uiFieldEmail, - tel: uiFieldTel, - localized: uiFieldLocalized, - lanes: uiFieldLanes, - maxspeed: uiFieldMaxspeed, + onewayCheck: uiFieldOnewayCheck, radio: uiFieldRadio, restrictions: uiFieldRestrictions, + tel: uiFieldTel, + text: uiFieldText, textarea: uiFieldTextarea, + typeCombo: uiFieldTypeCombo, + url: uiFieldUrl, wikipedia: uiFieldWikipedia };