Files
iD/modules/ui/preset.js

330 lines
9.2 KiB
JavaScript

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'),
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.id === 'name' || 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(context.storage('preset_fields.expanded') !== 'false')
.on('toggled', toggled)
.content(content)
);
function toggled(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 = [];
if (presets.field('name')) {
fieldsArr.push(UIField(presets.field('name'), entity));
}
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');
}