From c3216ddeca02ba899ce02dcea9a960c9fed4586d Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 18 Mar 2019 14:52:44 -0400 Subject: [PATCH] Add geometry filter controls to the add feature results dropdown (close #6056) --- css/80_app.css | 33 ++++++++++++++- data/core.yaml | 5 +++ dist/locales/en.json | 13 ++++-- modules/ui/search_add.js | 91 ++++++++++++++++++++++++++++++++++------ 4 files changed, 124 insertions(+), 18 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index bbfa4601c..d539020ce 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -641,17 +641,46 @@ button.add-note svg.icon { top: 44px; border: none; border-radius: 6px; - overflow-y: auto; max-width: 350px; +} +.search-add .popover .popover-content { + overflow-y: auto; +} +.search-add .popover, +.search-add .popover .popover-content { /* ensure corners are rounded in Chrome */ -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC); } +.search-add .popover .popover-footer { + padding: 5px 10px 5px 10px; + background: #f6f6f6; + border-top: 1px solid #DCDCDC; + display: flex; +} +.search-add .popover .popover-footer .message { + color: #666666; + flex-grow: 1; +} +.search-add .popover .popover-footer button.filter { + height: 20px; + background: transparent; + color: #666; +} +.search-add .popover .popover-footer button.filter.active { + color: #7092ff; +} +.search-add .popover .popover-footer button.filter:hover { + color: #333; +} +.search-add .popover .popover-footer button.filter.active:hover { + color: #597be7; +} .search-add .popover::-webkit-scrollbar { /* don't overlap rounded corners */ background: transparent; } .search-add .popover .list { - max-height: 70vh; + max-height: 65vh; } .search-add .list-item > .row { display: flex; diff --git a/data/core.yaml b/data/core.yaml index 5172ab2c2..e39a1419b 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -13,18 +13,23 @@ en: title: Add a feature description: "Search for features to add to the map." key: Tab + result: "{count} result" + results: "{count} results" add_area: title: Area description: "Add parks, buildings, lakes or other areas to the map." tail: "Click on the map to start drawing an area, like a park, lake, or building." + filter_tooltip: areas add_line: title: Line description: "Add highways, streets, pedestrian paths, canals or other lines to the map." tail: "Click on the map to start drawing a road, path, or route." + filter_tooltip: lines add_point: title: Point description: "Add restaurants, monuments, postal boxes or other points to the map." tail: Click on the map to add a point. + filter_tooltip: points add_note: title: Note description: "Spotted an issue? Let other mappers know." diff --git a/dist/locales/en.json b/dist/locales/en.json index ac7cc4f5c..43123d9ee 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -14,22 +14,27 @@ "add_feature": { "title": "Add a feature", "description": "Search for features to add to the map.", - "key": "Tab" + "key": "Tab", + "result": "{count} result", + "results": "{count} results" }, "add_area": { "title": "Area", "description": "Add parks, buildings, lakes or other areas to the map.", - "tail": "Click on the map to start drawing an area, like a park, lake, or building." + "tail": "Click on the map to start drawing an area, like a park, lake, or building.", + "filter_tooltip": "areas" }, "add_line": { "title": "Line", "description": "Add highways, streets, pedestrian paths, canals or other lines to the map.", - "tail": "Click on the map to start drawing a road, path, or route." + "tail": "Click on the map to start drawing a road, path, or route.", + "filter_tooltip": "lines" }, "add_point": { "title": "Point", "description": "Add restaurants, monuments, postal boxes or other points to the map.", - "tail": "Click on the map to add a point." + "tail": "Click on the map to add a point.", + "filter_tooltip": "points" }, "add_note": { "title": "Note", diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index b7555066d..c75e39521 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -1,3 +1,4 @@ +import _clone from 'lodash-es/clone'; import _debounce from 'lodash-es/debounce'; import { dispatch as d3_dispatch } from 'd3-dispatch'; @@ -29,13 +30,41 @@ export function uiSearchAdd(context) { var searchWrap = d3_select(null), search = d3_select(null), popover = d3_select(null), - list = d3_select(null); + popoverContent = d3_select(null), + list = d3_select(null), + footer = d3_select(null), + message = d3_select(null); - var shownGeometry = ['area', 'line', 'point', 'vertex']; + var allowedGeometry = ['area', 'line', 'point', 'vertex']; + var shownGeometry = []; + + function updateShownGeometry(geom) { + shownGeometry = geom.sort(); + presets = context.presets().matchAnyGeometry(shownGeometry); + } + function toggleShownGeometry(d) { + var geom = shownGeometry; + var index = geom.indexOf(d); + if (index === -1) { + geom.push(d); + if (d === 'point') geom.push('vertex'); + } else { + geom.splice(index, 1); + if (d === 'point') geom.splice(geom.indexOf('vertex'), 1); + } + updateShownGeometry(geom); + } + + function updateFilterButtonsStates() { + footer.selectAll('button.filter') + .classed('active', function(d) { + return shownGeometry.indexOf(d) !== -1; + }); + } function searchAdd(selection) { - presets = context.presets().matchAnyGeometry(shownGeometry); + updateShownGeometry(_clone(allowedGeometry)); var key = t('modes.add_feature.key'); @@ -82,7 +111,7 @@ export function uiSearchAdd(context) { }) .on('keypress', keypress) .on('keydown', keydown) - .on('input', searchInput); + .on('input', updateResultsList); searchWrap .call(svgIcon('#iD-icon-search', 'search-icon pre-text')); @@ -96,10 +125,43 @@ export function uiSearchAdd(context) { d3_event.stopPropagation(); }); - list = popover + popoverContent = popover .append('div') + .attr('class', 'popover-content'); + + list = popoverContent.append('div') .attr('class', 'list'); + footer = popover + .append('div') + .attr('class', 'popover-footer'); + + message = footer.append('div') + .attr('class', 'message'); + + footer.append('div') + .attr('class', 'filter-wrap') + .selectAll('button.filter') + .data(['point', 'line', 'area']) + .enter() + .append('button') + .attr('class', 'filter active') + .attr('title', function(d) { + return t('modes.add_' + d + '.filter_tooltip'); + }) + .each(function(d) { + d3_select(this).call(svgIcon('#iD-icon-' + d)); + }) + .on('click', function(d) { + toggleShownGeometry(d); + if (shownGeometry.length === 0) { + updateShownGeometry(_clone(allowedGeometry)); + toggleShownGeometry(d); + } + updateFilterButtonsStates(); + updateResultsList(); + }); + context.features().on('change.search-add', updateForFeatureHiddenState); context.keybinding().on(key, function() { @@ -115,6 +177,8 @@ export function uiSearchAdd(context) { .on('drawn.search-add', debouncedUpdate); updateEnabledState(); + + updateResultsList(); } function osmEditable() { @@ -200,7 +264,7 @@ export function uiSearchAdd(context) { } } - function searchInput() { + function updateResultsList() { var value = search.property('value'); var results; @@ -219,6 +283,9 @@ export function uiSearchAdd(context) { popover.selectAll('.list .list-item.focused') .classed('focused', false); focusListItem(popover.selectAll('.list > .list-item:first-child')); + + var resultCount = results.length; + message.text(t('modes.add_feature.' + (resultCount === 1 ? 'result' : 'results'), { count: resultCount })); } function focusListItem(selection) { @@ -233,14 +300,14 @@ export function uiSearchAdd(context) { if (selection.empty()) return; var node = selection.nodes()[0]; - var popoverNode = popover.node(); + var scrollableNode = popoverContent.node(); - if (node.offsetTop < popoverNode.scrollTop) { - popoverNode.scrollTop = node.offsetTop; + if (node.offsetTop < scrollableNode.scrollTop) { + scrollableNode.scrollTop = node.offsetTop; - } else if (node.offsetTop + node.offsetHeight > popoverNode.scrollTop + popoverNode.offsetHeight && - node.offsetHeight < popoverNode.offsetHeight) { - popoverNode.scrollTop = node.offsetTop + node.offsetHeight - popoverNode.offsetHeight; + } else if (node.offsetTop + node.offsetHeight > scrollableNode.scrollTop + scrollableNode.offsetHeight && + node.offsetHeight < scrollableNode.offsetHeight) { + scrollableNode.scrollTop = node.offsetTop + node.offsetHeight - scrollableNode.offsetHeight; } }