mirror of
https://github.com/FoggedLens/iD.git
synced 2026-04-29 15:16:07 +02:00
Merge pull request #7262 from openstreetmap/multiselect-tag-editing
Raw tag editor for multiple select features (2.x)
This commit is contained in:
+1
-7
@@ -1245,18 +1245,12 @@ a.hide-toggle {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-end;
|
||||
padding: 0 20px;
|
||||
padding: 5px 0 0 0;
|
||||
}
|
||||
.quick-link {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.data-editor .quick-links,
|
||||
.error-editor .quick-links,
|
||||
.note-editor .quick-links {
|
||||
padding: 5px 0 0 0;
|
||||
}
|
||||
|
||||
|
||||
/* Entity/Preset Editor
|
||||
------------------------------------------------------- */
|
||||
|
||||
+5
-1
@@ -8,6 +8,8 @@ en:
|
||||
copy: copy
|
||||
view_on: view on {domain}
|
||||
favorite: favorite
|
||||
list: list
|
||||
text: text
|
||||
toolbar:
|
||||
inspect: Inspect
|
||||
undo_redo: Undo / Redo
|
||||
@@ -555,17 +557,19 @@ en:
|
||||
edit_reference: "edit/translate"
|
||||
wiki_reference: View documentation
|
||||
wiki_en_reference: View documentation in English
|
||||
key_value: "key=value"
|
||||
multiple_values: Multiple Values
|
||||
hidden_preset:
|
||||
manual: "{features} are hidden. Enable them in the Map Data pane."
|
||||
zoom: "{features} are hidden. Zoom in to enable them."
|
||||
back_tooltip: Change feature
|
||||
remove: Remove
|
||||
search: Search
|
||||
multiselect: Selected features
|
||||
unknown: Unknown
|
||||
incomplete: <not downloaded>
|
||||
feature_list: Search features
|
||||
edit: Edit feature
|
||||
edit_features: Edit features
|
||||
check:
|
||||
"yes": "Yes"
|
||||
"no": "No"
|
||||
|
||||
Vendored
+6
-2
@@ -8,7 +8,9 @@
|
||||
"zoom_to": "zoom to",
|
||||
"copy": "copy",
|
||||
"view_on": "view on {domain}",
|
||||
"favorite": "favorite"
|
||||
"favorite": "favorite",
|
||||
"list": "list",
|
||||
"text": "text"
|
||||
},
|
||||
"toolbar": {
|
||||
"inspect": "Inspect",
|
||||
@@ -698,6 +700,8 @@
|
||||
"edit_reference": "edit/translate",
|
||||
"wiki_reference": "View documentation",
|
||||
"wiki_en_reference": "View documentation in English",
|
||||
"key_value": "key=value",
|
||||
"multiple_values": "Multiple Values",
|
||||
"hidden_preset": {
|
||||
"manual": "{features} are hidden. Enable them in the Map Data pane.",
|
||||
"zoom": "{features} are hidden. Zoom in to enable them."
|
||||
@@ -705,11 +709,11 @@
|
||||
"back_tooltip": "Change feature",
|
||||
"remove": "Remove",
|
||||
"search": "Search",
|
||||
"multiselect": "Selected features",
|
||||
"unknown": "Unknown",
|
||||
"incomplete": "<not downloaded>",
|
||||
"feature_list": "Search features",
|
||||
"edit": "Edit feature",
|
||||
"edit_features": "Edit features",
|
||||
"check": {
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
|
||||
@@ -19,7 +19,6 @@ import { modeDragNote } from './drag_note';
|
||||
import { osmNode, osmWay } from '../osm';
|
||||
import * as Operations from '../operations/index';
|
||||
import { uiEditMenu } from '../ui/edit_menu';
|
||||
import { uiSelectionList } from '../ui/selection_list';
|
||||
import { uiCmd } from '../ui/cmd';
|
||||
import {
|
||||
utilArrayIntersection, utilDeepMemberSelector, utilEntityOrDeepMemberSelector,
|
||||
@@ -307,7 +306,7 @@ export function modeSelect(context, selectedIDs) {
|
||||
.call(keybinding);
|
||||
|
||||
context.ui().sidebar
|
||||
.select(singular() ? singular().id : null, _newFeature);
|
||||
.select(selectedIDs, _newFeature);
|
||||
|
||||
context.history()
|
||||
.on('change.select', function() {
|
||||
@@ -332,11 +331,6 @@ export function modeSelect(context, selectedIDs) {
|
||||
|
||||
selectElements();
|
||||
|
||||
if (selectedIDs.length > 1) {
|
||||
var entities = uiSelectionList(context, selectedIDs);
|
||||
context.ui().sidebar.show(entities);
|
||||
}
|
||||
|
||||
if (_follow) {
|
||||
var extent = geoExtent();
|
||||
var graph = context.graph();
|
||||
|
||||
+301
-207
@@ -1,5 +1,5 @@
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import {event as d3_event, selectAll as d3_selectAll } from 'd3-selection';
|
||||
import { event as d3_event, selectAll as d3_selectAll, select as d3_select } from 'd3-selection';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
import { t, textDirection } from '../util/locale';
|
||||
@@ -15,6 +15,7 @@ import { uiRawTagEditor } from './raw_tag_editor';
|
||||
import { uiTagReference } from './tag_reference';
|
||||
import { uiPresetEditor } from './preset_editor';
|
||||
import { uiEntityIssues } from './entity_issues';
|
||||
import { uiSelectionList } from './selection_list';
|
||||
import { uiTooltipHtml } from './tooltipHtml';
|
||||
import { utilCleanTags, utilRebind } from '../util';
|
||||
|
||||
@@ -24,12 +25,13 @@ export function uiEntityEditor(context) {
|
||||
var _state = 'select';
|
||||
var _coalesceChanges = false;
|
||||
var _modified = false;
|
||||
var _scrolled = false;
|
||||
var _base;
|
||||
var _entityID;
|
||||
var _entityIDs;
|
||||
var _activePreset;
|
||||
var _tagReference;
|
||||
var _newFeature;
|
||||
|
||||
var selectionList = uiSelectionList(context);
|
||||
var entityIssues = uiEntityIssues(context);
|
||||
var quickLinks = uiQuickLinks();
|
||||
var presetEditor = uiPresetEditor(context).on('change', changeTags);
|
||||
@@ -38,8 +40,9 @@ export function uiEntityEditor(context) {
|
||||
var rawMembershipEditor = uiRawMembershipEditor(context);
|
||||
|
||||
function entityEditor(selection) {
|
||||
var entity = context.entity(_entityID);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
var entityID = singularEntityID();
|
||||
var entity = entityID && context.entity(entityID);
|
||||
var tags = entity && Object.assign({}, entity.tags); // shallow copy
|
||||
|
||||
// Header
|
||||
var header = selection.selectAll('.header')
|
||||
@@ -62,19 +65,21 @@ export function uiEntityEditor(context) {
|
||||
.call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
|
||||
|
||||
headerEnter
|
||||
.append('h3')
|
||||
.text(t('inspector.edit'));
|
||||
.append('h3');
|
||||
|
||||
// Update
|
||||
header = header
|
||||
.merge(headerEnter);
|
||||
|
||||
header.selectAll('h3')
|
||||
.text(entityID ? t('inspector.edit') : t('inspector.edit_features'));
|
||||
|
||||
header.selectAll('.preset-reset')
|
||||
.style('display', entityID ? null : 'none')
|
||||
.on('click', function() {
|
||||
dispatch.call('choose', this, _activePreset);
|
||||
});
|
||||
|
||||
|
||||
// Body
|
||||
var body = selection.selectAll('.inspector-body')
|
||||
.data([0]);
|
||||
@@ -82,159 +87,215 @@ export function uiEntityEditor(context) {
|
||||
// Enter
|
||||
var bodyEnter = body.enter()
|
||||
.append('div')
|
||||
.attr('class', 'inspector-body')
|
||||
.on('scroll.entity-editor', function() { _scrolled = true; });
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'preset-list-item inspector-inner')
|
||||
.append('div')
|
||||
.attr('class', 'preset-list-button-wrap')
|
||||
.append('button')
|
||||
.attr('class', 'preset-list-button preset-reset')
|
||||
.call(tooltip().title(t('inspector.back_tooltip')).placement('bottom'))
|
||||
.append('div')
|
||||
.attr('class', 'label')
|
||||
.append('div')
|
||||
.attr('class', 'label-inner');
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'preset-quick-links');
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'entity-issues');
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'preset-editor');
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'raw-tag-editor inspector-inner');
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'raw-member-editor inspector-inner');
|
||||
|
||||
bodyEnter
|
||||
.append('div')
|
||||
.attr('class', 'raw-membership-editor inspector-inner');
|
||||
|
||||
bodyEnter
|
||||
.append('input')
|
||||
.attr('type', 'text')
|
||||
.attr('class', 'key-trap');
|
||||
|
||||
.attr('class', 'entity-editor inspector-body sep-top');
|
||||
|
||||
// Update
|
||||
body = body
|
||||
.merge(bodyEnter);
|
||||
|
||||
// update header
|
||||
if (_tagReference) {
|
||||
body.selectAll('.preset-list-button-wrap')
|
||||
.call(_tagReference.button);
|
||||
|
||||
body.selectAll('.preset-list-item')
|
||||
.call(_tagReference.body);
|
||||
}
|
||||
|
||||
body.selectAll('.preset-reset')
|
||||
.on('click', function() {
|
||||
dispatch.call('choose', this, _activePreset);
|
||||
});
|
||||
|
||||
body.select('.preset-list-item button')
|
||||
.call(uiPresetIcon(context)
|
||||
.geometry(context.geometry(_entityID))
|
||||
.preset(_activePreset)
|
||||
);
|
||||
|
||||
// NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
|
||||
var label = body.select('.label-inner');
|
||||
var nameparts = label.selectAll('.namepart')
|
||||
.data(_activePreset.name().split(' – '), function(d) { return d; });
|
||||
|
||||
nameparts.exit()
|
||||
.remove();
|
||||
|
||||
nameparts
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('class', 'namepart')
|
||||
.text(function(d) { return d; });
|
||||
|
||||
// update quick links
|
||||
var choices = [{
|
||||
id: 'zoom_to',
|
||||
label: 'inspector.zoom_to.title',
|
||||
tooltip: function() {
|
||||
return uiTooltipHtml(t('inspector.zoom_to.tooltip_feature'), t('inspector.zoom_to.key'));
|
||||
},
|
||||
click: function zoomTo() {
|
||||
context.mode().zoomToSelected();
|
||||
}
|
||||
}];
|
||||
|
||||
body.select('.preset-quick-links')
|
||||
.call(quickLinks.choices(choices));
|
||||
|
||||
|
||||
// update editor sections
|
||||
body.select('.entity-issues')
|
||||
.call(entityIssues
|
||||
.entityID(_entityID)
|
||||
);
|
||||
|
||||
body.select('.preset-editor')
|
||||
.call(presetEditor
|
||||
.preset(_activePreset)
|
||||
.entityID(_entityID)
|
||||
.tags(tags)
|
||||
.state(_state)
|
||||
);
|
||||
|
||||
body.select('.raw-tag-editor')
|
||||
.call(rawTagEditor
|
||||
.preset(_activePreset)
|
||||
.entityID(_entityID)
|
||||
.tags(tags)
|
||||
.state(_state)
|
||||
);
|
||||
|
||||
if (entity.type === 'relation') {
|
||||
body.select('.raw-member-editor')
|
||||
.style('display', 'block')
|
||||
.call(rawMemberEditor
|
||||
.entityID(_entityID)
|
||||
);
|
||||
} else {
|
||||
body.select('.raw-member-editor')
|
||||
.style('display', 'none');
|
||||
}
|
||||
|
||||
body.select('.raw-membership-editor')
|
||||
.call(rawMembershipEditor
|
||||
.entityID(_entityID)
|
||||
);
|
||||
|
||||
body.select('.key-trap')
|
||||
.on('keydown.key-trap', function() {
|
||||
// On tabbing, send focus back to the first field on the inspector-body
|
||||
// (probably the `name` field) #4159
|
||||
if (d3_event.keyCode === 9 && !d3_event.shiftKey) {
|
||||
d3_event.preventDefault();
|
||||
body.select('input').node().focus();
|
||||
var sectionInfos = [
|
||||
{
|
||||
klass: 'selection-list',
|
||||
shouldHave: _entityIDs.length > 1,
|
||||
update: function(section) {
|
||||
section
|
||||
.call(selectionList
|
||||
.selectedIDs(_entityIDs)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
klass: 'preset-list-item inspector-inner',
|
||||
shouldHave: entityID,
|
||||
create: function(sectionEnter) {
|
||||
|
||||
var presetButtonWrap = sectionEnter
|
||||
.append('div')
|
||||
.attr('class', 'preset-list-button-wrap');
|
||||
|
||||
var presetButton = presetButtonWrap.append('button')
|
||||
.attr('class', 'preset-list-button preset-reset')
|
||||
.call(tooltip().title(t('inspector.back_tooltip')).placement('bottom'));
|
||||
|
||||
presetButton
|
||||
.append('div')
|
||||
.attr('class', 'label')
|
||||
.append('div')
|
||||
.attr('class', 'label-inner');
|
||||
|
||||
presetButtonWrap.append('div')
|
||||
.attr('class', 'accessory-buttons');
|
||||
|
||||
// update quick links
|
||||
var choices = [{
|
||||
id: 'zoom_to',
|
||||
label: 'inspector.zoom_to.title',
|
||||
tooltip: function() {
|
||||
return uiTooltipHtml(t('inspector.zoom_to.tooltip_feature'), t('inspector.zoom_to.key'));
|
||||
},
|
||||
click: function zoomTo() {
|
||||
context.mode().zoomToSelected();
|
||||
}
|
||||
}];
|
||||
|
||||
sectionEnter
|
||||
.append('div')
|
||||
.attr('class', 'preset-quick-links')
|
||||
.call(quickLinks.choices(choices));
|
||||
},
|
||||
update: function(section) {
|
||||
|
||||
// update header
|
||||
if (_tagReference) {
|
||||
section.selectAll('.preset-list-button-wrap .accessory-buttons')
|
||||
.call(_tagReference.button);
|
||||
|
||||
section.selectAll('.preset-list-item')
|
||||
.call(_tagReference.body);
|
||||
}
|
||||
|
||||
section.selectAll('.preset-reset')
|
||||
.on('click', function() {
|
||||
dispatch.call('choose', this, _activePreset);
|
||||
})
|
||||
.on('mousedown', function() {
|
||||
d3_event.preventDefault();
|
||||
d3_event.stopPropagation();
|
||||
})
|
||||
.on('mouseup', function() {
|
||||
d3_event.preventDefault();
|
||||
d3_event.stopPropagation();
|
||||
});
|
||||
|
||||
section.select('.preset-list-item button')
|
||||
.call(uiPresetIcon(context)
|
||||
.geometry(context.geometry(entityID))
|
||||
.preset(_activePreset)
|
||||
);
|
||||
|
||||
// NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
|
||||
var label = section.select('.label-inner');
|
||||
var nameparts = label.selectAll('.namepart')
|
||||
.data(_activePreset.name().split(' – '), function(d) { return d; });
|
||||
|
||||
nameparts.exit()
|
||||
.remove();
|
||||
|
||||
nameparts
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('class', 'namepart')
|
||||
.text(function(d) { return d; });
|
||||
|
||||
}
|
||||
}, {
|
||||
klass: 'entity-issues',
|
||||
shouldHave: entityID,
|
||||
update: function(section) {
|
||||
section
|
||||
.call(entityIssues
|
||||
.entityID(entityID)
|
||||
);
|
||||
}
|
||||
}, {
|
||||
klass: 'preset-editor',
|
||||
shouldHave: entityID,
|
||||
update: function(section) {
|
||||
section
|
||||
.call(presetEditor
|
||||
.preset(_activePreset)
|
||||
.entityID(entityID)
|
||||
.tags(tags)
|
||||
.state(_state)
|
||||
);
|
||||
}
|
||||
}, {
|
||||
klass: 'raw-tag-editor inspector-inner',
|
||||
shouldHave: true,
|
||||
update: function(section) {
|
||||
section
|
||||
.call(rawTagEditor
|
||||
.preset(_activePreset)
|
||||
.entityIDs(_entityIDs)
|
||||
.state(_state)
|
||||
);
|
||||
}
|
||||
}, {
|
||||
klass: 'raw-member-editor inspector-inner',
|
||||
shouldHave: entity && entity.type === 'relation',
|
||||
update: function(section) {
|
||||
section
|
||||
.call(rawMemberEditor
|
||||
.entityID(entityID)
|
||||
);
|
||||
}
|
||||
}, {
|
||||
klass: 'raw-membership-editor inspector-inner',
|
||||
shouldHave: entityID,
|
||||
update: function(section) {
|
||||
section
|
||||
.call(rawMembershipEditor
|
||||
.entityID(entityID)
|
||||
);
|
||||
}
|
||||
}, {
|
||||
klass: 'key-trap-wrap',
|
||||
shouldHave: true,
|
||||
create: function(sectionEnter) {
|
||||
sectionEnter
|
||||
.append('input')
|
||||
.attr('type', 'text')
|
||||
.attr('class', 'key-trap');
|
||||
},
|
||||
update: function(section) {
|
||||
section.select('key-trap')
|
||||
.on('keydown.key-trap', function() {
|
||||
// On tabbing, send focus back to the first field on the inspector-body
|
||||
// (probably the `name` field) #4159
|
||||
if (d3_event.keyCode === 9 && !d3_event.shiftKey) {
|
||||
d3_event.preventDefault();
|
||||
body.select('input').node().focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
sectionInfos = sectionInfos.filter(function(info) {
|
||||
return info.shouldHave;
|
||||
});
|
||||
|
||||
var sections = body.selectAll('.section')
|
||||
.data(sectionInfos, function(d) { return d.klass; });
|
||||
|
||||
sections.exit().remove();
|
||||
|
||||
var sectionsEnter = sections.enter()
|
||||
.append('div')
|
||||
.attr('class', function(d) {
|
||||
return 'section ' + d.klass;
|
||||
});
|
||||
|
||||
sectionsEnter.each(function(d) {
|
||||
if (d.create) {
|
||||
d.create(d3_select(this));
|
||||
}
|
||||
});
|
||||
|
||||
sections = sectionsEnter
|
||||
.merge(sections);
|
||||
|
||||
sections.each(function(d) {
|
||||
if (d.update) {
|
||||
d.update(d3_select(this));
|
||||
}
|
||||
});
|
||||
|
||||
context.history()
|
||||
.on('change.entity-editor', historyChanged);
|
||||
|
||||
|
||||
function historyChanged(difference) {
|
||||
if (selection.selectAll('.entity-editor').empty()) return;
|
||||
if (_state === 'hide') return;
|
||||
var significant = !difference ||
|
||||
difference.didChange.properties ||
|
||||
@@ -242,30 +303,12 @@ export function uiEntityEditor(context) {
|
||||
difference.didChange.deletion;
|
||||
if (!significant) return;
|
||||
|
||||
var entity = context.hasEntity(_entityID);
|
||||
_entityIDs = _entityIDs.filter(context.hasEntity);
|
||||
if (!_entityIDs.length) return;
|
||||
|
||||
loadActivePreset();
|
||||
|
||||
var graph = context.graph();
|
||||
if (!entity) return;
|
||||
|
||||
var match = context.presets().match(entity, graph);
|
||||
var activePreset = entityEditor.preset();
|
||||
var weakPreset = activePreset &&
|
||||
Object.keys(activePreset.addTags || {}).length === 0;
|
||||
|
||||
// A "weak" preset doesn't set any tags. (e.g. "Address")
|
||||
// Don't replace a weak preset with a fallback preset (e.g. "Point")
|
||||
if (!(weakPreset && match.isFallback())) {
|
||||
entityEditor.preset(match);
|
||||
|
||||
if (match.id !== activePreset.id) {
|
||||
// flash the button to indicate the preset changed
|
||||
selection
|
||||
.selectAll('button.preset-reset .label')
|
||||
.style('background-color', '#fff')
|
||||
.transition()
|
||||
.duration(500)
|
||||
.style('background-color', null);
|
||||
}
|
||||
}
|
||||
entityEditor.modified(_base !== graph);
|
||||
entityEditor(selection);
|
||||
}
|
||||
@@ -275,27 +318,45 @@ 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(_entityID);
|
||||
var annotation = t('operations.change_tags.annotation');
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
|
||||
for (var k in changed) {
|
||||
if (!k) continue;
|
||||
var v = changed[k];
|
||||
if (v !== undefined || tags.hasOwnProperty(k)) {
|
||||
tags[k] = v;
|
||||
var actions = [];
|
||||
for (var i in _entityIDs) {
|
||||
var entityID = _entityIDs[i];
|
||||
var entity = context.entity(entityID);
|
||||
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
|
||||
for (var k in changed) {
|
||||
if (!k) continue;
|
||||
var v = changed[k];
|
||||
if (v !== undefined || tags.hasOwnProperty(k)) {
|
||||
tags[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (!onInput) {
|
||||
tags = utilCleanTags(tags);
|
||||
}
|
||||
|
||||
if (!deepEqual(entity.tags, tags)) {
|
||||
actions.push(actionChangeTags(entityID, tags));
|
||||
}
|
||||
}
|
||||
|
||||
if (!onInput) {
|
||||
tags = utilCleanTags(tags);
|
||||
}
|
||||
if (actions.length) {
|
||||
var combinedAction = function(graph) {
|
||||
actions.forEach(function(action) {
|
||||
graph = action(graph);
|
||||
});
|
||||
return graph;
|
||||
};
|
||||
|
||||
var annotation = t('operations.change_tags.annotation');
|
||||
|
||||
if (!deepEqual(entity.tags, tags)) {
|
||||
if (_coalesceChanges) {
|
||||
context.overwrite(actionChangeTags(_entityID, tags), annotation);
|
||||
context.overwrite(combinedAction, annotation);
|
||||
} else {
|
||||
context.perform(actionChangeTags(_entityID, tags), annotation);
|
||||
context.perform(combinedAction, annotation);
|
||||
_coalesceChanges = !!onInput;
|
||||
}
|
||||
}
|
||||
@@ -310,8 +371,6 @@ export function uiEntityEditor(context) {
|
||||
entityEditor.modified = function(val) {
|
||||
if (!arguments.length) return _modified;
|
||||
_modified = val;
|
||||
d3_selectAll('button.preset-close use')
|
||||
.attr('xlink:href', (_modified ? '#iD-icon-apply' : '#iD-icon-close'));
|
||||
return entityEditor;
|
||||
};
|
||||
|
||||
@@ -323,39 +382,74 @@ export function uiEntityEditor(context) {
|
||||
};
|
||||
|
||||
|
||||
entityEditor.entityID = function(val) {
|
||||
if (!arguments.length) return _entityID;
|
||||
if (_entityID === val) return entityEditor; // exit early if no change
|
||||
entityEditor.entityIDs = function(val) {
|
||||
if (!arguments.length) return _entityIDs;
|
||||
if (_entityIDs === val) return entityEditor; // exit early if no change
|
||||
|
||||
_entityID = val;
|
||||
_entityIDs = val;
|
||||
_base = context.graph();
|
||||
_coalesceChanges = false;
|
||||
|
||||
// reset the scroll to the top of the inspector (warning: triggers reflow)
|
||||
if (_scrolled) {
|
||||
window.requestIdleCallback(function() {
|
||||
var body = d3_selectAll('.entity-editor-pane .inspector-body');
|
||||
if (!body.empty()) {
|
||||
_scrolled = false;
|
||||
body.node().scrollTop = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var presetMatch = context.presets().match(context.entity(_entityID), _base);
|
||||
loadActivePreset();
|
||||
|
||||
return entityEditor
|
||||
.preset(presetMatch)
|
||||
.modified(false);
|
||||
};
|
||||
|
||||
|
||||
entityEditor.newFeature = function(val) {
|
||||
if (!arguments.length) return _newFeature;
|
||||
_newFeature = val;
|
||||
return entityEditor;
|
||||
};
|
||||
|
||||
|
||||
function singularEntityID() {
|
||||
if (_entityIDs.length === 1) {
|
||||
return _entityIDs[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function loadActivePreset() {
|
||||
var entityID = singularEntityID();
|
||||
var entity = entityID && context.hasEntity(entityID);
|
||||
if (!entity) return;
|
||||
|
||||
var graph = context.graph();
|
||||
var match = context.presets().match(entity, graph);
|
||||
|
||||
// A "weak" preset doesn't set any tags. (e.g. "Address")
|
||||
var weakPreset = _activePreset &&
|
||||
Object.keys(_activePreset.addTags || {}).length === 0;
|
||||
|
||||
// Don't replace a weak preset with a fallback preset (e.g. "Point")
|
||||
if ((weakPreset && match.isFallback()) ||
|
||||
// don't reload for same preset
|
||||
match === _activePreset) return;
|
||||
|
||||
if (_activePreset && match.id !== _activePreset.id) {
|
||||
// flash the button to indicate the preset changed
|
||||
d3_selectAll('.entity-editor button.preset-reset .label')
|
||||
.style('background-color', '#fff')
|
||||
.transition()
|
||||
.duration(500)
|
||||
.style('background-color', null);
|
||||
}
|
||||
|
||||
entityEditor.preset(match);
|
||||
}
|
||||
|
||||
entityEditor.preset = function(val) {
|
||||
if (!arguments.length) return _activePreset;
|
||||
if (val !== _activePreset) {
|
||||
_activePreset = val;
|
||||
_tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context)
|
||||
.showing(false);
|
||||
var entityID = singularEntityID();
|
||||
if (entityID) {
|
||||
_tagReference = uiTagReference(_activePreset.reference(context.geometry(entityID)), context)
|
||||
.showing(false);
|
||||
}
|
||||
}
|
||||
return entityEditor;
|
||||
};
|
||||
|
||||
+43
-15
@@ -13,19 +13,19 @@ export function uiInspector(context) {
|
||||
presetPane = d3_select(null),
|
||||
editorPane = d3_select(null);
|
||||
var _state = 'select';
|
||||
var _entityID;
|
||||
var _entityIDs;
|
||||
var _newFeature = false;
|
||||
|
||||
|
||||
function inspector(selection, newFeature) {
|
||||
presetList
|
||||
.entityID(_entityID)
|
||||
.entityID(_entityIDs.length === 1 && _entityIDs[0])
|
||||
.autofocus(_newFeature)
|
||||
.on('choose', inspector.setPreset);
|
||||
|
||||
entityEditor
|
||||
.state(_state)
|
||||
.entityID(_entityID)
|
||||
.entityIDs(_entityIDs)
|
||||
.on('choose', inspector.showList);
|
||||
|
||||
wrap = selection.selectAll('.panewrap')
|
||||
@@ -47,16 +47,34 @@ export function uiInspector(context) {
|
||||
presetPane = wrap.selectAll('.preset-list-pane');
|
||||
editorPane = wrap.selectAll('.entity-editor-pane');
|
||||
|
||||
var entity = context.entity(_entityID);
|
||||
function shouldDefaultToPresetList() {
|
||||
// can only change preset on single selection
|
||||
if (_entityIDs.length !== 1) return false;
|
||||
|
||||
var hasNonGeometryTags = entity.hasNonGeometryTags();
|
||||
var isTaglessOrIntersectionVertex = entity.geometry(context.graph()) === 'vertex' &&
|
||||
(!hasNonGeometryTags && !entity.isHighwayIntersection(context.graph()));
|
||||
var issues = context.validator().getEntityIssues(_entityID);
|
||||
// start with the preset list if the feature is new and untagged or is an uninteresting vertex
|
||||
var showPresetList = (newFeature && !hasNonGeometryTags) || (isTaglessOrIntersectionVertex && !issues.length);
|
||||
var entityID = _entityIDs[0];
|
||||
var entity = context.hasEntity(entityID);
|
||||
if (!entity) return false;
|
||||
|
||||
if (showPresetList) {
|
||||
// default to inspector if there are already tags
|
||||
if (entity.hasNonGeometryTags()) return false;
|
||||
|
||||
// prompt to select preset if feature is new and untagged
|
||||
if (newFeature) return true;
|
||||
|
||||
// all existing features except vertices should default to inspector
|
||||
if (entity.geometry(context.graph()) !== 'vertex') return false;
|
||||
|
||||
// show vertex issues if there are any
|
||||
if (context.validator().getEntityIssues(entityID).length) return false;
|
||||
|
||||
// show turn retriction editor for junction vertices
|
||||
if (entity.isHighwayIntersection(context.graph())) return false;
|
||||
|
||||
// otherwise show preset list for uninteresting vertices
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shouldDefaultToPresetList()) {
|
||||
wrap.style('right', '-100%');
|
||||
presetPane.call(presetList);
|
||||
} else {
|
||||
@@ -74,11 +92,21 @@ export function uiInspector(context) {
|
||||
|
||||
footer
|
||||
.call(uiViewOnOSM(context)
|
||||
.what(context.hasEntity(_entityID))
|
||||
.what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
|
||||
);
|
||||
}
|
||||
|
||||
inspector.showList = function(preset) {
|
||||
|
||||
if (!preset) {
|
||||
if (_entityIDs.length !== 1) return;
|
||||
|
||||
var entity = context.hasEntity(_entityIDs[0]);
|
||||
if (!entity) return;
|
||||
|
||||
preset = context.presets().match(entity, context.graph());
|
||||
}
|
||||
|
||||
wrap.transition()
|
||||
.styleTween('right', function() { return d3_interpolate('0%', '-100%'); });
|
||||
|
||||
@@ -115,9 +143,9 @@ export function uiInspector(context) {
|
||||
};
|
||||
|
||||
|
||||
inspector.entityID = function(val) {
|
||||
if (!arguments.length) return _entityID;
|
||||
_entityID = val;
|
||||
inspector.entityIDs = function(val) {
|
||||
if (!arguments.length) return _entityIDs;
|
||||
_entityIDs = val;
|
||||
return inspector;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ export function uiPresetList(context) {
|
||||
|
||||
|
||||
function presetList(selection) {
|
||||
if (!_entityID) {
|
||||
//selection.html('');
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = context.entity(_entityID);
|
||||
var geometry = context.geometry(_entityID);
|
||||
|
||||
@@ -461,7 +466,9 @@ export function uiPresetList(context) {
|
||||
presetList.entityID = function(val) {
|
||||
if (!arguments.length) return _entityID;
|
||||
_entityID = val;
|
||||
presetList.preset(context.presets().match(context.entity(_entityID), context.graph()));
|
||||
if (_entityID) {
|
||||
presetList.preset(context.presets().match(context.entity(_entityID), context.graph()));
|
||||
}
|
||||
return presetList;
|
||||
};
|
||||
|
||||
|
||||
+123
-19
@@ -7,7 +7,8 @@ import { svgIcon } from '../svg/icon';
|
||||
import { uiCombobox } from './combobox';
|
||||
import { uiDisclosure } from './disclosure';
|
||||
import { uiTagReference } from './tag_reference';
|
||||
import { utilArrayDifference, utilGetSetValue, utilNoAuto, utilRebind, utilTagDiff } from '../util';
|
||||
import { utilArrayDifference, utilArrayIdentical } from '../util/array';
|
||||
import { utilGetSetValue, utilNoAuto, utilRebind, utilTagDiff } from '../util';
|
||||
|
||||
|
||||
export function uiRawTagEditor(context) {
|
||||
@@ -22,6 +23,7 @@ export function uiRawTagEditor(context) {
|
||||
var _readOnlyTags = [];
|
||||
// the keys in the order we want them to display
|
||||
var _orderedKeys = [];
|
||||
var _keyValues = null;
|
||||
var _showBlank = false;
|
||||
var _updatePreference = true;
|
||||
var _expanded = false;
|
||||
@@ -29,7 +31,7 @@ export function uiRawTagEditor(context) {
|
||||
var _state;
|
||||
var _preset;
|
||||
var _tags;
|
||||
var _entityID;
|
||||
var _entityIDs;
|
||||
|
||||
|
||||
function rawTagEditor(selection) {
|
||||
@@ -91,8 +93,11 @@ export function uiRawTagEditor(context) {
|
||||
var options = wrap.selectAll('.raw-tag-options')
|
||||
.data([0]);
|
||||
|
||||
options.exit()
|
||||
.remove();
|
||||
|
||||
var optionsEnter = options.enter()
|
||||
.append('div')
|
||||
.insert('div', ':first-child')
|
||||
.attr('class', 'raw-tag-options');
|
||||
|
||||
var optionEnter = optionsEnter.selectAll('.raw-tag-option')
|
||||
@@ -104,7 +109,7 @@ export function uiRawTagEditor(context) {
|
||||
.attr('class', function(d) {
|
||||
return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
|
||||
})
|
||||
.attr('title', function(d) { return d.id; })
|
||||
.attr('title', function(d) { return t('icons.' + d.id); })
|
||||
.on('click', function(d) {
|
||||
_tagView = d.id;
|
||||
context.storage('raw-tag-editor-view', d.id);
|
||||
@@ -134,6 +139,7 @@ export function uiRawTagEditor(context) {
|
||||
.append('textarea')
|
||||
.attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : ''))
|
||||
.call(utilNoAuto)
|
||||
.attr('placeholder', t('inspector.key_value'))
|
||||
.attr('spellcheck', 'false')
|
||||
.merge(textarea);
|
||||
|
||||
@@ -237,17 +243,23 @@ export function uiRawTagEditor(context) {
|
||||
var key = row.select('input.key'); // propagate bound data
|
||||
var value = row.select('input.value'); // propagate bound data
|
||||
|
||||
if (_entityID && taginfo && _state !== 'hover') {
|
||||
if (_entityIDs && taginfo && _state !== 'hover') {
|
||||
bindTypeahead(key, value);
|
||||
}
|
||||
|
||||
var isRelation = (_entityID && context.entity(_entityID).type === 'relation');
|
||||
var reference;
|
||||
|
||||
if (isRelation && d.key === 'type') {
|
||||
reference = uiTagReference({ rtype: d.value }, context);
|
||||
if (typeof d.value !== 'string') {
|
||||
reference = uiTagReference({ key: d.key }, context);
|
||||
} else {
|
||||
reference = uiTagReference({ key: d.key, value: d.value }, context);
|
||||
var isRelation = _entityIDs && _entityIDs.some(function(entityID) {
|
||||
return context.entity(entityID).type === 'relation';
|
||||
});
|
||||
if (isRelation && d.key === 'type') {
|
||||
reference = uiTagReference({ rtype: d.value }, context);
|
||||
} else {
|
||||
reference = uiTagReference({ key: d.key, value: d.value }, context);
|
||||
}
|
||||
}
|
||||
|
||||
if (_state === 'hover') {
|
||||
@@ -266,12 +278,19 @@ export function uiRawTagEditor(context) {
|
||||
.attr('title', function(d) { return d.key; })
|
||||
.call(utilGetSetValue, function(d) { return d.key; })
|
||||
.attr('readonly', function(d) {
|
||||
return isReadOnly(d) || null;
|
||||
return (isReadOnly(d) || (typeof d.value !== 'string')) || null;
|
||||
});
|
||||
|
||||
items.selectAll('input.value')
|
||||
.attr('title', function(d) { return d.value; })
|
||||
.call(utilGetSetValue, function(d) { return d.value; })
|
||||
.attr('title', function(d) {
|
||||
return typeof d.value === 'string' ? d.value : Array.from(_keyValues[d.key]).sort().join('; ');
|
||||
})
|
||||
.attr('placeholder', function(d) {
|
||||
return typeof d.value === 'string' ? null : t('inspector.multiple_values');
|
||||
})
|
||||
.call(utilGetSetValue, function(d) {
|
||||
return typeof d.value === 'string' ? d.value : '';
|
||||
})
|
||||
.attr('readonly', function(d) {
|
||||
return isReadOnly(d) || null;
|
||||
});
|
||||
@@ -323,7 +342,9 @@ export function uiRawTagEditor(context) {
|
||||
var str = rows
|
||||
.filter(function(row) { return row.key && row.key.trim() !== ''; })
|
||||
.map(function(row) {
|
||||
var val = row.value ? stringify(row.value) : '';
|
||||
var rawVal = row.value;
|
||||
if (rawVal === true) rawVal = '*';
|
||||
var val = rawVal ? stringify(rawVal) : '';
|
||||
return stringify(row.key) + '=' + val;
|
||||
})
|
||||
.join('\n');
|
||||
@@ -352,6 +373,9 @@ export function uiRawTagEditor(context) {
|
||||
tagDiff.forEach(function(change) {
|
||||
if (isReadOnly({ key: change.key })) return;
|
||||
|
||||
// skip unchanged multiselection placeholders
|
||||
if (change.newVal === '*' && change.oldVal === true) return;
|
||||
|
||||
if (change.type === '-') {
|
||||
_pendingChange[change.key] = undefined;
|
||||
} else if (change.type === '+') {
|
||||
@@ -359,6 +383,11 @@ export function uiRawTagEditor(context) {
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(_pendingChange).length === 0) {
|
||||
_pendingChange = null;
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleChange();
|
||||
}
|
||||
|
||||
@@ -376,7 +405,24 @@ export function uiRawTagEditor(context) {
|
||||
function bindTypeahead(key, value) {
|
||||
if (isReadOnly(key.datum())) return;
|
||||
|
||||
var geometry = context.geometry(_entityID);
|
||||
if (typeof value.datum().value !== 'string' && _keyValues) {
|
||||
value.call(uiCombobox(context, 'tag-value')
|
||||
.minItems(1)
|
||||
.fetcher(function(value, callback) {
|
||||
var keyString = utilGetSetValue(key);
|
||||
if (!_keyValues[keyString]) return;
|
||||
var data = Array.from(_keyValues[keyString]).map(function(tagValue) {
|
||||
return {
|
||||
value: tagValue,
|
||||
title: tagValue
|
||||
};
|
||||
});
|
||||
callback(data);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
var geometry = context.geometry(_entityIDs[0]);
|
||||
|
||||
key.call(uiCombobox(context, 'tag-key')
|
||||
.fetcher(function(value, callback) {
|
||||
@@ -432,6 +478,8 @@ export function uiRawTagEditor(context) {
|
||||
|
||||
|
||||
function keyChange(d) {
|
||||
if (d3_select(this).attr('readonly')) return;
|
||||
|
||||
var kOld = d.key;
|
||||
var kNew = this.value.trim();
|
||||
var row = this.parentNode.parentNode;
|
||||
@@ -487,6 +535,9 @@ export function uiRawTagEditor(context) {
|
||||
function valueChange(d) {
|
||||
if (isReadOnly(d)) return;
|
||||
|
||||
// exit if this is a multiselection and no value was entered
|
||||
if (typeof d.value !== 'string' && !this.value) return;
|
||||
|
||||
_pendingChange = _pendingChange || {};
|
||||
|
||||
// exit if we are currently about to delete this row anyway - #6366
|
||||
@@ -550,7 +601,7 @@ export function uiRawTagEditor(context) {
|
||||
rawTagEditor.preset = function(val) {
|
||||
if (!arguments.length) return _preset;
|
||||
_preset = val;
|
||||
if (_preset.isFallback()) {
|
||||
if (_preset && _preset.isFallback()) {
|
||||
_expanded = true;
|
||||
_updatePreference = false;
|
||||
} else {
|
||||
@@ -568,12 +619,65 @@ export function uiRawTagEditor(context) {
|
||||
};
|
||||
|
||||
|
||||
rawTagEditor.entityID = function(val) {
|
||||
if (!arguments.length) return _entityID;
|
||||
if (_entityID !== val) {
|
||||
rawTagEditor.entityIDs = function(val) {
|
||||
if (!arguments.length) return _entityIDs;
|
||||
if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
|
||||
_entityIDs = val;
|
||||
_orderedKeys = [];
|
||||
_entityID = val;
|
||||
}
|
||||
|
||||
var combinedTags = {};
|
||||
var sharedKeys = null;
|
||||
_keyValues = {};
|
||||
|
||||
_entityIDs.forEach(function(entityID) {
|
||||
var entity = context.entity(entityID);
|
||||
var entityTags = entity.tags;
|
||||
var entityKey;
|
||||
|
||||
if (sharedKeys === null) {
|
||||
sharedKeys = {};
|
||||
for (entityKey in entityTags) {
|
||||
sharedKeys[entityKey] = true;
|
||||
}
|
||||
} else {
|
||||
for (var sharedKey in sharedKeys) {
|
||||
if (!entityTags.hasOwnProperty(sharedKey)) {
|
||||
delete sharedKeys[sharedKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (entityKey in entityTags) {
|
||||
|
||||
var entityValue = entityTags[entityKey];
|
||||
|
||||
if (!_keyValues.hasOwnProperty(entityKey)) {
|
||||
_keyValues[entityKey] = new Set();
|
||||
}
|
||||
_keyValues[entityKey].add(entityValue);
|
||||
|
||||
if (combinedTags.hasOwnProperty(entityKey)) {
|
||||
var combinedValue = combinedTags[entityKey];
|
||||
if (combinedValue !== true &&
|
||||
combinedValue !== entityValue) {
|
||||
|
||||
combinedTags[entityKey] = true;
|
||||
}
|
||||
} else {
|
||||
combinedTags[entityKey] = entityValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (var key in combinedTags) {
|
||||
if (!sharedKeys.hasOwnProperty(key)) {
|
||||
// treat tags that aren't shared by all entities the same as if there are multiple values
|
||||
combinedTags[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
rawTagEditor.tags(combinedTags);
|
||||
return rawTagEditor;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { event as d3_event, select as d3_select } from 'd3-selection';
|
||||
|
||||
import { t } from '../util/locale';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { osmEntity } from '../osm';
|
||||
import { svgIcon } from '../svg/icon';
|
||||
import { utilDisplayName, utilHighlightEntities } from '../util';
|
||||
|
||||
|
||||
export function uiSelectionList(context, selectedIDs) {
|
||||
export function uiSelectionList(context) {
|
||||
|
||||
var _selectedIDs = [];
|
||||
|
||||
|
||||
function selectEntity(entity) {
|
||||
context.enter(modeSelect(context, [entity.id]));
|
||||
@@ -16,33 +18,25 @@ export function uiSelectionList(context, selectedIDs) {
|
||||
|
||||
function deselectEntity(entity) {
|
||||
d3_event.stopPropagation();
|
||||
|
||||
var selectedIDs = _selectedIDs.slice();
|
||||
var index = selectedIDs.indexOf(entity.id);
|
||||
if (index > -1) {
|
||||
selectedIDs.splice(index, 1);
|
||||
context.enter(modeSelect(context, selectedIDs));
|
||||
}
|
||||
context.enter(modeSelect(context, selectedIDs));
|
||||
}
|
||||
|
||||
|
||||
function selectionList(selection) {
|
||||
selection.classed('selection-list-pane', true);
|
||||
|
||||
var header = selection
|
||||
var list = selection.selectAll('.feature-list')
|
||||
.data([0]);
|
||||
|
||||
list = list.enter()
|
||||
.append('div')
|
||||
.attr('class', 'header fillL cf');
|
||||
|
||||
header
|
||||
.append('h3')
|
||||
.text(t('inspector.multiselect'));
|
||||
|
||||
var listWrap = selection
|
||||
.append('div')
|
||||
.attr('class', 'inspector-body');
|
||||
|
||||
var list = listWrap
|
||||
.append('div')
|
||||
.attr('class', 'feature-list cf');
|
||||
|
||||
.attr('class', 'feature-list')
|
||||
.merge(list);
|
||||
|
||||
context.history()
|
||||
.on('change.selectionList', function(difference) {
|
||||
@@ -51,11 +45,10 @@ export function uiSelectionList(context, selectedIDs) {
|
||||
|
||||
drawList();
|
||||
|
||||
|
||||
function drawList() {
|
||||
var entities = selectedIDs
|
||||
var entities = _selectedIDs
|
||||
.map(function(id) { return context.hasEntity(id); })
|
||||
.filter(function(entity) { return entity; });
|
||||
.filter(Boolean);
|
||||
|
||||
var items = list.selectAll('.feature-list-item')
|
||||
.data(entities, osmEntity.key);
|
||||
@@ -119,5 +112,13 @@ export function uiSelectionList(context, selectedIDs) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
selectionList.selectedIDs = function(val) {
|
||||
if (!arguments.length) return _selectedIDs;
|
||||
_selectedIDs = val;
|
||||
return selectionList;
|
||||
};
|
||||
|
||||
|
||||
return selectionList;
|
||||
}
|
||||
|
||||
+18
-18
@@ -8,7 +8,7 @@ import {
|
||||
event as d3_event,
|
||||
selectAll as d3_selectAll
|
||||
} from 'd3-selection';
|
||||
|
||||
import { utilArrayIdentical } from '../util/array';
|
||||
import { osmEntity, osmNote, qaError } from '../osm';
|
||||
import { services } from '../services';
|
||||
import { uiDataEditor } from './data_editor';
|
||||
@@ -167,10 +167,10 @@ export function uiSidebar(context) {
|
||||
.classed('inspector-hidden', false)
|
||||
.classed('inspector-hover', true);
|
||||
|
||||
if (inspector.entityID() !== datum.id || inspector.state() !== 'hover') {
|
||||
if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
|
||||
inspector
|
||||
.state('hover')
|
||||
.entityID(datum.id);
|
||||
.entityIDs([datum.id]);
|
||||
|
||||
inspectorWrap
|
||||
.call(inspector);
|
||||
@@ -206,17 +206,16 @@ export function uiSidebar(context) {
|
||||
};
|
||||
|
||||
|
||||
sidebar.select = function(id, newFeature) {
|
||||
sidebar.select = function(ids, newFeature) {
|
||||
sidebar.hide();
|
||||
|
||||
if (id) {
|
||||
var entity = context.entity(id);
|
||||
// uncollapse the sidebar
|
||||
if (selection.classed('collapsed')) {
|
||||
if (newFeature) {
|
||||
var extent = entity.extent(context.graph());
|
||||
sidebar.expand(sidebar.intersects(extent));
|
||||
}
|
||||
if (ids && ids.length) {
|
||||
|
||||
var entity = ids.length === 1 && context.entity(ids[0]);
|
||||
if (entity && newFeature && selection.classed('collapsed')) {
|
||||
// uncollapse the sidebar
|
||||
var extent = entity.extent(context.graph());
|
||||
sidebar.expand(sidebar.intersects(extent));
|
||||
}
|
||||
|
||||
featureListWrap
|
||||
@@ -226,20 +225,16 @@ export function uiSidebar(context) {
|
||||
.classed('inspector-hidden', false)
|
||||
.classed('inspector-hover', false);
|
||||
|
||||
if (inspector.entityID() !== id || inspector.state() !== 'select') {
|
||||
if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
|
||||
inspector
|
||||
.state('select')
|
||||
.entityID(id)
|
||||
.entityIDs(ids)
|
||||
.newFeature(newFeature);
|
||||
|
||||
inspectorWrap
|
||||
.call(inspector, newFeature);
|
||||
}
|
||||
|
||||
sidebar.showPresetList = function() {
|
||||
inspector.showList(context.presets().match(entity, context.graph()));
|
||||
};
|
||||
|
||||
} else {
|
||||
inspector
|
||||
.state('hide');
|
||||
@@ -247,6 +242,11 @@ export function uiSidebar(context) {
|
||||
};
|
||||
|
||||
|
||||
sidebar.showPresetList = function() {
|
||||
inspector.showList();
|
||||
};
|
||||
|
||||
|
||||
sidebar.show = function(component, element) {
|
||||
featureListWrap
|
||||
.classed('inspector-hidden', true);
|
||||
|
||||
+13
-1
@@ -1,4 +1,17 @@
|
||||
|
||||
// Returns true if a and b have the same elements at the same indices.
|
||||
export function utilArrayIdentical(a, b) {
|
||||
// an array is always identical to itself
|
||||
if (a === b) return true;
|
||||
|
||||
var i = a.length;
|
||||
if (i !== b.length) return false;
|
||||
while (i--) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// http://2ality.com/2015/01/es6-set-operations.html
|
||||
|
||||
// Difference (a \ b): create a set that contains those elements of set a that are not in set b.
|
||||
@@ -136,4 +149,3 @@ export function utilArrayUniqBy(a, key) {
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ export { utilArrayChunk } from './array';
|
||||
export { utilArrayDifference } from './array';
|
||||
export { utilArrayFlatten } from './array';
|
||||
export { utilArrayGroupBy } from './array';
|
||||
export { utilArrayIdentical } from './array';
|
||||
export { utilArrayIntersection } from './array';
|
||||
export { utilArrayUnion } from './array';
|
||||
export { utilArrayUniq } from './array';
|
||||
|
||||
@@ -3,7 +3,7 @@ describe('iD.uiRawTagEditor', function() {
|
||||
|
||||
function render(tags) {
|
||||
taglist = iD.uiRawTagEditor(context)
|
||||
.entityID(entity.id)
|
||||
.entityIDs([entity.id])
|
||||
.preset({isFallback: function() { return false; }})
|
||||
.tags(tags)
|
||||
.expanded(true);
|
||||
|
||||
Reference in New Issue
Block a user