Merge branch 'field_refactor'

This commit is contained in:
Bryan Housel
2017-08-06 22:17:23 -04:00
25 changed files with 709 additions and 544 deletions
+67 -33
View File
@@ -968,7 +968,7 @@ button.save.has-count .count::before {
position: absolute;
top: 0;
right: 0;
width: 10%;
width: 32px;
background: #fafafa;
}
@@ -1024,22 +1024,22 @@ button.save.has-count .count::before {
/* preset form basics */
.inspector-preset {
.preset-editor {
overflow: hidden;
padding-bottom: 10px;
}
.inspector-preset a.hide-toggle {
.preset-editor a.hide-toggle {
margin: 0 20px 10px 20px;
}
.inspector-preset .preset-form {
.preset-editor .preset-form {
padding: 10px;
margin: 0 10px 10px 10px;
border-radius: 8px;
}
.inspector-preset .preset-form:empty {
.preset-editor .preset-form:empty {
display: none;
}
@@ -1056,7 +1056,8 @@ button.save.has-count .count::before {
transition: margin-bottom 200ms;
}
.form-field:last-child {
.form-field.nowrap,
.wrap-form-field:last-child .form-field {
margin-bottom: 0;
}
@@ -1097,7 +1098,7 @@ button.save.has-count .count::before {
.form-label button {
border-left: 1px solid #ccc;
width: 10%;
width: 32px;
height: 100%;
border-radius: 0;
background: #f6f6f6;
@@ -1106,6 +1107,7 @@ button.save.has-count .count::before {
border-left: none;
border-right: 1px solid #CCC;
border-radius: 4px 0 0 0;
width: 31px;
}
.form-label button:hover {
background: #f1f1f1;
@@ -1146,6 +1148,7 @@ button.save.has-count .count::before {
.inspector-hover .form-field-multicombo,
.inspector-hover .structure-extras-wrap,
.inspector-hover input,
.inspector-hover textarea,
.inspector-hover label {
background: #ececec;
}
@@ -1296,6 +1299,7 @@ button.save.has-count .count::before {
/* preset form access */
/* preset form cycleway */
/* preset form structure extras */
.form-field-structure .structure-extras-wrap li,
.form-field-cycleway .preset-input-wrap li,
@@ -1335,6 +1339,34 @@ button.save.has-count .count::before {
border: 1px solid #ccc;
border-radius: 4px;
}
.structure-extras-wrap li:first-child span {
border-top-left-radius: 4px;
}
.structure-extras-wrap li:first-child input {
border-top-right-radius: 4px;
}
.structure-extras-wrap li:last-child span {
border-bottom-left-radius: 4px;
}
.structure-extras-wrap li:last-child input {
border-bottom-right-radius: 4px;
}
[dir='rtl'] .structure-extras-wrap li:first-child span {
border-top-left-radius: 0;
border-top-right-radius: 4px;
}
[dir='rtl'] .structure-extras-wrap li:first-child input {
border-top-right-radius: 0;
border-top-left-radius: 4px;
}
[dir='rtl'] .structure-extras-wrap li:last-child span {
border-bottom-left-radius: 0;
border-bottom-right-radius: 4px;
}
[dir='rtl'] .structure-extras-wrap li:last-child input {
border-bottom-right-radius: 0;
border-bottom-left-radius: 4px;
}
/* preset form multicombo */
@@ -1403,28 +1435,43 @@ input[type=number] {
}
.spin-control {
width: 20%;
height: 29px;
width: 64px;
height: 30px;
display: inline-block;
margin-left: -20%;
margin-left: -64px;
margin-bottom: -11px;
position: relative;
}
[dir='rtl'] .spin-control{
margin-left: 0;
margin-right: -64px;
}
.spin-control button {
right: 1px;
position: relative;
float: left;
height: 100%;
width: 50%;
width: 32px;
border-left: 1px solid #CCC;
border-radius: 0;
background: rgba(0, 0, 0, 0);
}
[dir='rtl'] .spin-control button{
border-left: 0;
border-right: 1px solid #CCC;
}
.spin-control button.decrement {
border-bottom-right-radius: 3px;
}
[dir='rtl'] .spin-control button.decrement {
border-bottom-right-radius: 0;
}
[dir='rtl'] .spin-control button.increment {
border-bottom-left-radius: 3px;
right: 0;
}
.spin-control button.decrement::after,
.spin-control button.increment::after {
@@ -1447,6 +1494,7 @@ input[type=number] {
border-right: 5px solid transparent;
}
/* preset form checkbox */
.checkselect label:last-of-type {
@@ -1530,11 +1578,11 @@ input[type=number] {
}
.form-field .wiki-title ~ .combobox-caret {
right: 10%;
right: 32px;
}
[dir='rtl'] .form-field .wiki-title ~ .combobox-caret {
right: auto;
left: 10%;
left: 32px;
}
/* Localized field */
@@ -1549,8 +1597,8 @@ input[type=number] {
.form-field .button-input-action {
position: relative;
right: 1px;
width: 10%;
margin-left: -10%;
width: 32px;
margin-left: -32px;
border: 1px solid #CCC;
border-top-width: 0;
border-right-width: 0;
@@ -1560,7 +1608,7 @@ input[type=number] {
}
[dir='rtl'] .form-field .button-input-action {
margin-left: 0;
margin-right: -10%;
margin-right: -32px;
border-right-width: 1px;
border-radius: 0 0 0 4px;
}
@@ -2708,6 +2756,9 @@ img.tile-removing {
background: none;
color: #ddd;
}
[dir='rtl'] .panel-title button.close {
float: left;
}
.panel-title button.close:hover {
color: #fff;
@@ -4165,23 +4216,6 @@ li.hide + li.version .badge .tooltip .tooltip-arrow {
border-radius: 4px 0 0 4px;
}
/* increment / decrement control - code by Naoufel Razouane */
[dir='rtl'] .spin-control{
margin-left: 0;
margin-right: -20%;
}
[dir='rtl'] .spin-control button{
border-left: 0;
border-right: 1px solid #CCC;
}
[dir='rtl'] .spin-control button.decrement{
border-bottom-right-radius: 0;
}
[dir='rtl'] .spin-control button.increment{
border-bottom-left-radius: 3px;
}
/* modal */
[dir='rtl'] .modal > button {
position: absolute;
+27
View File
@@ -246,6 +246,11 @@ en:
brand:
# brand=*
label: Brand
bridge:
# bridge=*
label: Type
# bridge field placeholder
placeholder: Default
building:
# building=*
label: Building
@@ -369,6 +374,11 @@ en:
currency_multi:
# 'currency:=*'
label: Currency Types
cutting:
# cutting=*
label: Type
# cutting field placeholder
placeholder: Default
cycle_network:
# cycle_network=*
label: Network
@@ -458,6 +468,11 @@ en:
label: Email
# email field placeholder
placeholder: example@example.com
embankment:
# embankment=*
label: Type
# embankment field placeholder
placeholder: Default
emergency:
# emergency=*
label: Emergency
@@ -505,6 +520,11 @@ en:
fixme:
# fixme=*
label: Fix Me
ford:
# ford=*
label: Type
# ford field placeholder
placeholder: Default
fuel:
# fuel=*
label: Fuel
@@ -634,6 +654,8 @@ en:
layer:
# layer=*
label: Layer
# layer field placeholder
placeholder: '0'
leaf_cycle:
# leaf_cycle=*
label: Leaf Cycle
@@ -1361,6 +1383,11 @@ en:
# trees=*
label: Trees
tunnel:
# tunnel=*
label: Type
# tunnel field placeholder
placeholder: Default
tunnel_waterway:
# tunnel=*
label: Tunnel
vending:
+34 -3
View File
@@ -329,6 +329,12 @@
"type": "text",
"label": "Brand"
},
"bridge": {
"key": "bridge",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
},
"building_area": {
"key": "building",
"type": "combo",
@@ -488,6 +494,12 @@
"type": "multiCombo",
"label": "Currency Types"
},
"cutting": {
"key": "cutting",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
},
"cycle_network": {
"key": "cycle_network",
"type": "networkCombo",
@@ -631,6 +643,12 @@
"universal": true,
"label": "Email"
},
"embankment": {
"key": "embankment",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
},
"emergency": {
"key": "emergency",
"type": "check",
@@ -695,6 +713,12 @@
"label": "Fix Me",
"universal": true
},
"ford": {
"key": "ford",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
},
"fuel_multi": {
"key": "fuel:",
"type": "multiCombo",
@@ -883,8 +907,9 @@
},
"layer": {
"key": "layer",
"type": "combo",
"label": "Layer"
"type": "number",
"label": "Layer",
"placeholder": "0"
},
"leaf_cycle_singular": {
"key": "leaf_cycle",
@@ -1842,11 +1867,17 @@
"type": "semiCombo",
"label": "Trees"
},
"tunnel": {
"tunnel_waterway": {
"key": "tunnel",
"type": "combo",
"label": "Tunnel"
},
"tunnel": {
"key": "tunnel",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
},
"vending": {
"key": "vending",
"type": "combo",
+6
View File
@@ -0,0 +1,6 @@
{
"key": "bridge",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
}
+6
View File
@@ -0,0 +1,6 @@
{
"key": "cutting",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
}
+6
View File
@@ -0,0 +1,6 @@
{
"key": "embankment",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
}
+6
View File
@@ -0,0 +1,6 @@
{
"key": "ford",
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
}
+4 -3
View File
@@ -1,5 +1,6 @@
{
"key": "layer",
"type": "combo",
"label": "Layer"
}
"type": "number",
"label": "Layer",
"placeholder": "0"
}
+3 -2
View File
@@ -1,5 +1,6 @@
{
"key": "tunnel",
"type": "combo",
"label": "Tunnel"
"type": "typeCombo",
"label": "Type",
"placeholder": "Default"
}
+5
View File
@@ -0,0 +1,5 @@
{
"key": "tunnel",
"type": "combo",
"label": "Tunnel"
}
+4 -4
View File
@@ -14776,7 +14776,7 @@
"waterway/ditch": {
"icon": "waterway-ditch",
"fields": [
"tunnel"
"tunnel_waterway"
],
"geometry": [
"line"
@@ -14812,7 +14812,7 @@
"waterway/drain": {
"icon": "waterway-stream",
"fields": [
"tunnel"
"tunnel_waterway"
],
"geometry": [
"line"
@@ -14850,7 +14850,7 @@
"icon": "waterway-river",
"fields": [
"name",
"tunnel",
"tunnel_waterway",
"width"
],
"geometry": [
@@ -14921,7 +14921,7 @@
"icon": "waterway-stream",
"fields": [
"name",
"tunnel",
"tunnel_waterway",
"width"
],
"geometry": [
+1 -1
View File
@@ -1,7 +1,7 @@
{
"icon": "waterway-ditch",
"fields": [
"tunnel"
"tunnel_waterway"
],
"geometry": [
"line"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"icon": "waterway-stream",
"fields": [
"tunnel"
"tunnel_waterway"
],
"geometry": [
"line"
+1 -1
View File
@@ -2,7 +2,7 @@
"icon": "waterway-river",
"fields": [
"name",
"tunnel",
"tunnel_waterway",
"width"
],
"geometry": [
+1 -1
View File
@@ -2,7 +2,7 @@
"icon": "waterway-stream",
"fields": [
"name",
"tunnel",
"tunnel_waterway",
"width"
],
"geometry": [
+23 -2
View File
@@ -1282,6 +1282,10 @@
"brand": {
"label": "Brand"
},
"bridge": {
"label": "Type",
"placeholder": "Default"
},
"building_area": {
"label": "Building"
},
@@ -1384,6 +1388,10 @@
"currency_multi": {
"label": "Currency Types"
},
"cutting": {
"label": "Type",
"placeholder": "Default"
},
"cycle_network": {
"label": "Network"
},
@@ -1469,6 +1477,10 @@
"label": "Email",
"placeholder": "example@example.com"
},
"embankment": {
"label": "Type",
"placeholder": "Default"
},
"emergency": {
"label": "Emergency"
},
@@ -1509,6 +1521,10 @@
"fixme": {
"label": "Fix Me"
},
"ford": {
"label": "Type",
"placeholder": "Default"
},
"fuel_multi": {
"label": "Fuel Types"
},
@@ -1624,7 +1640,8 @@
"placeholder": "1, 2, 3..."
},
"layer": {
"label": "Layer"
"label": "Layer",
"placeholder": "0"
},
"leaf_cycle_singular": {
"label": "Leaf Cycle",
@@ -2216,9 +2233,13 @@
"trees": {
"label": "Trees"
},
"tunnel": {
"tunnel_waterway": {
"label": "Tunnel"
},
"tunnel": {
"label": "Type",
"placeholder": "Default"
},
"vending": {
"label": "Type of Goods"
},
+1
View File
@@ -37,6 +37,7 @@ export { rendererFeatures as Features } from './renderer/features';
export { rendererMap as Map } from './renderer/map';
export { rendererTileLayer as TileLayer } from './renderer/tile_layer';
export { utilDetect as Detect } from './util/detect';
export { uiPresetEditor as uiPreset } from './ui/preset_editor';
export var debug = false;
+33 -24
View File
@@ -10,7 +10,7 @@ import { uiRawMemberEditor } from './raw_member_editor';
import { uiRawMembershipEditor } from './raw_membership_editor';
import { uiRawTagEditor } from './raw_tag_editor';
import { uiTagReference } from './tag_reference';
import { uiPreset } from './preset';
import { uiPresetEditor } from './preset_editor';
import { utilRebind } from '../util';
@@ -20,18 +20,18 @@ export function uiEntityEditor(context) {
coalesceChanges = false,
modified = false,
base,
id,
entityId,
activePreset,
reference;
var presetEditor = uiPreset(context)
var presetEditor = uiPresetEditor(context)
.on('change', changeTags);
var rawTagEditor = uiRawTagEditor(context)
.on('change', changeTags);
function entityEditor(selection) {
var entity = context.entity(id),
var entity = context.entity(entityId),
tags = _.clone(entity.tags);
// Header
@@ -63,7 +63,9 @@ export function uiEntityEditor(context) {
.merge(enter);
header.selectAll('.preset-reset')
.on('click', function() { dispatch.call('choose', this, activePreset); });
.on('click', function() {
dispatch.call('choose', this, activePreset);
});
// Body
@@ -88,7 +90,7 @@ export function uiEntityEditor(context) {
enter
.append('div')
.attr('class', 'inspector-border inspector-preset');
.attr('class', 'inspector-border preset-editor');
enter
.append('div')
@@ -119,35 +121,41 @@ export function uiEntityEditor(context) {
.call(reference.body);
body.selectAll('.preset-reset')
.on('click', function() { dispatch.call('choose', this, activePreset); });
.on('click', function() {
dispatch.call('choose', this, activePreset);
});
body.select('.preset-list-item button')
.call(uiPresetIcon()
.geometry(context.geometry(id))
.preset(activePreset));
.geometry(context.geometry(entityId))
.preset(activePreset)
);
body.select('.preset-list-item .label')
.text(activePreset.name());
body.select('.inspector-preset')
body.select('.preset-editor')
.call(presetEditor
.preset(activePreset)
.entityID(id)
.entityID(entityId)
.tags(tags)
.state(state));
.state(state)
);
body.select('.raw-tag-editor')
.call(rawTagEditor
.preset(activePreset)
.entityID(id)
.entityID(entityId)
.tags(tags)
.state(state));
.state(state)
);
if (entity.type === 'relation') {
body.select('.raw-member-editor')
.style('display', 'block')
.call(uiRawMemberEditor(context)
.entityID(id));
.entityID(entityId)
);
} else {
body.select('.raw-member-editor')
.style('display', 'none');
@@ -155,7 +163,8 @@ export function uiEntityEditor(context) {
body.select('.raw-membership-editor')
.call(uiRawMembershipEditor(context)
.entityID(id));
.entityID(entityId)
);
body.select('.key-trap')
.on('keydown.key-trap', function() {
@@ -174,7 +183,7 @@ export function uiEntityEditor(context) {
function historyChanged() {
if (state === 'hide') return;
var entity = context.hasEntity(id),
var entity = context.hasEntity(entityId),
graph = context.graph();
if (!entity) return;
@@ -226,7 +235,7 @@ export function uiEntityEditor(context) {
// Tag changes that fire on input can all get coalesced into a single
// history operation when the user leaves the field. #2342
function changeTags(changed, onInput) {
var entity = context.entity(id),
var entity = context.entity(entityId),
annotation = t('operations.change_tags.annotation'),
tags = _.clone(entity.tags);
@@ -242,9 +251,9 @@ export function uiEntityEditor(context) {
if (!_.isEqual(entity.tags, tags)) {
if (coalesceChanges) {
context.overwrite(actionChangeTags(id, tags), annotation);
context.overwrite(actionChangeTags(entityId, tags), annotation);
} else {
context.perform(actionChangeTags(id, tags), annotation);
context.perform(actionChangeTags(entityId, tags), annotation);
coalesceChanges = !!onInput;
}
}
@@ -267,10 +276,10 @@ export function uiEntityEditor(context) {
entityEditor.entityID = function(_) {
if (!arguments.length) return id;
id = _;
if (!arguments.length) return entityId;
entityId = _;
base = context.graph();
entityEditor.preset(context.presets().match(context.entity(id), base));
entityEditor.preset(context.presets().match(context.entity(entityId), base));
entityEditor.modified(false);
coalesceChanges = false;
return entityEditor;
@@ -281,7 +290,7 @@ export function uiEntityEditor(context) {
if (!arguments.length) return activePreset;
if (_ !== activePreset) {
activePreset = _;
reference = uiTagReference(activePreset.reference(context.geometry(id)), context)
reference = uiTagReference(activePreset.reference(context.geometry(entityId)), context)
.showing(false);
}
return entityEditor;
+182
View File
@@ -0,0 +1,182 @@
import * as d3 from 'd3';
import _ from 'lodash';
import { textDirection } from '../util/locale';
import { svgIcon } from '../svg';
import { uiFields } from './fields';
import { uiTagReference } from './tag_reference';
import { utilRebind } from '../util';
export function uiField(context, presetField, entity, options) {
options = _.extend({
show: true,
wrap: true
}, options);
var dispatch = d3.dispatch('change'),
field = _.clone(presetField),
state = '',
tags = {};
field.impl = uiFields[field.type](field, context)
.on('change', function(t, onInput) {
dispatch.call('change', field, t, onInput);
});
if (field.impl.entity) {
field.impl.entity(entity);
}
field.keys = field.keys || [field.key];
field.show = options.show;
function isModified() {
var original = context.graph().base().entities[entity.id];
return _.some(field.keys, function(key) {
return original ? tags[key] !== original.tags[key] : tags[key];
});
}
function isPresent() {
return _.some(field.keys, function(key) {
return tags[key];
});
}
function revert(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
var original = context.graph().base().entities[entity.id],
t = {};
d.keys.forEach(function(key) {
t[key] = original ? original.tags[key] : undefined;
});
dispatch.call('change', d, t);
}
function remove(d) {
d3.event.stopPropagation();
d3.event.preventDefault();
var t = {};
d.keys.forEach(function(key) {
t[key] = undefined;
});
dispatch.call('change', d, t);
}
field.render = function(selection) {
var container = selection.selectAll('.form-field')
.data([field]);
// Enter
var enter = container.enter()
.append('div')
.attr('class', function(d) { return 'form-field form-field-' + d.id; })
.classed('nowrap', !options.wrap);
if (options.wrap) {
var label = enter
.append('label')
.attr('class', 'form-label')
.attr('for', function(d) { return 'preset-input-' + d.id; })
.text(function(d) { return d.label(); });
var wrap = label
.append('div')
.attr('class', 'form-label-button-wrap');
wrap
.append('button')
.attr('class', 'remove-icon')
.attr('tabindex', -1)
.call(svgIcon('#operation-delete'));
wrap
.append('button')
.attr('class', 'modified-icon')
.attr('tabindex', -1)
.call(
(textDirection === 'rtl') ? svgIcon('#icon-redo') : svgIcon('#icon-undo')
);
}
// Update
container = container
.merge(enter);
container.selectAll('.form-label-button-wrap .remove-icon')
.on('click', remove);
container.selectAll('.form-label-button-wrap .modified-icon')
.on('click', revert);
container
.classed('modified', isModified())
.classed('present', isPresent())
.each(function(d) {
if (options.wrap) {
var referenceKey = d.key;
if (d.type === 'multiCombo') { // lookup key without the trailing ':'
referenceKey = referenceKey.replace(/:$/, '');
}
var reference = uiTagReference(d.reference || { key: referenceKey }, context);
if (state === 'hover') {
reference.showing(false);
}
}
d3.select(this)
.call(d.impl);
if (options.wrap) {
d3.select(this)
.call(reference.body)
.select('.form-label-button-wrap')
.call(reference.button);
}
d.impl.tags(tags);
});
};
field.state = function(_) {
if (!arguments.length) return state;
state = _;
return field;
};
field.tags = function(_) {
if (!arguments.length) return tags;
tags = _;
return field;
};
field.isShown = function() {
return field.show || _.some(field.keys, function(key) { return !!tags[key]; });
};
field.focus = function() {
field.impl.focus();
};
return utilRebind(field, dispatch, 'on');
}
+9 -7
View File
@@ -1,7 +1,7 @@
import * as d3 from 'd3';
import { t } from '../../util/locale';
import { dataPhoneFormats } from '../../../data/index';
import { services } from '../../services/index';
import { t, textDirection } from '../../util/locale';
import { dataPhoneFormats } from '../../../data';
import { services } from '../../services';
import {
utilGetSetValue,
utilNoAuto,
@@ -52,6 +52,8 @@ export function uiFieldText(field, context) {
});
} else if (field.type === 'number') {
var rtl = (textDirection === 'rtl');
input.attr('type', 'text');
var spinControl = selection.selectAll('.spin-control')
@@ -63,14 +65,14 @@ export function uiFieldText(field, context) {
enter
.append('button')
.datum(-1)
.attr('class', 'decrement')
.datum(rtl ? 1 : -1)
.attr('class', rtl ? 'increment' : 'decrement')
.attr('tabindex', -1);
enter
.append('button')
.datum(1)
.attr('class', 'increment')
.datum(rtl ? -1 : 1)
.attr('class', rtl ? 'decrement' : 'increment')
.attr('tabindex', -1);
spinControl = spinControl
+66 -133
View File
@@ -1,13 +1,7 @@
import * as d3 from 'd3';
import { t } from '../../util/locale';
import { d3combobox } from '../../lib/d3.combobox.js';
import { services } from '../../services/index';
import {
utilGetSetValue,
utilNoAuto,
utilRebind
} from '../../util';
import { uiField } from '../field';
import { utilRebind } from '../../util';
export { uiFieldRadio as uiFieldStructureRadio };
@@ -15,13 +9,12 @@ export { uiFieldRadio as uiFieldStructureRadio };
export function uiFieldRadio(field, context) {
var dispatch = d3.dispatch('change'),
taginfo = services.taginfo,
placeholder = d3.select(null),
wrap = d3.select(null),
labels = d3.select(null),
radios = d3.select(null),
typeInput = d3.select(null),
layerInput = d3.select(null),
typeField,
layerField,
oldType = {},
entity;
@@ -32,32 +25,6 @@ export function uiFieldRadio(field, context) {
return !node.empty() && node.datum();
}
// returns the tag value for a display value
function tagValue(dispVal) {
dispVal = snake(clean(dispVal || ''));
return dispVal.toLowerCase() || 'yes';
}
// returns the display value for a tag value
function displayValue(tagVal) {
tagVal = tagVal || '';
return tagVal.toLowerCase() === 'yes' ? '' : unsnake(tagVal);
}
function snake(s) {
return s.replace(/\s+/g, '_');
}
function unsnake(s) {
return s.replace(/_+/g, ' ');
}
function clean(s) {
return s.split(';')
.map(function(s) { return s.trim(); })
.join(';');
}
function radio(selection) {
selection.classed('preset-radio', true);
@@ -101,11 +68,16 @@ export function uiFieldRadio(field, context) {
radios = labels.selectAll('input')
.on('change', changeRadio);
}
function structureExtras(selection) {
var selected = selectedKey();
function structureExtras(selection, tags) {
var selected = selectedKey(),
type = context.presets().field(selected),
layer = context.presets().field('layer'),
showLayer = (selected === 'bridge' || selected === 'tunnel');
var extrasWrap = selection.selectAll('.structure-extras-wrap')
.data(selected ? [0] : []);
@@ -127,55 +99,67 @@ export function uiFieldRadio(field, context) {
// Type
var typeItem = list.selectAll('.structure-type-item')
.data([0]);
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()
.append('li')
.insert('li', ':first-child')
.attr('class', 'cf structure-type-item');
typeEnter
.append('span')
.attr('class', 'col6 label structure-label-type')
.attr('for', 'structure-input-type')
.attr('for', 'preset-input-' + selected)
.text(t('inspector.radio.structure.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.radio.structure.default'))
.call(utilNoAuto);
.attr('class', 'col6 structure-input-type-wrap');
// Update
typeItem = typeItem
.merge(typeEnter);
typeInput = typeItem.selectAll('.structure-input-type');
if (taginfo) {
typeInput
.call(d3combobox()
.container(context.container())
.fetcher(typeFetcher)
);
if (typeField) {
typeItem.selectAll('.structure-input-type-wrap')
.call(typeField.render);
}
typeInput
.on('change', changeType)
.on('blur', changeType);
// Layer
var showLayer = (selected === 'bridge' || selected === 'tunnel');
if (layer && showLayer) {
if (!layerField) {
layerField = uiField(context, layer, entity, { wrap: false })
.on('change', changeLayer);
}
layerField.tags(tags);
} else {
layerField = null;
}
var layerItem = list.selectAll('.structure-layer-item')
.data(showLayer ? [0] : []);
.data(layerField ? [layerField] : []);
// Exit
layerItem.exit()
.remove();
// Enter
var layerEnter = layerItem.enter()
.append('li')
.attr('class', 'cf structure-layer-item');
@@ -183,87 +167,39 @@ export function uiFieldRadio(field, context) {
layerEnter
.append('span')
.attr('class', 'col6 label structure-label-layer')
.attr('for', 'structure-input-layer')
.attr('for', 'preset-input-layer')
.text(t('inspector.radio.structure.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', 'decrement')
.attr('tabindex', -1);
spin
.append('button')
.datum(1)
.attr('class', 'increment')
.attr('tabindex', -1);
.attr('class', 'col6 structure-input-layer-wrap');
// Update
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();
});
if (layerField) {
layerItem.selectAll('.structure-input-layer-wrap')
.call(layerField.render);
}
}
function typeFetcher(q, callback) {
taginfo.values({
debounce: true,
key: selectedKey(),
query: q
}, function(err, data) {
if (err) return;
var comboData = data.map(function(d) {
return {
key: d.value,
value: unsnake(d.value),
title: d.title
};
});
if (callback) callback(comboData);
});
}
function changeType() {
var key = selectedKey(),
t = {};
function changeType(t, onInput) {
var key = selectedKey();
if (!key) return;
var val = tagValue(utilGetSetValue(typeInput));
t[key] = val;
var val = t[key];
if (val !== 'no') oldType[key] = val;
dispatch.call('change', this, t);
dispatch.call('change', this, t, onInput);
}
function changeLayer() {
// note: don't use utilGetSetValue here because we want 0 to be falsy.
var t = { layer: layerInput.node().value || undefined };
dispatch.call('change', this, t);
function changeLayer(t, onInput) {
if (t.layer === '0') {
t.layer = undefined;
}
dispatch.call('change', this, t, onInput);
}
@@ -314,19 +250,16 @@ export function uiFieldRadio(field, context) {
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 = oldType[selection.datum()] = tags[selection.datum()];
oldType[selection.datum()] = tags[selection.datum()];
}
if (field.type === 'structureRadio') {
wrap.call(structureExtras);
utilGetSetValue(typeInput, displayValue(typeVal) || '');
utilGetSetValue(layerInput, tags.layer || '');
wrap.call(structureExtras, tags);
}
};
+2 -1
View File
@@ -13,6 +13,7 @@ export { uiEditMenu } from './edit_menu';
export { uiEntityEditor } from './entity_editor';
export { uiFeatureInfo } from './feature_info';
export { uiFeatureList } from './feature_list';
export { uiField } from './field';
export { uiFlash } from './flash';
export { uiFullScreen } from './full_screen';
export { uiGeolocate } from './geolocate';
@@ -26,7 +27,7 @@ export { uiMapInMap } from './map_in_map';
export { uiModal } from './modal';
export { uiModes } from './modes';
export { uiNotice } from './notice';
export { uiPreset } from './preset';
export { uiPresetEditor } from './preset_editor';
export { uiPresetIcon } from './preset_icon';
export { uiPresetList } from './preset_list';
export { uiRadialMenu } from './radial_menu';
+1 -1
View File
@@ -326,7 +326,7 @@ export function uiIntroNavigation(context, reveal) {
var onClick = function() { continueTo(closeTownHall); };
reveal('.inspector-body .inspector-preset',
reveal('.inspector-body .preset-editor',
t('intro.navigation.fields_townhall'),
{ buttonText: t('intro.ok'), buttonCallback: onClick }
);
-327
View File
@@ -1,327 +0,0 @@
import * as d3 from 'd3';
import _ from 'lodash';
import { d3combobox } from '../lib/d3.combobox.js';
import { t, textDirection } from '../util/locale';
import { modeBrowse } from '../modes/index';
import { svgIcon } from '../svg/index';
import { uiDisclosure } from './disclosure';
import { uiFields } from './fields/index';
import { uiTagReference } from './tag_reference';
import {
utilGetSetValue,
utilNoAuto,
utilRebind
} from '../util';
export function uiPreset(context) {
var dispatch = d3.dispatch('change'),
expandedPreference = (context.storage('preset_fields.expanded') !== 'false'),
state,
fieldsArr,
preset,
tags,
id;
// Field Constructor
function UIField(field, entity, show) {
field = _.clone(field);
field.input = uiFields[field.type](field, context)
.on('change', function(t, onInput) {
dispatch.call('change', field, t, onInput);
});
if (field.input.entity) field.input.entity(entity);
field.keys = field.keys || [field.key];
field.show = show;
field.shown = function() {
return field.show || _.some(field.keys, function(key) { return !!tags[key]; });
};
field.modified = function() {
var original = context.graph().base().entities[entity.id];
return _.some(field.keys, function(key) {
return original ? tags[key] !== original.tags[key] : tags[key];
});
};
field.revert = function() {
var original = context.graph().base().entities[entity.id],
t = {};
field.keys.forEach(function(key) {
t[key] = original ? original.tags[key] : undefined;
});
return t;
};
field.present = function() {
return _.some(field.keys, function(key) {
return tags[key];
});
};
field.remove = function() {
var t = {};
field.keys.forEach(function(key) {
t[key] = undefined;
});
return t;
};
return field;
}
function fieldKey(field) {
return field.id;
}
function presets(selection) {
selection.call(uiDisclosure()
.title(t('inspector.all_fields'))
.expanded(expandedPreference)
.on('toggled', toggled)
.content(content)
);
function toggled(expanded) {
expandedPreference = expanded;
context.storage('preset_fields.expanded', expanded);
}
}
function content(selection) {
if (!fieldsArr) {
var entity = context.entity(id),
geometry = context.geometry(id),
presets = context.presets();
fieldsArr = [];
preset.fields.forEach(function(field) {
if (field.matchGeometry(geometry)) {
fieldsArr.push(UIField(field, entity, true));
}
});
if (entity.isHighwayIntersection(context.graph()) && presets.field('restrictions')) {
fieldsArr.push(UIField(presets.field('restrictions'), entity, true));
}
presets.universal().forEach(function(field) {
if (preset.fields.indexOf(field) < 0) {
fieldsArr.push(UIField(field, entity));
}
});
}
var shown = fieldsArr.filter(function(field) { return field.shown(); }),
notShown = fieldsArr.filter(function(field) { return !field.shown(); });
var form = selection.selectAll('.preset-form')
.data([0]);
form = form.enter()
.append('div')
.attr('class', 'preset-form inspector-inner fillL3')
.merge(form);
var fields = form.selectAll('.form-field')
.data(shown, fieldKey);
fields.exit()
.remove();
// Enter
var enter = fields.enter()
.append('div')
.attr('class', function(field) {
return 'form-field form-field-' + field.id;
});
var label = enter
.append('label')
.attr('class', 'form-label')
.attr('for', function(field) { return 'preset-input-' + field.id; })
.text(function(field) { return field.label(); });
var wrap = label
.append('div')
.attr('class', 'form-label-button-wrap');
wrap.append('button')
.attr('class', 'remove-icon')
.attr('tabindex', -1)
.call(svgIcon('#operation-delete'));
wrap.append('button')
.attr('class', 'modified-icon')
.attr('tabindex', -1)
.call(
(textDirection === 'rtl') ? svgIcon('#icon-redo') : svgIcon('#icon-undo')
);
// Update
fields = fields
.merge(enter);
fields.selectAll('.form-label-button-wrap .remove-icon')
.on('click', remove);
fields.selectAll('.modified-icon')
.on('click', revert);
fields
.order()
.classed('modified', function(field) { return field.modified(); })
.classed('present', function(field) { return field.present(); })
.each(function(field) {
var referenceKey = field.key;
if (field.type === 'multiCombo') { // lookup key without the trailing ':'
referenceKey = referenceKey.replace(/:$/, '');
}
var reference = uiTagReference(field.reference || { key: referenceKey }, context);
if (state === 'hover') {
reference.showing(false);
}
d3.select(this)
.call(field.input)
.selectAll('input')
.on('keydown', function() {
// if user presses enter, and combobox is not active, accept edits..
if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
context.enter(modeBrowse(context));
}
});
d3.select(this)
.call(reference.body)
.select('.form-label-button-wrap')
.call(reference.button);
field.input.tags(tags);
});
notShown = notShown.map(function(field) {
return {
title: field.label(),
value: field.label(),
field: field
};
});
var more = selection.selectAll('.more-fields')
.data((notShown.length > 0) ? [0] : []);
more.exit()
.remove();
more = more.enter()
.append('div')
.attr('class', 'more-fields')
.append('label')
.text(t('inspector.add_fields'))
.merge(more);
var input = more.selectAll('.value')
.data([0]);
input.exit()
.remove();
input = input.enter()
.append('input')
.attr('class', 'value')
.attr('type', 'text')
.call(utilNoAuto)
.merge(input);
input
.call(utilGetSetValue, '')
.attr('placeholder', function() {
var placeholder = [];
for (var field in notShown) {
placeholder.push(notShown[field].title);
}
return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
})
.call(d3combobox()
.container(context.container())
.data(notShown)
.minItems(1)
.on('accept', show)
);
function show(field) {
field = field.field;
field.show = true;
content(selection);
field.input.focus();
}
function revert(field) {
d3.event.stopPropagation();
d3.event.preventDefault();
dispatch.call('change', field, field.revert());
}
function remove(field) {
d3.event.stopPropagation();
d3.event.preventDefault();
dispatch.call('change', field, field.remove());
}
}
presets.preset = function(_) {
if (!arguments.length) return preset;
if (preset && preset.id === _.id) return presets;
preset = _;
fieldsArr = null;
return presets;
};
presets.state = function(_) {
if (!arguments.length) return state;
state = _;
return presets;
};
presets.tags = function(_) {
if (!arguments.length) return tags;
tags = _;
// Don't reset fieldsArr here.
return presets;
};
presets.entityID = function(_) {
if (!arguments.length) return id;
if (id === _) return presets;
id = _;
fieldsArr = null;
return presets;
};
return utilRebind(presets, dispatch, 'on');
}
+220
View File
@@ -0,0 +1,220 @@
import * as d3 from 'd3';
import { d3combobox } from '../lib/d3.combobox.js';
import { t } from '../util/locale';
import { modeBrowse } from '../modes';
import { uiDisclosure } from './disclosure';
import { uiField } from './field';
import {
utilGetSetValue,
utilNoAuto,
utilRebind
} from '../util';
export function uiPresetEditor(context) {
var dispatch = d3.dispatch('change'),
expandedPreference = (context.storage('preset_fields.expanded') !== 'false'),
state,
fieldsArr,
preset,
tags,
entityId;
function presetEditor(selection) {
selection.call(uiDisclosure()
.title(t('inspector.all_fields'))
.expanded(expandedPreference)
.on('toggled', toggled)
.content(render)
);
function toggled(expanded) {
expandedPreference = expanded;
context.storage('preset_fields.expanded', expanded);
}
}
function render(selection) {
if (!fieldsArr) {
var entity = context.entity(entityId),
geometry = context.geometry(entityId),
presets = context.presets();
fieldsArr = [];
preset.fields.forEach(function(field) {
if (field.matchGeometry(geometry)) {
fieldsArr.push(
uiField(context, field, entity)
);
}
});
if (entity.isHighwayIntersection(context.graph()) && presets.field('restrictions')) {
fieldsArr.push(
uiField(context, presets.field('restrictions'), entity)
);
}
presets.universal().forEach(function(field) {
if (preset.fields.indexOf(field) === -1) {
fieldsArr.push(
uiField(context, field, entity, { show: false })
);
}
});
fieldsArr.forEach(function(field) {
field
.on('change', function(t, onInput) {
dispatch.call('change', field, t, onInput);
});
});
}
fieldsArr.forEach(function(field) {
field
.state(state)
.tags(tags);
});
var shown = fieldsArr.filter(function(field) { return field.isShown(); }),
notShown = fieldsArr.filter(function(field) { return !field.isShown(); });
var form = selection.selectAll('.preset-form')
.data([0]);
form = form.enter()
.append('div')
.attr('class', 'preset-form inspector-inner fillL3')
.merge(form);
var fields = form.selectAll('.wrap-form-field')
.data(shown, function(d) { return d.id; });
fields.exit()
.remove();
// Enter
var enter = fields.enter()
.append('div')
.attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.id; });
// Update
fields = fields
.merge(enter);
fields
.order()
.each(function(d) {
d3.select(this)
.call(d.render)
.selectAll('input')
.on('keydown', function() {
// if user presses enter, and combobox is not active, accept edits..
if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
context.enter(modeBrowse(context));
}
});
});
notShown = notShown.map(function(field) {
return {
title: field.label(),
value: field.label(),
field: field
};
});
var more = selection.selectAll('.more-fields')
.data((notShown.length > 0) ? [0] : []);
more.exit()
.remove();
more = more.enter()
.append('div')
.attr('class', 'more-fields')
.append('label')
.text(t('inspector.add_fields'))
.merge(more);
var input = more.selectAll('.value')
.data([0]);
input.exit()
.remove();
input = input.enter()
.append('input')
.attr('class', 'value')
.attr('type', 'text')
.call(utilNoAuto)
.merge(input);
input
.call(utilGetSetValue, '')
.attr('placeholder', function() {
var placeholder = [];
for (var field in notShown) {
placeholder.push(notShown[field].title);
}
return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
})
.call(d3combobox()
.container(context.container())
.data(notShown)
.minItems(1)
.on('accept', function (d) {
var field = d.field;
field.show = true;
render(selection);
field.focus();
})
);
}
presetEditor.preset = function(_) {
if (!arguments.length) return preset;
if (preset && preset.id === _.id) return presetEditor;
preset = _;
fieldsArr = null;
return presetEditor;
};
presetEditor.state = function(_) {
if (!arguments.length) return state;
state = _;
return presetEditor;
};
presetEditor.tags = function(_) {
if (!arguments.length) return tags;
tags = _;
// Don't reset fieldsArr here.
return presetEditor;
};
presetEditor.entityID = function(_) {
if (!arguments.length) return entityId;
if (entityId === _) return presetEditor;
entityId = _;
fieldsArr = null;
return presetEditor;
};
return utilRebind(presetEditor, dispatch, 'on');
}