diff --git a/css/20_map.css b/css/20_map.css index 2a6fa0f25..01962274b 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -294,6 +294,13 @@ g.point.tag-wikidata .icon { color: #666; } +/* Selected Members */ +g.vertex.selected-member .shadow, +g.point.selected-member .shadow, +path.shadow.selected-member { + stroke-opacity: 0.95; + stroke: #FFDE70; +} /* Highlighting */ g.point.highlighted .shadow, diff --git a/modules/modes/select.js b/modules/modes/select.js index 43aca6091..8c2e9459d 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -22,7 +22,7 @@ import { uiEditMenu } from '../ui/edit_menu'; import { uiSelectionList } from '../ui/selection_list'; import { uiCmd } from '../ui/cmd'; import { - utilArrayIntersection, utilEntityOrMemberSelector, + utilArrayIntersection, utilDeepMemberSelector, utilEntityOrDeepMemberSelector, utilEntitySelector, utilKeybinding } from '../util'; @@ -397,7 +397,6 @@ export function modeSelect(context, selectedIDs) { if (entity && context.geometry(entity.id) === 'relation') { _suppressMenu = true; - return; } surface.selectAll('.related') @@ -422,7 +421,7 @@ export function modeSelect(context, selectedIDs) { } else if (context.map().withinEditableZoom()) { var selection = context.surface() - .selectAll(utilEntityOrMemberSelector(selectedIDs, context.graph())); + .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())); if (selection.empty()) { // Return to browse mode if selected DOM elements have @@ -432,6 +431,9 @@ export function modeSelect(context, selectedIDs) { context.enter(modeBrowse(context)); } } else { + context.surface() + .selectAll(utilDeepMemberSelector(selectedIDs, context.graph())) + .classed('selected-member', true); selection .classed('selected', true); } @@ -591,6 +593,10 @@ export function modeSelect(context, selectedIDs) { surface .on('dblclick.select', null); + surface + .selectAll('.selected-member') + .classed('selected-member', false); + surface .selectAll('.selected') .classed('selected', false); diff --git a/modules/util/index.js b/modules/util/index.js index b7c57d20b..37e79cb65 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -9,6 +9,7 @@ export { utilArrayUniqBy } from './array'; export { utilAsyncMap } from './util'; export { utilCleanTags } from './clean_tags'; +export { utilDeepMemberSelector } from './util'; export { utilDetect } from './detect'; export { utilDisplayName } from './util'; export { utilDisplayNameForPath } from './util'; diff --git a/modules/util/util.js b/modules/util/util.js index e37a165cc..ceadbcb34 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -97,6 +97,32 @@ export function utilEntityAndDeepMemberIDs(ids, graph) { } } +// returns an selector to select entity ids for: +// - deep descendant entityIDs for any of those entities that are relations +export function utilDeepMemberSelector(ids, graph) { + var idsSet = new Set(ids); + var seen = new Set(); + var returners = new Set(); + ids.forEach(collectDeepDescendants); + return utilEntitySelector(Array.from(returners)); + + function collectDeepDescendants(id) { + if (seen.has(id)) return; + seen.add(id); + + if (!idsSet.has(id)) { + returners.add(id); + } + + var entity = graph.hasEntity(id); + if (!entity || entity.type !== 'relation') return; + + entity.members + .map(function(member) { return member.id; }) + .forEach(collectDeepDescendants); // recurse + } +} + // Adds or removes highlight styling for the specified entities export function utilHighlightEntities(ids, highlighted, context) {