From 81feb1cd990993cc432ef13dea5ec74805a6ab75 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 24 Oct 2018 22:17:28 -0700 Subject: [PATCH 1/4] Adds hover-highlighting for relations in the raw membership list Moves hover-highlighting behavior to its own function Hover-highlighting now correctly highlights all members of the target relation --- modules/ui/entity_highlight.js | 17 +++++++++++++++++ modules/ui/raw_member_editor.js | 10 ++++------ modules/ui/raw_membership_editor.js | 10 ++++++++++ modules/ui/selection_list.js | 8 ++++---- 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 modules/ui/entity_highlight.js diff --git a/modules/ui/entity_highlight.js b/modules/ui/entity_highlight.js new file mode 100644 index 0000000000..a9f222c1c4 --- /dev/null +++ b/modules/ui/entity_highlight.js @@ -0,0 +1,17 @@ +import _forEach from 'lodash-es/forEach'; + +export function highlightEntity(context, entity, highlighted) { + + // highlight the member feature in the map while hovering on the list item + var selectorPrefix = entity.type === 'node' ? 'g.' : 'path.'; + context.surface().selectAll(selectorPrefix+entity.id).classed('highlighted', highlighted); + if (entity.members) { + // recursively highlight members so that relations will appear highlighted + _forEach(entity.members, function(member){ + if (member.id && context.hasEntity(member.id)) { + highlightEntity(context, context.entity(member.id), highlighted); + } + }); + } + +} diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index a371534644..bf820776a3 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -7,6 +7,7 @@ import { d3combobox as d3_combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { actionChangeMember, actionDeleteMember } from '../actions'; +import { highlightEntity } from './entity_highlight'; import { modeBrowse, modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; @@ -37,8 +38,7 @@ export function uiRawMemberEditor(context) { context.map().zoomTo(entity); // highlight the feature in case it wasn't previously on-screen - var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + highlightEntity(context, d.member, true); } @@ -134,13 +134,11 @@ export function uiRawMemberEditor(context) { .each(function(d) { if (d.member) { - // highlight the member feature in the map while hovering on the list item - var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; d3_select(this).on('mouseover', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + highlightEntity(context, d.member, true); }); d3_select(this).on('mouseout', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', false); + highlightEntity(context, d.member, false); }); var label = d3_select(this).append('label') diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js index 0fadc1b7bf..1c71eeb2ca 100644 --- a/modules/ui/raw_membership_editor.js +++ b/modules/ui/raw_membership_editor.js @@ -19,6 +19,7 @@ import { actionDeleteMember } from '../actions'; +import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity, osmRelation } from '../osm'; import { services } from '../services'; @@ -173,6 +174,15 @@ export function uiRawMembershipEditor(context) { .append('li') .attr('class', 'member-row member-row-normal form-field'); + enter.each(function(d){ + d3_select(this).on('mouseover', function() { + highlightEntity(context, d.relation, true); + }); + d3_select(this).on('mouseout', function() { + highlightEntity(context, d.relation, false); + }); + }); + var label = enter .append('label') .attr('class', 'form-label') diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 6362a7cead..0345943bfb 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -4,6 +4,7 @@ import { } from 'd3-selection'; import { t } from '../util/locale'; +import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; @@ -69,13 +70,12 @@ export function uiSelectionList(context, selectedIDs) { enter .each(function(d) { - // highlight the feature in the map while hovering on the list item - var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; + d3_select(this).on('mouseover', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + highlightEntity(context, d, true); }); d3_select(this).on('mouseout', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', false); + highlightEntity(context, d, false); }); }); From ddb6e1b119d4c37b3e500a322263336dd3254d6d Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 24 Oct 2018 22:27:37 -0700 Subject: [PATCH 2/4] Fixes comments related to hover-highlighting --- modules/ui/entity_highlight.js | 2 +- modules/ui/raw_member_editor.js | 1 + modules/ui/raw_membership_editor.js | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ui/entity_highlight.js b/modules/ui/entity_highlight.js index a9f222c1c4..cc92a0250a 100644 --- a/modules/ui/entity_highlight.js +++ b/modules/ui/entity_highlight.js @@ -2,8 +2,8 @@ import _forEach from 'lodash-es/forEach'; export function highlightEntity(context, entity, highlighted) { - // highlight the member feature in the map while hovering on the list item var selectorPrefix = entity.type === 'node' ? 'g.' : 'path.'; + // set the class for the SVG to add or remove the highlighted styling context.surface().selectAll(selectorPrefix+entity.id).classed('highlighted', highlighted); if (entity.members) { // recursively highlight members so that relations will appear highlighted diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index bf820776a3..a2a70be895 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -134,6 +134,7 @@ export function uiRawMemberEditor(context) { .each(function(d) { if (d.member) { + // highlight the member feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { highlightEntity(context, d.member, true); }); diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js index 1c71eeb2ca..a63c9e668a 100644 --- a/modules/ui/raw_membership_editor.js +++ b/modules/ui/raw_membership_editor.js @@ -175,6 +175,7 @@ export function uiRawMembershipEditor(context) { .attr('class', 'member-row member-row-normal form-field'); enter.each(function(d){ + // highlight the relation in the map while hovering on the list item d3_select(this).on('mouseover', function() { highlightEntity(context, d.relation, true); }); From 51386e03ae93943af718aab10e6457e3dafac6df Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 24 Oct 2018 22:30:29 -0700 Subject: [PATCH 3/4] Restores the hover-highlight comment in selection_list.js --- modules/ui/selection_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 0345943bfb..0e12a66a61 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -70,7 +70,7 @@ export function uiSelectionList(context, selectedIDs) { enter .each(function(d) { - + // highlight the feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { highlightEntity(context, d, true); }); From 1161767932dd04b04b8704eea51a4bc9ab9c3f74 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Fri, 26 Oct 2018 18:39:01 -0700 Subject: [PATCH 4/4] Moves the entity highlighting function to util/util.js Breaks out the code for getting a selector for entities and all their descendants into a generic function Accounts for circular relations when recursively getting all relation member IDs --- modules/ui/entity_highlight.js | 17 --------------- modules/ui/raw_member_editor.js | 10 ++++----- modules/ui/raw_membership_editor.js | 7 +++--- modules/ui/selection_list.js | 7 +++--- modules/util/index.js | 2 ++ modules/util/util.js | 33 +++++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 30 deletions(-) delete mode 100644 modules/ui/entity_highlight.js diff --git a/modules/ui/entity_highlight.js b/modules/ui/entity_highlight.js deleted file mode 100644 index cc92a0250a..0000000000 --- a/modules/ui/entity_highlight.js +++ /dev/null @@ -1,17 +0,0 @@ -import _forEach from 'lodash-es/forEach'; - -export function highlightEntity(context, entity, highlighted) { - - var selectorPrefix = entity.type === 'node' ? 'g.' : 'path.'; - // set the class for the SVG to add or remove the highlighted styling - context.surface().selectAll(selectorPrefix+entity.id).classed('highlighted', highlighted); - if (entity.members) { - // recursively highlight members so that relations will appear highlighted - _forEach(entity.members, function(member){ - if (member.id && context.hasEntity(member.id)) { - highlightEntity(context, context.entity(member.id), highlighted); - } - }); - } - -} diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index a2a70be895..0d1080dca3 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -7,7 +7,6 @@ import { d3combobox as d3_combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { actionChangeMember, actionDeleteMember } from '../actions'; -import { highlightEntity } from './entity_highlight'; import { modeBrowse, modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; @@ -16,7 +15,8 @@ import { uiDisclosure } from './disclosure'; import { utilDisplayName, utilDisplayType, - utilNoAuto + utilNoAuto, + utilHighlightEntity } from '../util'; @@ -38,7 +38,7 @@ export function uiRawMemberEditor(context) { context.map().zoomTo(entity); // highlight the feature in case it wasn't previously on-screen - highlightEntity(context, d.member, true); + utilHighlightEntity(d.id, true, context); } @@ -136,10 +136,10 @@ export function uiRawMemberEditor(context) { // highlight the member feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { - highlightEntity(context, d.member, true); + utilHighlightEntity(d.id, true, context); }); d3_select(this).on('mouseout', function() { - highlightEntity(context, d.member, false); + utilHighlightEntity(d.id, false, context); }); var label = d3_select(this).append('label') diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js index a63c9e668a..f856cd7ab5 100644 --- a/modules/ui/raw_membership_editor.js +++ b/modules/ui/raw_membership_editor.js @@ -19,13 +19,12 @@ import { actionDeleteMember } from '../actions'; -import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity, osmRelation } from '../osm'; import { services } from '../services'; import { svgIcon } from '../svg'; import { uiDisclosure } from './disclosure'; -import { utilDisplayName, utilNoAuto } from '../util'; +import { utilDisplayName, utilNoAuto, utilHighlightEntity } from '../util'; export function uiRawMembershipEditor(context) { @@ -177,10 +176,10 @@ export function uiRawMembershipEditor(context) { enter.each(function(d){ // highlight the relation in the map while hovering on the list item d3_select(this).on('mouseover', function() { - highlightEntity(context, d.relation, true); + utilHighlightEntity(d.relation.id, true, context); }); d3_select(this).on('mouseout', function() { - highlightEntity(context, d.relation, false); + utilHighlightEntity(d.relation.id, false, context); }); }); diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 0e12a66a61..f7f3a3ee7b 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -4,11 +4,10 @@ import { } from 'd3-selection'; import { t } from '../util/locale'; -import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; -import { utilDisplayName } from '../util'; +import { utilDisplayName, utilHighlightEntity } from '../util'; export function uiSelectionList(context, selectedIDs) { @@ -72,10 +71,10 @@ export function uiSelectionList(context, selectedIDs) { .each(function(d) { // highlight the feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { - highlightEntity(context, d, true); + utilHighlightEntity(d.id, true, context); }); d3_select(this).on('mouseout', function() { - highlightEntity(context, d, false); + utilHighlightEntity(d.id, false, context); }); }); diff --git a/modules/util/index.js b/modules/util/index.js index 16460cd148..a0269240ff 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -7,12 +7,14 @@ export { utilDisplayType } from './util'; export { utilEditDistance } from './util'; export { utilEntitySelector } from './util'; export { utilEntityOrMemberSelector } from './util'; +export { utilEntityOrDeepMemberSelector } from './util'; export { utilFastMouse } from './util'; export { utilFunctor } from './util'; export { utilGetAllNodes } from './util'; export { utilGetPrototypeOf } from './util'; export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; +export { utilHighlightEntity } from './util'; export { utilIdleWorker } from './idle_worker'; export { utilNoAuto } from './util'; export { utilPrefixCSSProperty } from './util'; diff --git a/modules/util/util.js b/modules/util/util.js index 6b03c9f4cd..5eb821ced1 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -34,6 +34,32 @@ export function utilEntityOrMemberSelector(ids, graph) { } +export function utilEntityOrDeepMemberSelector(ids, graph) { + var seen = {}; + var allIDs = []; + function addEntityAndMembersIfNotYetSeen(id) { + // avoid infinite recursion for circular relations by skipping seen entities + if (seen[id]) return; + // mark the entity as seen + seen[id] = true; + // add the id; + allIDs.push(id); + if (graph.hasEntity(id)) { + var entity = graph.entity(id); + if (entity.type === 'relation' && entity.members) { + entity.members.forEach(function(member){ + addEntityAndMembersIfNotYetSeen(member.id); + }); + } + } + } + ids.forEach(function(id) { + addEntityAndMembersIfNotYetSeen(id); + }); + return utilEntitySelector(allIDs); +} + + export function utilGetAllNodes(ids, graph) { var seen = {}; var nodes = []; @@ -282,3 +308,10 @@ export function utilHashcode(str) { } return hash; } + +// Adds or removes highlight styling for the specified entity's SVG elements in the map. +export function utilHighlightEntity(id, highlighted, context) { + context.surface() + .selectAll(utilEntityOrDeepMemberSelector([id], context.graph())) + .classed('highlighted', highlighted); +}