From b6effd6ed0cdbb9216d9c95562688dbaddc99093 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 11:19:40 -0500 Subject: [PATCH 01/65] Add initial search bar UI element --- css/80_app.css | 23 +++++++++++++++++++++ data/core.yaml | 2 ++ dist/locales/en.json | 3 +++ modules/ui/init.js | 12 ++++++++--- modules/ui/search_add.js | 43 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 modules/ui/search_add.js diff --git a/css/80_app.css b/css/80_app.css index 64246010c..73283418a 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -597,6 +597,29 @@ button.add-note svg.icon { border-right-width: 0; } +.search-add .search-wrap { + position: relative; +} +.search-add input[type='search'] { + position: relative; + width: 100%; + height:100%; + border: none; + border-radius: 20px; + font-size: 14px; + min-width: 160px; + text-indent: 25px; + padding: 5px 10px +} +.search-add .search-icon { + color: #333; + display: block; + position: absolute; + left: 10px; + top: 10px; + pointer-events: none; +} + /* Header for modals / panes ------------------------------------------------------- */ diff --git a/data/core.yaml b/data/core.yaml index ca8a5340b..b16152a63 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -9,6 +9,8 @@ en: open_wikidata: open on wikidata.org favorite: favorite modes: + add_feature: + title: Add a feature add_area: title: Area description: "Add parks, buildings, lakes or other areas to the map." diff --git a/dist/locales/en.json b/dist/locales/en.json index b41a90abf..2b037d142 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -11,6 +11,9 @@ "favorite": "favorite" }, "modes": { + "add_feature": { + "title": "Add a feature" + }, "add_area": { "title": "Area", "description": "Add parks, buildings, lakes or other areas to the map.", diff --git a/modules/ui/init.js b/modules/ui/init.js index d1d962658..472b0484e 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -36,6 +36,7 @@ import { uiSidebar } from './sidebar'; import { uiSpinner } from './spinner'; import { uiSplash } from './splash'; import { uiStatus } from './status'; +import { uiSearchAdd } from './search_add'; import { uiTooltipHtml } from './tooltipHtml'; import { uiUndoRedo } from './undo_redo'; import { uiVersion } from './version'; @@ -110,10 +111,15 @@ export function uiInit(context) { // Center area button group (Point/Line/Area/Note mode buttons) - bar - .append('div') - .attr('class', 'tool-group center-area') + var centerArea = bar .append('div') + .attr('class', 'tool-group center-area'); + + centerArea.append('div') + .attr('class', 'search-add') + .call(uiSearchAdd(context), bar); + + centerArea.append('div') .attr('class', 'modes joined') .call(uiModes(context), bar); diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js new file mode 100644 index 000000000..6b3b31522 --- /dev/null +++ b/modules/ui/search_add.js @@ -0,0 +1,43 @@ +import { dispatch as d3_dispatch } from 'd3-dispatch'; + +import { + event as d3_event, + select as d3_select, + selectAll as d3_selectAll +} from 'd3-selection'; + +import { t, textDirection } from '../util/locale'; +import { actionChangePreset } from '../actions/index'; +import { operationDelete } from '../operations/index'; +import { svgIcon } from '../svg/index'; +import { tooltip } from '../util/tooltip'; +import { uiPresetIcon } from './preset_icon'; +import { uiTagReference } from './tag_reference'; +import { utilKeybinding, utilNoAuto, utilRebind } from '../util'; + + +export function uiSearchAdd(context) { + var dispatch = d3_dispatch('choose'); + + function searchAdd(selection) { + + var searchWrap = selection + .append('div') + .attr('class', 'search-wrap'); + + var search = searchWrap + .append('input') + .attr('class', 'search-input') + .attr('placeholder', t('modes.add_feature.title')) + .attr('type', 'search') + .call(utilNoAuto); + //.on('keydown', initialKeydown) + //.on('keypress', keypress) + //.on('input', inputevent); + + searchWrap + .call(svgIcon('#iD-icon-search', 'search-icon pre-text')); + } + + return utilRebind(searchAdd, dispatch, 'on'); +} From 4a82604cab3cd0def48282a702cef381f40dec38 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 15:00:00 -0500 Subject: [PATCH 02/65] Add search-to-add functionality --- css/80_app.css | 68 +++++++++++++++-- modules/presets/collection.js | 17 ++++- modules/ui/init.js | 2 +- modules/ui/modes.js | 5 +- modules/ui/preset_icon.js | 2 +- modules/ui/save.js | 2 +- modules/ui/search_add.js | 133 ++++++++++++++++++++++++++++++++-- modules/ui/undo_redo.js | 2 +- 8 files changed, 208 insertions(+), 23 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 73283418a..73a3a288b 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -451,21 +451,24 @@ button[disabled].action:hover { width: 100%; } .tool-group.leading-area { - flex-shrink: 2; justify-content: flex-start; } +.tool-group.leading-area, +.tool-group.trailing-area { + flex-shrink: 2; +} .tool-group.center-area { justify-content: center; } .tool-group.trailing-area { - justify-content: flex-start; + justify-content: flex-end; } .tool-group > div { display: flex; margin: 0 5px; } -.tool-group button { +.tool-group button.bar-button { flex: 1 1 auto; flex-flow: row nowrap; align-items: center; @@ -589,7 +592,7 @@ button.add-note svg.icon { width: unset; } #bar.narrow .spinner, -#bar.narrow button .label { +#bar.narrow button.bar-button .label { display: none; } #bar.narrow button .count { @@ -597,8 +600,17 @@ button.add-note svg.icon { border-right-width: 0; } +/* Add a feature search bar +------------------------------------------------------- */ + +.search-add { + width: 100%; + min-width: 200px; + max-width: 600px; +} .search-add .search-wrap { position: relative; + width: 100%; } .search-add input[type='search'] { position: relative; @@ -607,10 +619,13 @@ button.add-note svg.icon { border: none; border-radius: 20px; font-size: 14px; - min-width: 160px; text-indent: 25px; padding: 5px 10px } +.search-add input[type='search']:focus { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} .search-add .search-icon { color: #333; display: block; @@ -619,7 +634,45 @@ button.add-note svg.icon { top: 10px; pointer-events: none; } - +.search-add .popover { + max-height: 200px; + width: 100%; + position: absolute; + top: 40px; + border-radius: 0 0 4px 4px; + border-top: 2px solid #DCDCDC; + overflow-y: auto; +} +.search-add .list-item { + display: flex; + position: relative; + padding: 2px; +} +.search-add .list-item:not(:last-of-type) { + border-bottom: 1px solid #DCDCDC; +} +.search-add .list-item .label { + font-weight: bold; + font-size: 12px; + padding-left: 2px; + top: 0; + bottom: 0; + position: relative; + display: flex; + align-items: center; + line-height: 1.2em; +} +.search-add .list-item > *:not(button) { + pointer-events: none; +} +.search-add .list-item button { + position: absolute; + border-radius: 0; + height: 100%; + width: 100%; + top: 0; + left: 0; +} /* Header for modals / panes ------------------------------------------------------- */ @@ -1018,9 +1071,10 @@ a.hide-toggle { height: 60px; text-align: center; } -#bar .preset-icon-container { +.preset-icon-container.small { width: 40px; height: 40px; + flex: 0 0 auto; } .preset-icon-line { diff --git a/modules/presets/collection.js b/modules/presets/collection.js index d110ee059..efc439038 100644 --- a/modules/presets/collection.js +++ b/modules/presets/collection.js @@ -34,6 +34,14 @@ export function presetCollection(collection) { })); }, + matchAnyGeometry: function(geometries) { + return presetCollection(this.collection.filter(function(d) { + return geometries.some(function(geometry) { + return d.matchGeometry(geometry); + }); + })); + }, + search: function(value, geometry) { if (!value) return this; @@ -134,8 +142,6 @@ export function presetCollection(collection) { return a.preset; }); - var other = presets.item(geometry); - var results = leading_name.concat( leading_suggestions, leading_terms, @@ -145,7 +151,12 @@ export function presetCollection(collection) { similar_terms ).slice(0, maxSearchResults - 1); - return presetCollection(_uniq(results.concat(other))); + if (geometry) { + var other = presets.item(geometry); + results = results.concat(other); + } + + return presetCollection(_uniq(results)); } }; diff --git a/modules/ui/init.js b/modules/ui/init.js index 472b0484e..c40ce62ea 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -91,7 +91,7 @@ export function uiInit(context) { var sidebarButton = leadingArea .append('div') .append('button') - .attr('class', 'sidebar-toggle') + .attr('class', 'sidebar-toggle bar-button') .attr('tabindex', -1) .on('click', ui.sidebar.toggle) .call(tooltip() diff --git a/modules/ui/modes.js b/modules/ui/modes.js index ea15f1199..111b04578 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -98,8 +98,6 @@ export function uiModes(context) { var favoritePresets = context.getFavoritePresets(); var favoriteModes = favoritePresets.map(function(d) { var preset = context.presets().item(d.id); - var isMaki = /^maki-/.test(preset.icon); - var icon = '#' + preset.icon + (isMaki ? '-11' : ''); var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + preset.name() .replace(/\s+/g, '_') + '-' + d.geom; //replace spaces with underscores to avoid css interpretation @@ -117,7 +115,6 @@ export function uiModes(context) { title: presetName, description: t(tooltipTitleID, { feature: presetName }), key: '', - icon: icon, preset: preset, geometry: d.geom }; @@ -145,7 +142,7 @@ export function uiModes(context) { var buttonsEnter = buttons.enter() .append('button') .attr('tabindex', -1) - .attr('class', function(d) { return d.id + ' add-button'; }) + .attr('class', function(d) { return d.id + ' add-button bar-button'; }) .on('click.mode-buttons', function(d) { if (!enabled(d)) return; diff --git a/modules/ui/preset_icon.js b/modules/ui/preset_icon.js index f699272e2..0f394c9d2 100644 --- a/modules/ui/preset_icon.js +++ b/modules/ui/preset_icon.js @@ -128,7 +128,7 @@ export function uiPresetIcon() { container = container.enter() .append('div') - .attr('class', 'preset-icon-container') + .attr('class', 'preset-icon-container ' + sizeClass) .merge(container); var p = preset.apply(this, arguments); diff --git a/modules/ui/save.js b/modules/ui/save.js index a818941f7..7bb972914 100644 --- a/modules/ui/save.js +++ b/modules/ui/save.js @@ -77,7 +77,7 @@ export function uiSave(context) { var button = selection .append('button') - .attr('class', 'save disabled') + .attr('class', 'save disabled bar-button') .attr('tabindex', -1) .on('click', save) .call(tooltipBehavior); diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 6b3b31522..a4b44242e 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -6,6 +6,12 @@ import { selectAll as d3_selectAll } from 'd3-selection'; +import { + modeAddArea, + modeAddLine, + modeAddPoint +} from '../modes'; + import { t, textDirection } from '../util/locale'; import { actionChangePreset } from '../actions/index'; import { operationDelete } from '../operations/index'; @@ -18,25 +24,142 @@ import { utilKeybinding, utilNoAuto, utilRebind } from '../util'; export function uiSearchAdd(context) { var dispatch = d3_dispatch('choose'); + var presets; + var search = d3_select(null), popover = d3_select(null), list = d3_select(null); function searchAdd(selection) { + presets = context.presets().matchAnyGeometry(['point', 'line', 'area']); + var searchWrap = selection .append('div') .attr('class', 'search-wrap'); - var search = searchWrap + search = searchWrap .append('input') .attr('class', 'search-input') .attr('placeholder', t('modes.add_feature.title')) .attr('type', 'search') - .call(utilNoAuto); - //.on('keydown', initialKeydown) - //.on('keypress', keypress) - //.on('input', inputevent); + .call(utilNoAuto) + .on('focus', function() { + popover.classed('hide', false); + }) + .on('blur', function() { + popover.classed('hide', true); + }) + .on('input', function () { + var value = search.property('value'); + //list.classed('filtered', value.length); + if (value.length) { + var results = presets.search(value); + list.call(drawList, results); + } else { + //list.call(drawList, context.presets().defaults(geometry, 36)); + } + }); searchWrap .call(svgIcon('#iD-icon-search', 'search-icon pre-text')); + + popover = searchWrap + .append('div') + .attr('class', 'popover fillL hide') + .on('mousedown', function() { + // don't blur the search input (and thus close results) + d3_event.preventDefault(); + d3_event.stopPropagation(); + }); + + list = popover + .append('div') + .attr('class', 'list');// + //.call(drawList, context.presets().defaults(geometry, 36)); + } + + function supportedGeometry(preset) { + return preset.geometry.filter(function(geometry) { + return ['point', 'line', 'area'].indexOf(geometry) !== -1; + }); + } + function defaultGeometry(item) { + if (item.geometry.filter) { + var supportedGeom = supportedGeometry(item); + if (supportedGeom.length === 1) { + return supportedGeom[0]; + } + } else { + return item.geometry; + } + return 'point'; + } + + function drawList(list, presets) { + /*var collection = presets.collection.reduce(function(collection, preset) { + if (preset.members) { + collection.push(CategoryItem(preset)); + } else if (preset.visible()) { + collection.push(PresetItem(preset)); + } + return collection; + }, []);*/ + + var items = list.selectAll('.list-item') + .data(presets.collection, function(d) { return d.id; }); + + items.order(); + + items.exit() + .remove(); + + var row = items.enter() + .append('div') + .attr('class', function(item) { return 'list-item preset-' + item.id.replace('/', '-'); }); + + var button = row.append('button') + .on('click', function(d) { + var geom = defaultGeometry(d); + var markerClass = 'add-preset add-' + geom + ' add-preset-' + d.name() + .replace(/\s+/g, '_') + + '-' + geom; //replace spaces with underscores to avoid css interpretation + var modeInfo = { + id: markerClass, + button: markerClass, + preset: d, + geometry: geom + }; + var mode; + switch (geom) { + case 'point': + case 'vertex': + mode = modeAddPoint(context, modeInfo); + break; + case 'line': + mode = modeAddLine(context, modeInfo); + break; + case 'area': + mode = modeAddArea(context, modeInfo); + } + search.node().blur(); + context.enter(mode); + }); + + row.each(function(d) { + d3_select(this).call( + uiPresetIcon() + .geometry(defaultGeometry(d)) + .preset(d) + .sizeClass('small') + ); + }); + row.append('div') + .attr('class', 'label') + .append('div') + .attr('class', 'label-inner') + .text(function(d) { + return d.name(); + }); + + //updateForFeatureHiddenState(); } return utilRebind(searchAdd, dispatch, 'on'); diff --git a/modules/ui/undo_redo.js b/modules/ui/undo_redo.js index eef37b5d2..7222396b9 100644 --- a/modules/ui/undo_redo.js +++ b/modules/ui/undo_redo.js @@ -46,7 +46,7 @@ export function uiUndoRedo(context) { .data(commands) .enter() .append('button') - .attr('class', function(d) { return 'disabled ' + d.id + '-button'; }) + .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; }) .on('click', function(d) { return d.action(); }) .call(tooltipBehavior); From a2297c88960a89b32bafe3337b81b3002bf22449 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 15:23:08 -0500 Subject: [PATCH 03/65] Move spinner out of the top bar --- css/80_app.css | 19 +++++++++++-------- modules/ui/init.js | 11 ++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 73a3a288b..c3a47586a 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -568,15 +568,15 @@ button.add-note svg.icon { .spinner { opacity: .5; - display: flex; - flex-shrink: 2; - justify-content: flex-end; + position: absolute; + right: 4px; + bottom: 26px; } .spinner img { - height: 40px; - width: 40px; - border-radius: 4px; - background: black; + height: 20px; + width: 20px; + background: transparent; + border-radius: 100%; } [dir='rtl'] .spinner img { -moz-transform: scaleX(-1); @@ -635,7 +635,7 @@ button.add-note svg.icon { pointer-events: none; } .search-add .popover { - max-height: 200px; + max-height: 250px; width: 100%; position: absolute; top: 40px; @@ -643,6 +643,9 @@ button.add-note svg.icon { border-top: 2px solid #DCDCDC; overflow-y: auto; } +.search-add .popover .list { + max-height: 70vh; +} .search-add .list-item { display: flex; position: relative; diff --git a/modules/ui/init.js b/modules/ui/init.js index c40ce62ea..27fbf56f7 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -140,13 +140,6 @@ export function uiInit(context) { .call(uiSave(context)); - // For now, just put spinner at the end of the #bar - bar - .append('div') - .attr('class', 'spinner') - .call(uiSpinner(context)); - - // Map controls (appended to #bar, but absolutely positioned) var controls = content .append('div') @@ -190,6 +183,10 @@ export function uiInit(context) { .call(help.renderToggleButton); content.call(help.renderPane); + content + .append('div') + .attr('class', 'spinner') + .call(uiSpinner(context)); // Add attribution and footer var about = content From 4e5b507fc56b2c804e7bd643ce5b81ab2fedb4a9 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 15:48:47 -0500 Subject: [PATCH 04/65] Separate the add note button from the add feature buttons --- modules/ui/init.js | 5 ++ modules/ui/modes.js | 25 ++------- modules/ui/notes.js | 128 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 21 deletions(-) create mode 100644 modules/ui/notes.js diff --git a/modules/ui/init.js b/modules/ui/init.js index 27fbf56f7..8845d6f40 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -26,6 +26,7 @@ import { uiLoading } from './loading'; import { uiMapData } from './map_data'; import { uiMapInMap } from './map_in_map'; import { uiModes } from './modes'; +import { uiNotes } from './notes'; import { uiNotice } from './notice'; import { uiPhotoviewer } from './photoviewer'; import { uiRestore } from './restore'; @@ -123,6 +124,10 @@ export function uiInit(context) { .attr('class', 'modes joined') .call(uiModes(context), bar); + centerArea.append('div') + .attr('class', 'notes') + .call(uiNotes(context), bar); + // Trailing area button group (Undo/Redo save buttons) var trailingArea = bar diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 111b04578..e3a6b3b6c 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -6,7 +6,6 @@ import { modeAddArea, modeAddLine, modeAddPoint, - modeAddNote, modeBrowse } from '../modes'; @@ -20,17 +19,12 @@ export function uiModes(context) { var modes = [ modeAddPoint(context), modeAddLine(context), - modeAddArea(context), - modeAddNote(context) + modeAddArea(context) ]; - function enabled(d) { - if (d.id === 'add-note') { - return notesEnabled() && notesEditable(); - } else { - return osmEditable(); - } + function enabled() { + return osmEditable(); } function osmEditable() { @@ -38,16 +32,6 @@ export function uiModes(context) { return context.editable() && mode && mode.id !== 'save'; } - function notesEnabled() { - var noteLayer = context.layers().layer('notes'); - return noteLayer && noteLayer.enabled(); - } - - function notesEditable() { - var mode = context.mode(); - return context.map().notesEditable() && mode && mode.id !== 'save'; - } - return function(selection) { context @@ -91,8 +75,7 @@ export function uiModes(context) { function update() { - var showNotes = notesEnabled(); - var data = showNotes ? modes : modes.slice(0, 3); + var data = modes; // add favorite presets to modes var favoritePresets = context.getFavoritePresets(); diff --git a/modules/ui/notes.js b/modules/ui/notes.js new file mode 100644 index 000000000..45a2d62be --- /dev/null +++ b/modules/ui/notes.js @@ -0,0 +1,128 @@ +import _debounce from 'lodash-es/debounce'; + +import { select as d3_select } from 'd3-selection'; + +import { + modeAddNote, + modeBrowse +} from '../modes'; + +import { svgIcon } from '../svg'; +import { tooltip } from '../util/tooltip'; +import { uiTooltipHtml } from './tooltipHtml'; + +export function uiNotes(context) { + + var mode = modeAddNote(context); + + function enabled() { + return notesEnabled() && notesEditable(); + } + + function notesEnabled() { + var noteLayer = context.layers().layer('notes'); + return noteLayer && noteLayer.enabled(); + } + + function notesEditable() { + var mode = context.mode(); + return context.map().notesEditable() && mode && mode.id !== 'save'; + } + + + return function(selection) { + context + .on('enter.editor.notes', function(entered) { + selection.selectAll('button.add-button') + .classed('active', function(mode) { return entered.button === mode.button; }); + context.container() + .classed('mode-' + entered.id, true); + }); + + context + .on('exit.editor.notes', function(exited) { + context.container() + .classed('mode-' + exited.id, false); + }); + + context.keybinding().on(mode.key, function() { + if (!enabled(mode)) return; + + if (mode.id === context.mode().id) { + context.enter(modeBrowse(context)); + } else { + context.enter(mode); + } + }); + + + var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true }); + + context.map() + .on('move.notes', debouncedUpdate) + .on('drawn.notes', debouncedUpdate); + + context + .on('enter.notes', update); + + update(); + + + function update() { + var showNotes = notesEnabled(); + var data = showNotes ? [mode] : []; + + var buttons = selection.selectAll('button.add-button') + .data(data, function(d) { return d.id; }); + + // exit + buttons.exit() + .remove(); + + // enter + var buttonsEnter = buttons.enter() + .append('button') + .attr('tabindex', -1) + .attr('class', function(d) { return d.id + ' add-button bar-button'; }) + .on('click.notes', function(d) { + if (!enabled(d)) return; + + // When drawing, ignore accidental clicks on mode buttons - #4042 + var currMode = context.mode().id; + if (/^draw/.test(currMode)) return; + + if (d.id === currMode) { + context.enter(modeBrowse(context)); + } else { + context.enter(d); + } + }) + .call(tooltip() + .placement('bottom') + .html(true) + .title(function(d) { return uiTooltipHtml(d.description, d.key); }) + ); + + buttonsEnter + .each(function(d) { + d3_select(this) + .call(svgIcon(d.icon || '#iD-icon-' + d.button)); + }); + + buttonsEnter + .append('span') + .attr('class', 'label') + .text(function(mode) { return mode.title; }); + + // if we are adding/removing the buttons, check if toolbar has overflowed + if (buttons.enter().size() || buttons.exit().size()) { + context.ui().checkOverflow('#bar', true); + } + + // update + buttons = buttons + .merge(buttonsEnter) + .classed('disabled', function(d) { return !enabled(d); }); + } + }; +} From 43661d1b39a30d6fa9ff34778c68f6f5fbb42c18 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 16:20:19 -0500 Subject: [PATCH 05/65] Merge add search bar UI with draw mode buttons UI --- css/80_app.css | 34 ++++++++++++++++++++-------------- modules/ui/init.js | 10 ++++------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index c3a47586a..94937f647 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -308,29 +308,29 @@ button.disabled { cursor: not-allowed; } -.joined button { +.joined > * { border-radius: 0; border-right: 1px solid rgba(0,0,0,.5); } -[dir='rtl'] .joined button { +[dir='rtl'] .joined > * { border-left: 1px solid rgba(0,0,0,.5); border-right: none; } -.fillL .joined button { +.fillL .joined > * { border-right: 1px solid #fff; } -.joined button:first-child { +.joined > *:first-child { border-radius: 4px 0 0 4px; } -[dir='rtl'] .joined button:first-child { +[dir='rtl'] .joined > *:first-child { border-radius: 0 4px 4px 0; } -.joined button:last-child { +.joined > *:last-child { border-right-width: 0; border-radius: 0 4px 4px 0; } -[dir='rtl'] .joined button:last-child { +[dir='rtl'] .joined > *.bar-button:last-child { border-radius: 4px 0 0 4px; } @@ -469,7 +469,7 @@ button[disabled].action:hover { margin: 0 5px; } .tool-group button.bar-button { - flex: 1 1 auto; + flex: 0 0 auto; flex-flow: row nowrap; align-items: center; padding: 0 10px; @@ -501,10 +501,10 @@ button[disabled].action:hover { padding-right: 0 !important; padding-left: 0 !important; } -.tool-group button > .icon { +.tool-group button.bar-button .icon { flex: 0 0 20px; } -.tool-group button .label { +.tool-group button.bar-button .label { flex: 0 1 auto; padding: 0 5px; } @@ -605,22 +605,28 @@ button.add-note svg.icon { .search-add { width: 100%; - min-width: 200px; - max-width: 600px; + justify-content: center; } .search-add .search-wrap { position: relative; width: 100%; + min-width: 200px; + max-width: 400px; + border-radius: 20px 0 0 20px; + background: #fff; +} +.search-add .search-wrap:only-child { + border-radius: 20px; } .search-add input[type='search'] { position: relative; width: 100%; height:100%; border: none; - border-radius: 20px; font-size: 14px; text-indent: 25px; - padding: 5px 10px + padding: 5px 10px; + border-radius: inherit; } .search-add input[type='search']:focus { border-bottom-left-radius: 0; diff --git a/modules/ui/init.js b/modules/ui/init.js index 8845d6f40..942f0be84 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -116,13 +116,11 @@ export function uiInit(context) { .append('div') .attr('class', 'tool-group center-area'); - centerArea.append('div') - .attr('class', 'search-add') - .call(uiSearchAdd(context), bar); + var addArea = centerArea.append('div') + .attr('class', 'search-add joined'); - centerArea.append('div') - .attr('class', 'modes joined') - .call(uiModes(context), bar); + addArea.call(uiSearchAdd(context), bar); + addArea.call(uiModes(context), bar); centerArea.append('div') .attr('class', 'notes') From 9b8d4d2ab42ae23fceca416549ee71a862739338 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 16:39:54 -0500 Subject: [PATCH 06/65] Remove labels from mode buttons --- css/80_app.css | 25 ++----------------------- modules/ui/modes.js | 4 ++-- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 94937f647..6c0d53eb9 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -477,29 +477,8 @@ button[disabled].action:hover { white-space: nowrap; display: flex; } -[dir='ltr'] .tool-group button.add-preset.add-point, -[dir='ltr'] .tool-group button.add-preset.add-point .label { - padding-left: 0px; -} -[dir='ltr'] .tool-group button.add-preset:not(.add-point) { - padding-left: 5px; -} -[dir='ltr'] .tool-group button.add-preset:not(.add-point) .label { - padding-left: 3px; -} -[dir='rtl'] .tool-group button.add-preset.add-point, -[dir='rtl'] .tool-group button.add-preset.add-point .label { - padding-right: 0px; -} -[dir='rtl'] .tool-group button.add-preset:not(.add-point) { - padding-right: 5px; -} -[dir='rtl'] .tool-group button.add-preset:not(.add-point) .label { - padding-right: 3px; -} -.narrow .tool-group button.add-preset { - padding-right: 0 !important; - padding-left: 0 !important; +.tool-group button.add-preset { + padding: 0; } .tool-group button.bar-button .icon { flex: 0 0 20px; diff --git a/modules/ui/modes.js b/modules/ui/modes.js index e3a6b3b6c..eecd929d4 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -159,12 +159,12 @@ export function uiModes(context) { .call(svgIcon(d.icon || '#iD-icon-' + d.button)); } }); - + /* buttonsEnter .append('span') .attr('class', 'label') .text(function(mode) { return mode.title; }); - + */ // if we are adding/removing the buttons, check if toolbar has overflowed if (buttons.enter().size() || buttons.exit().size()) { context.ui().checkOverflow('#bar', true); From 81996ee55166a5e7a6b8c008c9517430ac3ed8d3 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 17:13:38 -0500 Subject: [PATCH 07/65] Add favoriting or presets in the search add bar --- css/80_app.css | 24 ++++++++++++++++++------ modules/ui/preset_favorite.js | 6 +++--- modules/ui/search_add.js | 10 ++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 6c0d53eb9..07a6ea192 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -649,11 +649,12 @@ button.add-note svg.icon { display: flex; align-items: center; line-height: 1.2em; + width: 100%; } .search-add .list-item > *:not(button) { pointer-events: none; } -.search-add .list-item button { +.search-add .list-item button.choose { position: absolute; border-radius: 0; height: 100%; @@ -661,7 +662,17 @@ button.add-note svg.icon { top: 0; left: 0; } - +.search-add .list-item button.accessory { + position: relative; + flex: 0 0 auto; + color: #808080; + background: transparent; + padding-right: 3px; + padding-left: 3px; +} +.search-add .list-item button.accessory:hover { + color: #666; +} /* Header for modals / panes ------------------------------------------------------- */ .header { @@ -1209,7 +1220,8 @@ a.hide-toggle { .preset-list-item button.tag-reference-button { height: 100%; border: 1px solid #ccc; - flex: 32px; + width: 32px; + flex: 0 0 auto; background: #f6f6f6; } [dir='ltr'] .preset-list-item button.preset-favorite-button, @@ -1238,11 +1250,11 @@ a.hide-toggle { opacity: .5; } -.preset-list-item button.preset-favorite-button .icon { +button.preset-favorite-button .icon { fill-opacity: 0; - stroke-width: 1.6; + stroke-width: 1; } -.preset-list-item button.preset-favorite-button.active .icon { +button.preset-favorite-button.active .icon { fill-opacity: inherit; } diff --git a/modules/ui/preset_favorite.js b/modules/ui/preset_favorite.js index ef43ee338..d100b6e18 100644 --- a/modules/ui/preset_favorite.js +++ b/modules/ui/preset_favorite.js @@ -6,7 +6,7 @@ import { import { t } from '../util/locale'; import { svgIcon } from '../svg'; -export function uiPresetFavorite(preset, geom, context) { +export function uiPresetFavorite(preset, geom, context, klass) { var presetFavorite = {}; @@ -15,7 +15,7 @@ export function uiPresetFavorite(preset, geom, context) { presetFavorite.button = function(selection) { - var canFavorite = geom !== 'vertex' && geom !== 'relation' && !preset.isFallback(); + var canFavorite = geom !== 'vertex' && geom !== 'relation'; _button = selection.selectAll('.preset-favorite-button') .data(canFavorite ? [0] : []); @@ -24,7 +24,7 @@ export function uiPresetFavorite(preset, geom, context) { _button = _button.enter() .insert('button', '.tag-reference-button') - .attr('class', 'preset-favorite-button') + .attr('class', 'preset-favorite-button ' + klass) .attr('title', t('icons.favorite')) .attr('tabindex', -1) .call(svgIcon('#iD-icon-favorite')) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index a4b44242e..72a75ab9e 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -17,6 +17,7 @@ import { actionChangePreset } from '../actions/index'; import { operationDelete } from '../operations/index'; import { svgIcon } from '../svg/index'; import { tooltip } from '../util/tooltip'; +import { uiPresetFavorite } from './preset_favorite'; import { uiPresetIcon } from './preset_icon'; import { uiTagReference } from './tag_reference'; import { utilKeybinding, utilNoAuto, utilRebind } from '../util'; @@ -116,6 +117,7 @@ export function uiSearchAdd(context) { .attr('class', function(item) { return 'list-item preset-' + item.id.replace('/', '-'); }); var button = row.append('button') + .attr('class', 'choose') .on('click', function(d) { var geom = defaultGeometry(d); var markerClass = 'add-preset add-' + geom + ' add-preset-' + d.name() @@ -159,6 +161,14 @@ export function uiSearchAdd(context) { return d.name(); }); + row.each(function(d) { + var supportedGeom = supportedGeometry(d); + if (supportedGeom.length === 1) { + var presetFavorite = uiPresetFavorite(d, supportedGeom[0], context, 'accessory'); + d3_select(this).call(presetFavorite.button); + } + }); + //updateForFeatureHiddenState(); } From 81320c6e3f74c27c7a2af86b4d694648950d2493 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 17:29:20 -0500 Subject: [PATCH 08/65] Remove static add generic geometry mode buttons --- css/80_app.css | 2 +- modules/ui/modes.js | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 07a6ea192..9036834cd 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -477,7 +477,7 @@ button[disabled].action:hover { white-space: nowrap; display: flex; } -.tool-group button.add-preset { +.tool-group button.add-preset:not(.add-generic-preset) { padding: 0; } .tool-group button.bar-button .icon { diff --git a/modules/ui/modes.js b/modules/ui/modes.js index eecd929d4..f7728c6ae 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -16,11 +16,6 @@ import { uiPresetIcon } from './preset_icon'; import { uiTooltipHtml } from './tooltipHtml'; export function uiModes(context) { - var modes = [ - modeAddPoint(context), - modeAddLine(context), - modeAddArea(context) - ]; function enabled() { @@ -47,7 +42,7 @@ export function uiModes(context) { context.container() .classed('mode-' + exited.id, false); }); - +/* modes.forEach(function(mode) { context.keybinding().on(mode.key, function() { if (!enabled(mode)) return; @@ -59,7 +54,7 @@ export function uiModes(context) { } }); }); - +*/ var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true }); @@ -75,8 +70,6 @@ export function uiModes(context) { function update() { - var data = modes; - // add favorite presets to modes var favoritePresets = context.getFavoritePresets(); var favoriteModes = favoritePresets.map(function(d) { @@ -84,6 +77,9 @@ export function uiModes(context) { var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + preset.name() .replace(/\s+/g, '_') + '-' + d.geom; //replace spaces with underscores to avoid css interpretation + if (preset.isFallback()) { + markerClass += ' add-generic-preset'; + } var presetName = t('presets.presets.' + preset.id + '.name'); var relevantMatchingGeometry = preset.geometry.filter(function(geometry) { return ['point', 'line', 'area'].indexOf(geometry) !== -1; @@ -112,7 +108,7 @@ export function uiModes(context) { } }); - data = data.concat(favoriteModes); + var data = favoriteModes; var buttons = selection.selectAll('button.add-button') .data(data, function(d) { return d.id; }); @@ -147,16 +143,16 @@ export function uiModes(context) { buttonsEnter .each(function(d) { - if (d.preset) { + if (d.preset.isFallback()) { + d3_select(this) + .call(svgIcon('#iD-icon-' + d.preset.id)); + } else { d3_select(this) .call(uiPresetIcon() .geometry(d.geometry) .preset(d.preset) .sizeClass('small') ); - } else { - d3_select(this) - .call(svgIcon(d.icon || '#iD-icon-' + d.button)); } }); /* From a108b7958b6a75ad6653a26a63eec8c8efed3342 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 18:05:47 -0500 Subject: [PATCH 09/65] Keep add feature search results a constant width instead of matching search input width --- css/80_app.css | 20 +++++++++++--------- modules/ui/search_add.js | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 9036834cd..befce3184 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -585,12 +585,13 @@ button.add-note svg.icon { .search-add { width: 100%; justify-content: center; + position: relative; } .search-add .search-wrap { position: relative; width: 100%; min-width: 200px; - max-width: 400px; + max-width: 250px; border-radius: 20px 0 0 20px; background: #fff; } @@ -600,17 +601,13 @@ button.add-note svg.icon { .search-add input[type='search'] { position: relative; width: 100%; - height:100%; + height: 100%; border: none; font-size: 14px; text-indent: 25px; padding: 5px 10px; border-radius: inherit; } -.search-add input[type='search']:focus { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} .search-add .search-icon { color: #333; display: block; @@ -623,10 +620,15 @@ button.add-note svg.icon { max-height: 250px; width: 100%; position: absolute; - top: 40px; - border-radius: 0 0 4px 4px; - border-top: 2px solid #DCDCDC; + top: 44px; + border: none; + border-radius: 6px; overflow-y: auto; + max-width: 350px; +} +.search-add .popover::-webkit-scrollbar { + /* don't overlap rounded corners */ + background: transparent; } .search-add .popover .list { max-height: 70vh; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 72a75ab9e..0aa29b022 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -62,7 +62,7 @@ export function uiSearchAdd(context) { searchWrap .call(svgIcon('#iD-icon-search', 'search-icon pre-text')); - popover = searchWrap + popover = selection .append('div') .attr('class', 'popover fillL hide') .on('mousedown', function() { From 3c1bf2d74a42deaa6fa129057d28a81a6be90ed8 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 28 Feb 2019 18:13:26 -0500 Subject: [PATCH 10/65] Fix CSS --- css/80_app.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index befce3184..757ff6369 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -595,7 +595,7 @@ button.add-note svg.icon { border-radius: 20px 0 0 20px; background: #fff; } -.search-add .search-wrap:only-child { +.search-add .search-wrap:nth-last-child(2) { border-radius: 20px; } .search-add input[type='search'] { From 5ed5a7536b942eb08a71ce7a42f01d4c02adb99d Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Fri, 1 Mar 2019 09:07:41 -0500 Subject: [PATCH 11/65] Show preset browser when adding generic point, line, or area --- modules/behavior/draw_way.js | 3 ++- modules/modes/add_point.js | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/behavior/draw_way.js b/modules/behavior/draw_way.js index f017d03e5..70b2c7f46 100644 --- a/modules/behavior/draw_way.js +++ b/modules/behavior/draw_way.js @@ -330,7 +330,8 @@ export function behaviorDrawWay(context, wayID, index, mode, startGraph, baselin window.setTimeout(function() { context.map().dblclickEnable(true); }, 1000); - var isNewFeature = !mode.isContinuing && mode.button.indexOf('add-preset-') === -1; + + var isNewFeature = !mode.isContinuing && context.presets().match(origWay, context.graph()).isFallback(); context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature)); if (isNewFeature) { context.validator().validate(); diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index 3b79f86b6..c8d98b165 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -35,9 +35,7 @@ export function modeAddPoint(context, customMode) { t('operations.add.annotation.point') ); - context.enter( - modeSelect(context, [node.id]).newFeature(!mode.preset) - ); + enterSelectMode(node); } @@ -49,8 +47,12 @@ export function modeAddPoint(context, customMode) { t('operations.add.annotation.vertex') ); + enterSelectMode(node); + } + + function enterSelectMode(node) { context.enter( - modeSelect(context, [node.id]).newFeature(!mode.preset) + modeSelect(context, [node.id]).newFeature(mode.preset.isFallback()) ); } From b883504849a26c8e037a80e792dfe5c1266a6466 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Fri, 1 Mar 2019 09:44:16 -0500 Subject: [PATCH 12/65] Prevent adding features from search-to-add field if they are hidden --- css/80_app.css | 14 ++++++++++++ modules/ui/search_add.js | 48 ++++++++++++++++++++++++++++++++++++++-- modules/util/tooltip.js | 4 ++-- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 757ff6369..7fda6878b 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -653,6 +653,10 @@ button.add-note svg.icon { line-height: 1.2em; width: 100%; } +.search-add .list-item.disabled .preset-icon-container, +.search-add .list-item.disabled .label { + opacity: 0.55; +} .search-add .list-item > *:not(button) { pointer-events: none; } @@ -664,6 +668,9 @@ button.add-note svg.icon { top: 0; left: 0; } +.search-add .list-item button.choose.disabled { + background-color: #ececec; +} .search-add .list-item button.accessory { position: relative; flex: 0 0 auto; @@ -4854,22 +4861,29 @@ svg.mouseclick use.right { } /* dark tooltips for sidebar / panels */ +.tooltip.dark.top .tooltip-arrow, .map-pane .tooltip.top .tooltip-arrow, #sidebar .tooltip.top .tooltip-arrow { border-top-color: #000; } +.tooltip.dark.bottom .tooltip-arrow, .map-pane .tooltip.bottom .tooltip-arrow, #sidebar .tooltip.bottom .tooltip-arrow { border-bottom-color: #000; } +.tooltip.dark.left .tooltip-arrow, .map-pane .tooltip.left .tooltip-arrow, #sidebar .tooltip.left .tooltip-arrow { border-left-color: #000; } +.tooltip.dark.right .tooltip-arrow, .map-pane .tooltip.right .tooltip-arrow, #sidebar .tooltip.right .tooltip-arrow { border-right-color: #000; } +.tooltip.dark .tooltip-inner, +.tooltip.dark .tooltip-heading, +.tooltip.dark .keyhint-wrap, .map-pane .tooltip-inner, .map-pane .tooltip-heading, .map-pane .keyhint-wrap, diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 0aa29b022..f3f1b24f8 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -75,8 +75,16 @@ export function uiSearchAdd(context) { .append('div') .attr('class', 'list');// //.call(drawList, context.presets().defaults(geometry, 36)); + + context.features().on('change.search-add', updateForFeatureHiddenState); } + function isSingularItem(item) { + if (item.geometry.filter) { + return supportedGeometry(item).length === 1; + } + return false; + } function supportedGeometry(preset) { return preset.geometry.filter(function(geometry) { return ['point', 'line', 'area'].indexOf(geometry) !== -1; @@ -116,9 +124,11 @@ export function uiSearchAdd(context) { .append('div') .attr('class', function(item) { return 'list-item preset-' + item.id.replace('/', '-'); }); - var button = row.append('button') + row.append('button') .attr('class', 'choose') .on('click', function(d) { + if (d3_select(this).classed('disabled')) return; + var geom = defaultGeometry(d); var markerClass = 'add-preset add-' + geom + ' add-preset-' + d.name() .replace(/\s+/g, '_') @@ -169,7 +179,41 @@ export function uiSearchAdd(context) { } }); - //updateForFeatureHiddenState(); + updateForFeatureHiddenState(); + } + + function updateForFeatureHiddenState() { + + var listItem = d3_selectAll('.search-add .popover .list-item'); + + // remove existing tooltips + listItem.selectAll('button.choose').call(tooltip().destroyAny); + + listItem.each(function(item, index) { + if (!isSingularItem(item)) { + return; + } + var geometry = defaultGeometry(item); + + var hiddenPresetFeaturesId = context.features().isHiddenPreset(item, geometry); + var isHiddenPreset = !!hiddenPresetFeaturesId; + + var button = d3_select(this).selectAll('button.choose'); + + d3_select(this).classed('disabled', isHiddenPreset); + button.classed('disabled', isHiddenPreset); + + if (isHiddenPreset) { + var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId); + var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual'; + var tooltipObj = { features: t('feature.' + hiddenPresetFeaturesId + '.description') }; + button.call(tooltip('dark') + .html(true) + .title(t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj)) + .placement(index < 2 ? 'bottom' : 'top') + ); + } + }); } return utilRebind(searchAdd, dispatch, 'on'); diff --git a/modules/util/tooltip.js b/modules/util/tooltip.js index 637229aee..bdb15b3a7 100644 --- a/modules/util/tooltip.js +++ b/modules/util/tooltip.js @@ -3,7 +3,7 @@ import { utilFunctor } from './index'; var _tooltipID = 0; -export function tooltip() { +export function tooltip(klass) { var _id = _tooltipID++; var tooltip = function(selection) { selection.each(setup); @@ -98,7 +98,7 @@ export function tooltip() { var enter = tip.enter() .append('div') - .attr('class', 'tooltip tooltip-' + _id); + .attr('class', 'tooltip tooltip-' + _id + ' ' + (klass ? klass : '')); enter .append('div') From a857847bf70d468c7742b9f99b0dfda48d393d79 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 13:05:59 -0500 Subject: [PATCH 13/65] Add geometry selection to search-to-add field --- css/80_app.css | 14 +++- modules/ui/search_add.js | 175 +++++++++++++++++++++++++++------------ 2 files changed, 134 insertions(+), 55 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 7fda6878b..95f2e74fb 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -638,7 +638,8 @@ button.add-note svg.icon { position: relative; padding: 2px; } -.search-add .list-item:not(:last-of-type) { +.search-add .list-item:not(:last-of-type), +.search-add .subsection .list-item { border-bottom: 1px solid #DCDCDC; } .search-add .list-item .label { @@ -657,6 +658,10 @@ button.add-note svg.icon { .search-add .list-item.disabled .label { opacity: 0.55; } +.search-add .subsection .list-item .label { + font-weight: normal; + letter-spacing: 0.1; +} .search-add .list-item > *:not(button) { pointer-events: none; } @@ -671,6 +676,9 @@ button.add-note svg.icon { .search-add .list-item button.choose.disabled { background-color: #ececec; } +.search-add .subsection .list-item button.choose { + opacity: 0.85; +} .search-add .list-item button.accessory { position: relative; flex: 0 0 auto; @@ -682,6 +690,10 @@ button.add-note svg.icon { .search-add .list-item button.accessory:hover { color: #666; } +.search-add .subsection { + padding-left: 6px; + background-color: #CBCBCB; +} /* Header for modals / panes ------------------------------------------------------- */ .header { diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index f3f1b24f8..b669c5760 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -79,16 +79,10 @@ export function uiSearchAdd(context) { context.features().on('change.search-add', updateForFeatureHiddenState); } - function isSingularItem(item) { - if (item.geometry.filter) { - return supportedGeometry(item).length === 1; - } - return false; - } function supportedGeometry(preset) { return preset.geometry.filter(function(geometry) { return ['point', 'line', 'area'].indexOf(geometry) !== -1; - }); + }).sort(); } function defaultGeometry(item) { if (item.geometry.filter) { @@ -103,63 +97,57 @@ export function uiSearchAdd(context) { } function drawList(list, presets) { - /*var collection = presets.collection.reduce(function(collection, preset) { + + var collection = presets.collection.map(function(preset) { if (preset.members) { - collection.push(CategoryItem(preset)); + return CategoryItem(preset); } else if (preset.visible()) { - collection.push(PresetItem(preset)); + var supportedGeom = supportedGeometry(preset); + if (supportedGeom.length === 1) { + return AddablePresetItem(preset, supportedGeom[0]); + } + return MultiGeometryPresetItem(preset, supportedGeom); } - return collection; - }, []);*/ + }); var items = list.selectAll('.list-item') - .data(presets.collection, function(d) { return d.id; }); + .data(collection, function(d) { return d.preset.id; }); items.order(); items.exit() .remove(); - var row = items.enter() + items.enter(); + drawItems(items.enter()); + + updateForFeatureHiddenState(); + } + + function drawItems(selection) { + + var row = selection .append('div') - .attr('class', function(item) { return 'list-item preset-' + item.id.replace('/', '-'); }); + .attr('class', function(d) { return 'list-item preset-' + d.preset.id.replace(/\//g, '-'); }) + .attr('id', function(d) { + var id = 'search-add-list-item-preset-' + d.preset.id.replace(/\//g, '-'); + if (d.geometry) { + id += '-' + d.geometry; + } + return id; + }); row.append('button') .attr('class', 'choose') .on('click', function(d) { - if (d3_select(this).classed('disabled')) return; - - var geom = defaultGeometry(d); - var markerClass = 'add-preset add-' + geom + ' add-preset-' + d.name() - .replace(/\s+/g, '_') - + '-' + geom; //replace spaces with underscores to avoid css interpretation - var modeInfo = { - id: markerClass, - button: markerClass, - preset: d, - geometry: geom - }; - var mode; - switch (geom) { - case 'point': - case 'vertex': - mode = modeAddPoint(context, modeInfo); - break; - case 'line': - mode = modeAddLine(context, modeInfo); - break; - case 'area': - mode = modeAddArea(context, modeInfo); - } - search.node().blur(); - context.enter(mode); + d.choose.call(this); }); row.each(function(d) { d3_select(this).call( uiPresetIcon() - .geometry(defaultGeometry(d)) - .preset(d) + .geometry(d.geometry || defaultGeometry(d.preset)) + .preset(d.preset) .sizeClass('small') ); }); @@ -168,18 +156,18 @@ export function uiSearchAdd(context) { .append('div') .attr('class', 'label-inner') .text(function(d) { - return d.name(); + if (d.isSubitem) { + return t('modes.add_' + d.geometry + '.title'); + } + return d.preset.name(); }); row.each(function(d) { - var supportedGeom = supportedGeometry(d); - if (supportedGeom.length === 1) { - var presetFavorite = uiPresetFavorite(d, supportedGeom[0], context, 'accessory'); + if (d.geometry) { + var presetFavorite = uiPresetFavorite(d.preset,d.geometry, context, 'accessory'); d3_select(this).call(presetFavorite.button); } }); - - updateForFeatureHiddenState(); } function updateForFeatureHiddenState() { @@ -190,12 +178,9 @@ export function uiSearchAdd(context) { listItem.selectAll('button.choose').call(tooltip().destroyAny); listItem.each(function(item, index) { - if (!isSingularItem(item)) { - return; - } - var geometry = defaultGeometry(item); + if (!item.geometry) return; - var hiddenPresetFeaturesId = context.features().isHiddenPreset(item, geometry); + var hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, item.geometry); var isHiddenPreset = !!hiddenPresetFeaturesId; var button = d3_select(this).selectAll('button.choose'); @@ -216,5 +201,87 @@ export function uiSearchAdd(context) { }); } + function CategoryItem(preset) { + function item(selection) { + + } + item.preset = preset; + item.choose = function() { + }; + return item; + } + + function MultiGeometryPresetItem(preset, geometries) { + + var subsection = d3_select(null); + + function item(selection) { + + } + item.preset = preset; + item.geometries = geometries; + item.choose = function() { + var selection = d3_select(this); + if (selection.classed('disabled')) return; + + var shouldExpand = !selection.classed('expanded'); + + selection.classed('expanded', shouldExpand) + + if (shouldExpand) { + var subitems = geometries.map(function(geometry) { + return AddablePresetItem(preset, geometry, true); + }); + var selector = '#' + selection.node().closest('.list-item').id + ' + *'; + subsection = d3_selectAll('.search-add .popover .list').insert('div', selector) + .attr('class', 'subsection'); + var subitemsEnter = subsection.selectAll('.list-item') + .data(subitems) + .enter(); + drawItems(subitemsEnter); + updateForFeatureHiddenState(); + } else { + subsection.remove(); + } + }; + return item; + } + + function AddablePresetItem(preset, geometry, isSubitem) { + function item(selection) { + + } + item.isSubitem = isSubitem; + item.preset = preset; + item.geometry = geometry; + item.choose = function() { + if (d3_select(this).classed('disabled')) return; + + var markerClass = 'add-preset add-' + geometry + + ' add-preset-' + preset.name().replace(/\s+/g, '_') + '-' + geometry;n + var modeInfo = { + id: markerClass, + button: markerClass, + preset: preset, + geometry: geometry + }; + var mode; + switch (geometry) { + case 'point': + case 'vertex': + mode = modeAddPoint(context, modeInfo); + break; + case 'line': + mode = modeAddLine(context, modeInfo); + break; + case 'area': + mode = modeAddArea(context, modeInfo); + } + search.node().blur(); + context.enter(mode); + }; + return item; + } + return utilRebind(searchAdd, dispatch, 'on'); } From a8d5a16a7de48fed7894d5f20c0cfd6380b2c4e5 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 13:17:09 -0500 Subject: [PATCH 14/65] Tweak search-to-add --- css/80_app.css | 4 ++-- modules/ui/search_add.js | 22 +++++++--------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 95f2e74fb..6c02c614f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -659,8 +659,8 @@ button.add-note svg.icon { opacity: 0.55; } .search-add .subsection .list-item .label { - font-weight: normal; - letter-spacing: 0.1; + /*font-weight: normal; + letter-spacing: 0.1;*/ } .search-add .list-item > *:not(button) { pointer-events: none; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index b669c5760..a36124a5e 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -128,9 +128,9 @@ export function uiSearchAdd(context) { var row = selection .append('div') - .attr('class', function(d) { return 'list-item preset-' + d.preset.id.replace(/\//g, '-'); }) + .attr('class', 'list-item') .attr('id', function(d) { - var id = 'search-add-list-item-preset-' + d.preset.id.replace(/\//g, '-'); + var id = 'search-add-list-item-preset-' + d.preset.id.replace(/[^a-zA-Z\d:]/g, '-'); if (d.geometry) { id += '-' + d.geometry; } @@ -153,8 +153,6 @@ export function uiSearchAdd(context) { }); row.append('div') .attr('class', 'label') - .append('div') - .attr('class', 'label-inner') .text(function(d) { if (d.isSubitem) { return t('modes.add_' + d.geometry + '.title'); @@ -202,9 +200,7 @@ export function uiSearchAdd(context) { } function CategoryItem(preset) { - function item(selection) { - - } + var item = {}; item.preset = preset; item.choose = function() { }; @@ -215,9 +211,7 @@ export function uiSearchAdd(context) { var subsection = d3_select(null); - function item(selection) { - - } + var item = {}; item.preset = preset; item.geometries = geometries; item.choose = function() { @@ -226,7 +220,7 @@ export function uiSearchAdd(context) { var shouldExpand = !selection.classed('expanded'); - selection.classed('expanded', shouldExpand) + selection.classed('expanded', shouldExpand); if (shouldExpand) { var subitems = geometries.map(function(geometry) { @@ -248,9 +242,7 @@ export function uiSearchAdd(context) { } function AddablePresetItem(preset, geometry, isSubitem) { - function item(selection) { - - } + var item = {}; item.isSubitem = isSubitem; item.preset = preset; item.geometry = geometry; @@ -258,7 +250,7 @@ export function uiSearchAdd(context) { if (d3_select(this).classed('disabled')) return; var markerClass = 'add-preset add-' + geometry + - ' add-preset-' + preset.name().replace(/\s+/g, '_') + '-' + geometry;n + ' add-preset-' + preset.name().replace(/\s+/g, '_') + '-' + geometry; var modeInfo = { id: markerClass, button: markerClass, From 301d6267843fa42dc77a7cba7ec53ffd5f57ca9f Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 14:10:28 -0500 Subject: [PATCH 15/65] Select the search bar text upon focus --- css/80_app.css | 1 + modules/ui/search_add.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index 6c02c614f..07ede5b77 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -597,6 +597,7 @@ button.add-note svg.icon { } .search-add .search-wrap:nth-last-child(2) { border-radius: 20px; + border: none; } .search-add input[type='search'] { position: relative; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index a36124a5e..65b6a6c50 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -43,11 +43,20 @@ export function uiSearchAdd(context) { .attr('type', 'search') .call(utilNoAuto) .on('focus', function() { + search.attr('focusing', true); popover.classed('hide', false); }) .on('blur', function() { popover.classed('hide', true); }) + .on('click', function() { + if (search.attr('focusing')) { + search.attr('focusing', null); + search.node().setSelectionRange(0, search.property('value').length); + d3_event.preventDefault(); + d3_event.stopPropagation(); + } + }) .on('input', function () { var value = search.property('value'); //list.classed('filtered', value.length); From 3723c741f1e56d45eb5760e27e5a0fee9a0fee97 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 14:47:48 -0500 Subject: [PATCH 16/65] Add border to point icons in top bar --- css/80_app.css | 17 +++++++++++++---- modules/presets/preset.js | 11 ++++++----- modules/ui/preset_icon.js | 28 ++++++++++++++++++++++++++-- modules/ui/search_add.js | 25 +++++-------------------- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 07ede5b77..ce2dec94e 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1098,6 +1098,12 @@ a.hide-toggle { flex: 0 0 auto; } +.preset-icon-point-border path { + stroke: #333; + stroke-width: 1.5; + fill: transparent; +} + .preset-icon-line { margin: auto; position: absolute; @@ -1161,14 +1167,19 @@ a.hide-toggle { position: absolute; margin: auto; top: 26%; - left: 26%; + left: 0; + right: 0; width: 48%; height: 48%; } +.preset-icon-container.small .preset-icon.point-geom .icon { + width: 32%; + height: 32%; + top: 30%; +} .preset-icon.framed .icon { top: 30%; - left: 30%; width: 40%; height: 40%; } @@ -1178,14 +1189,12 @@ a.hide-toggle { .preset-icon-iD .icon { top: 0; - left: 0; height: 100%; width: 100%; } .preset-icon-iD.framed .icon { top: 13%; - left: 13%; width: 74%; height: 74%; } diff --git a/modules/presets/preset.js b/modules/presets/preset.js index 33e67fb55..ed1cb5c82 100644 --- a/modules/presets/preset.js +++ b/modules/presets/preset.js @@ -249,11 +249,12 @@ export function presetPreset(id, preset, fields, visible, rawPresets) { } } } - - for (var f in preset.fields) { - var field = preset.fields[f]; - if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) { - tags[field.key] = field.default; + if (geometry) { + for (var f in preset.fields) { + var field = preset.fields[f]; + if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) { + tags[field.key] = field.default; + } } } diff --git a/modules/ui/preset_icon.js b/modules/ui/preset_icon.js index 0f394c9d2..52169811a 100644 --- a/modules/ui/preset_icon.js +++ b/modules/ui/preset_icon.js @@ -27,6 +27,20 @@ export function uiPresetIcon() { return 'maki-marker-stroked'; } + function renderPointBorder(enter) { + var d = "M20,7 C14.4766667,7 10,10.207551 10,16.6166837 C10,23.0278061 20,33 20,33 C20,33 30,23.0278061 30,16.6166837 C30,10.207551 25.5233333,7 20,7 Z" + var w = 40, h = 40; + enter = enter + .append('svg') + .attr('class', 'preset-icon-fill preset-icon-point-border') + .attr('width', w) + .attr('height', h) + .attr('viewBox', '0 0 ' + w + ' ' + h); + + enter.append('path') + .attr('d', d); + } + function renderCircleFill(fillEnter) { var w = 60, h = 60, d = 40; fillEnter = fillEnter @@ -132,7 +146,7 @@ export function uiPresetIcon() { .merge(container); var p = preset.apply(this, arguments); - var geom = geometry.apply(this, arguments); + var geom = geometry ? geometry.apply(this, arguments) : null; var picon = getIcon(p, geom); var isMaki = /^maki-/.test(picon); var isTemaki = /^temaki-/.test(picon); @@ -151,6 +165,16 @@ export function uiPresetIcon() { } var tagClasses = svgTagClasses().getClassesString(tags, ''); + var pointBorder = container.selectAll('.preset-icon-point-border') + .data(geom === 'point' && isSmall() ? [0] : []); + + pointBorder.exit() + .remove(); + + var pointBorderEnter = pointBorder.enter(); + renderPointBorder(pointBorderEnter); + pointBorder = pointBorderEnter.merge(pointBorder); + var vertexFill = container.selectAll('.preset-icon-fill-vertex') .data(geom === 'vertex' ? [0] : []); @@ -206,7 +230,7 @@ export function uiPresetIcon() { .merge(icon); icon - .attr('class', 'preset-icon ' + geom + '-geom') + .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')) .classed('framed', isFramed) .classed('preset-icon-iD', isiDIcon); diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 65b6a6c50..1674ea7ef 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -88,31 +88,16 @@ export function uiSearchAdd(context) { context.features().on('change.search-add', updateForFeatureHiddenState); } - function supportedGeometry(preset) { - return preset.geometry.filter(function(geometry) { - return ['point', 'line', 'area'].indexOf(geometry) !== -1; - }).sort(); - } - function defaultGeometry(item) { - if (item.geometry.filter) { - var supportedGeom = supportedGeometry(item); - if (supportedGeom.length === 1) { - return supportedGeom[0]; - } - } else { - return item.geometry; - } - return 'point'; - } - function drawList(list, presets) { var collection = presets.collection.map(function(preset) { if (preset.members) { return CategoryItem(preset); } else if (preset.visible()) { - var supportedGeom = supportedGeometry(preset); - if (supportedGeom.length === 1) { + var supportedGeometry = preset.geometry.filter(function(geometry) { + return ['point', 'line', 'area'].indexOf(geometry) !== -1; + }).sort(); + if (supportedGeometry.length === 1) { return AddablePresetItem(preset, supportedGeom[0]); } return MultiGeometryPresetItem(preset, supportedGeom); @@ -155,7 +140,7 @@ export function uiSearchAdd(context) { row.each(function(d) { d3_select(this).call( uiPresetIcon() - .geometry(d.geometry || defaultGeometry(d.preset)) + .geometry(d.geometry) .preset(d.preset) .sizeClass('small') ); From b8a1a653160ee64aaa6ced4537a6fb7aacb403d3 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 15:06:45 -0500 Subject: [PATCH 17/65] Fix preset icon misalignment due to rounding errors --- css/80_app.css | 47 +++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index ce2dec94e..e28c32397 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1100,7 +1100,7 @@ a.hide-toggle { .preset-icon-point-border path { stroke: #333; - stroke-width: 1.5; + stroke-width: 1.2; fill: transparent; } @@ -1162,44 +1162,31 @@ a.hide-toggle { height:100%; position: absolute; z-index: 1; + transform: scale(0.48); } .preset-icon .icon { position: absolute; margin: auto; - top: 26%; left: 0; right: 0; - width: 48%; - height: 48%; -} -.preset-icon-container.small .preset-icon.point-geom .icon { - width: 32%; - height: 32%; - top: 30%; -} - -.preset-icon.framed .icon { - top: 30%; - width: 40%; - height: 40%; -} -.preset-icon.framed.line-geom .icon { - top: 20%; -} - -.preset-icon-iD .icon { - top: 0; - height: 100%; width: 100%; + height: 100%; } - -.preset-icon-iD.framed .icon { - top: 13%; - width: 74%; - height: 74%; +.preset-icon-container.small .preset-icon.point-geom { + transform: translateY(-5%) scale(0.32); } -.preset-icon-iD.framed.line-geom .icon { - top: 3%; +.preset-icon.framed { + transform: scale(0.4); +} +.preset-icon.framed.line-geom { + top: 20%; + transform: translateY(-30%) scale(0.4); +} +.preset-icon-iD { + transform: scale(1); +} +.preset-icon-iD.framed.line-geom { + transform: translateY(-30%) scale(0.74); } .preset-list-button .label { From 971b901b211cdab08183c7e23099b33934027586 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 15:37:15 -0500 Subject: [PATCH 18/65] Add `1` as the hotkey for focusing the search bar --- modules/modes/add_area.js | 3 +-- modules/modes/add_line.js | 3 +-- modules/modes/add_note.js | 2 +- modules/modes/add_point.js | 3 +-- modules/ui/search_add.js | 11 +++++++++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/modes/add_area.js b/modules/modes/add_area.js index 55b344943..0be3637db 100644 --- a/modules/modes/add_area.js +++ b/modules/modes/add_area.js @@ -15,8 +15,7 @@ export function modeAddArea(context, customMode) { id: 'add-area', button: 'area', title: t('modes.add_area.title'), - description: t('modes.add_area.description'), - key: '3' + description: t('modes.add_area.description') }; var behavior = behaviorAddWay(context) diff --git a/modules/modes/add_line.js b/modules/modes/add_line.js index 82f4578ae..032c96e3b 100644 --- a/modules/modes/add_line.js +++ b/modules/modes/add_line.js @@ -15,8 +15,7 @@ export function modeAddLine(context, customMode) { id: 'add-line', button: 'line', title: t('modes.add_line.title'), - description: t('modes.add_line.description'), - key: '2' + description: t('modes.add_line.description') }; var behavior = behaviorAddWay(context) diff --git a/modules/modes/add_note.js b/modules/modes/add_note.js index 8f7f065a0..c8d1376c4 100644 --- a/modules/modes/add_note.js +++ b/modules/modes/add_note.js @@ -11,7 +11,7 @@ export function modeAddNote(context) { button: 'note', title: t('modes.add_note.title'), description: t('modes.add_note.description'), - key: '4' + key: 'N' }; var behavior = behaviorDraw(context) diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index c8d98b165..b9dbf7057 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -11,8 +11,7 @@ export function modeAddPoint(context, customMode) { id: 'add-point', button: 'point', title: t('modes.add_point.title'), - description: t('modes.add_point.description'), - key: '1' + description: t('modes.add_point.description') }; var behavior = behaviorDraw(context) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 1674ea7ef..0feb836d0 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -44,6 +44,7 @@ export function uiSearchAdd(context) { .call(utilNoAuto) .on('focus', function() { search.attr('focusing', true); + search.node().setSelectionRange(0, search.property('value').length); popover.classed('hide', false); }) .on('blur', function() { @@ -86,6 +87,12 @@ export function uiSearchAdd(context) { //.call(drawList, context.presets().defaults(geometry, 36)); context.features().on('change.search-add', updateForFeatureHiddenState); + + context.keybinding().on('1', function() { + search.node().focus(); + d3_event.preventDefault(); + d3_event.stopPropagation(); + }); } function drawList(list, presets) { @@ -98,9 +105,9 @@ export function uiSearchAdd(context) { return ['point', 'line', 'area'].indexOf(geometry) !== -1; }).sort(); if (supportedGeometry.length === 1) { - return AddablePresetItem(preset, supportedGeom[0]); + return AddablePresetItem(preset, supportedGeometry[0]); } - return MultiGeometryPresetItem(preset, supportedGeom); + return MultiGeometryPresetItem(preset, supportedGeometry); } }); From ad58779d09f46e6084c47c8ba08054fb1f34bf2a Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 16:52:24 -0500 Subject: [PATCH 19/65] Press return to choose the first list item in search-to-add field --- css/80_app.css | 4 ++++ modules/ui/preset_list.js | 2 +- modules/ui/search_add.js | 13 +++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index e28c32397..1bb832f7f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -674,6 +674,10 @@ button.add-note svg.icon { top: 0; left: 0; } +.search-add .list-item button.choose:hover, +.search-add .list-item button.choose:focus { + background: #E6ECFF; +} .search-add .list-item button.choose.disabled { background-color: #ececec; } diff --git a/modules/ui/preset_list.js b/modules/ui/preset_list.js index 51c9f3a09..8aecda79b 100644 --- a/modules/ui/preset_list.js +++ b/modules/ui/preset_list.js @@ -91,7 +91,7 @@ export function uiPresetList(context) { var value = search.property('value'); if (d3_event.keyCode === 13 && value.length) { list.selectAll('.preset-list-item:first-child') - .each(function(d) { d.choose.call(d3_select(this).node()); }); + .each(function(d) { d.choose.call(this); }); } } diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 0feb836d0..0c6e1c734 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -42,9 +42,18 @@ export function uiSearchAdd(context) { .attr('placeholder', t('modes.add_feature.title')) .attr('type', 'search') .call(utilNoAuto) + .on('keypress', function() { + // enter/return + if (d3_event.keyCode === 13) { + popover.selectAll('.list > .list-item:first-child button.choose') + .each(function(d) { d.choose.call(this); }); + d3_event.preventDefault(); + d3_event.stopPropagation(); + } + }) .on('focus', function() { - search.attr('focusing', true); search.node().setSelectionRange(0, search.property('value').length); + search.attr('focusing', true); popover.classed('hide', false); }) .on('blur', function() { @@ -52,8 +61,8 @@ export function uiSearchAdd(context) { }) .on('click', function() { if (search.attr('focusing')) { - search.attr('focusing', null); search.node().setSelectionRange(0, search.property('value').length); + search.attr('focusing', null); d3_event.preventDefault(); d3_event.stopPropagation(); } From bc9c179a67eda3f1937ddcaab28a2db87f3852b9 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 2 Mar 2019 18:11:11 -0500 Subject: [PATCH 20/65] Fix search field selection upon focus --- modules/ui/search_add.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 0c6e1c734..2d62e1ecc 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -51,9 +51,19 @@ export function uiSearchAdd(context) { d3_event.stopPropagation(); } }) + .on('mousedown', function() { + search.attr('clicking', true); + }) + .on('mouseup', function() { + search.attr('clicking', null); + }) .on('focus', function() { - search.node().setSelectionRange(0, search.property('value').length); - search.attr('focusing', true); + if (search.attr('clicking')) { + search.attr('focusing', true); + search.attr('clicking', null); + } else { + search.node().setSelectionRange(0, search.property('value').length); + } popover.classed('hide', false); }) .on('blur', function() { @@ -63,8 +73,6 @@ export function uiSearchAdd(context) { if (search.attr('focusing')) { search.node().setSelectionRange(0, search.property('value').length); search.attr('focusing', null); - d3_event.preventDefault(); - d3_event.stopPropagation(); } }) .on('input', function () { From 2f40854780a412ccf710308a20ff00250ba7c8b3 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 08:41:48 -0500 Subject: [PATCH 21/65] Use the same point path for the point preset icon as in the map --- css/80_app.css | 2 +- modules/ui/preset_icon.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 1bb832f7f..5b9040ae5 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1177,7 +1177,7 @@ a.hide-toggle { height: 100%; } .preset-icon-container.small .preset-icon.point-geom { - transform: translateY(-5%) scale(0.32); + transform: translateY(-7%) scale(0.27); } .preset-icon.framed { transform: scale(0.4); diff --git a/modules/ui/preset_icon.js b/modules/ui/preset_icon.js index 52169811a..d7e5745ed 100644 --- a/modules/ui/preset_icon.js +++ b/modules/ui/preset_icon.js @@ -28,7 +28,6 @@ export function uiPresetIcon() { } function renderPointBorder(enter) { - var d = "M20,7 C14.4766667,7 10,10.207551 10,16.6166837 C10,23.0278061 20,33 20,33 C20,33 30,23.0278061 30,16.6166837 C30,10.207551 25.5233333,7 20,7 Z" var w = 40, h = 40; enter = enter .append('svg') @@ -38,7 +37,8 @@ export function uiPresetIcon() { .attr('viewBox', '0 0 ' + w + ' ' + h); enter.append('path') - .attr('d', d); + .attr('transform', 'translate(11.5, 8)') + .attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z'); } function renderCircleFill(fillEnter) { @@ -240,7 +240,7 @@ export function uiPresetIcon() { }); icon.selectAll('use') - .attr('href', '#' + picon + (isMaki ? '-15' : '')); + .attr('href', '#' + picon + (isMaki ? (isSmall() ? '-11' : '-15') : '')); } From 9f3329149c942854785c7f81086f07dfa85f9a36 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 10:03:05 -0500 Subject: [PATCH 22/65] Add basic arrow key navigation of the search-to-add bar --- css/80_app.css | 5 +++- modules/ui/preset_icon.js | 2 +- modules/ui/search_add.js | 62 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 5b9040ae5..a699a8479 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -676,7 +676,10 @@ button.add-note svg.icon { } .search-add .list-item button.choose:hover, .search-add .list-item button.choose:focus { - background: #E6ECFF; + background: #fff; +} +.search-add .list-item.focused:not(.disabled) button { + background: #e8ebff; } .search-add .list-item button.choose.disabled { background-color: #ececec; diff --git a/modules/ui/preset_icon.js b/modules/ui/preset_icon.js index d7e5745ed..9610be3e8 100644 --- a/modules/ui/preset_icon.js +++ b/modules/ui/preset_icon.js @@ -240,7 +240,7 @@ export function uiPresetIcon() { }); icon.selectAll('use') - .attr('href', '#' + picon + (isMaki ? (isSmall() ? '-11' : '-15') : '')); + .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : '')); } diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 2d62e1ecc..7c3ae2bfc 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -45,12 +45,52 @@ export function uiSearchAdd(context) { .on('keypress', function() { // enter/return if (d3_event.keyCode === 13) { - popover.selectAll('.list > .list-item:first-child button.choose') + popover.selectAll('.list .list-item.focused button.choose') .each(function(d) { d.choose.call(this); }); d3_event.preventDefault(); d3_event.stopPropagation(); } }) + .on('keydown', function(){ + // up/down arrow key navigation + + if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + + var nextFocus, + priorFocus = popover.selectAll('.list .list-item.focused'); + if (priorFocus.empty()) { + nextFocus = popover.selectAll('.list > .list-item:first-child'); + } else { + nextFocus = popover.selectAll('.list .list-item.focused + .list-item'); + if (nextFocus.empty()) { + nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling) + .selectAll('.list-item:first-child'); + } + } + if (!nextFocus.empty()) { + focusListItem(nextFocus); + priorFocus.classed('focused', false); + } + + } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + + var priorFocus = popover.selectAll('.list .list-item.focused'); + if (!priorFocus.empty()) { + var nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling); + if (!nextFocus.empty() && !nextFocus.classed('list-item')) { + nextFocus = nextFocus.selectAll('.list-item:last-child'); + } + if (!nextFocus.empty()) { + focusListItem(nextFocus); + priorFocus.classed('focused', false); + } + } + } + }) .on('mousedown', function() { search.attr('clicking', true); }) @@ -81,6 +121,9 @@ export function uiSearchAdd(context) { if (value.length) { var results = presets.search(value); list.call(drawList, results); + popover.selectAll('.list .list-item.focused') + .classed('focused', false); + focusListItem(popover.selectAll('.list > .list-item:first-child')); } else { //list.call(drawList, context.presets().defaults(geometry, 36)); } @@ -112,6 +155,13 @@ export function uiSearchAdd(context) { }); } + function focusListItem(selection) { + if (!selection.empty()) { + selection.classed('focused', true); + //selection.nodes()[0].scrollIntoView(); + } + } + function drawList(list, presets) { var collection = presets.collection.map(function(preset) { @@ -153,6 +203,16 @@ export function uiSearchAdd(context) { id += '-' + d.geometry; } return id; + }) + .on('mouseover', function(d) { + list.selectAll('.list-item.focused') + .classed('focused', false); + d3_select(this) + .classed('focused', true); + }) + .on('mouseout', function(d) { + d3_select(this) + .classed('focused', false); }); row.append('button') From 7bf73111c4da5837b92d6258dcf710bc15c8afa9 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 10:28:13 -0500 Subject: [PATCH 23/65] Don't use double point icons Remove subsections when reloading --- modules/ui/preset_icon.js | 32 +++++++++++++++++++------------- modules/ui/search_add.js | 1 + 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/modules/ui/preset_icon.js b/modules/ui/preset_icon.js index 9610be3e8..9bcd404fb 100644 --- a/modules/ui/preset_icon.js +++ b/modules/ui/preset_icon.js @@ -23,6 +23,8 @@ export function uiPresetIcon() { return 'iD-other-line'; else if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex'; + else if (isSmall() && geom === 'point') + return null; else return 'maki-marker-stroked'; } @@ -148,9 +150,9 @@ export function uiPresetIcon() { var p = preset.apply(this, arguments); var geom = geometry ? geometry.apply(this, arguments) : null; var picon = getIcon(p, geom); - var isMaki = /^maki-/.test(picon); - var isTemaki = /^temaki-/.test(picon); - var isFa = /^fa[srb]-/.test(picon); + var isMaki = picon ? /^maki-/.test(picon) : false; + var isTemaki = picon ? /^temaki-/.test(picon) : false; + var isFa = picon ? /^fa[srb]-/.test(picon) : false; var isiDIcon = !(isMaki || isTemaki || isFa); var isCategory = !p.setTags; var drawLine = geom === 'line' && !isCategory; @@ -229,18 +231,22 @@ export function uiPresetIcon() { .call(svgIcon('')) .merge(icon); - icon - .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')) - .classed('framed', isFramed) - .classed('preset-icon-iD', isiDIcon); + if (picon) { - icon.selectAll('svg') - .attr('class', function() { - return 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses); - }); + icon + .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')) + .classed('framed', isFramed) + .classed('preset-icon-iD', isiDIcon); + + icon.selectAll('svg') + .attr('class', function() { + return 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses); + }); + + icon.selectAll('use') + .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : '')); + } - icon.selectAll('use') - .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : '')); } diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 7c3ae2bfc..bf50cfbd1 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -119,6 +119,7 @@ export function uiSearchAdd(context) { var value = search.property('value'); //list.classed('filtered', value.length); if (value.length) { + popover.selectAll('.subsection').remove(); var results = presets.search(value); list.call(drawList, results); popover.selectAll('.list .list-item.focused') From fa2e718edfbe8450b0f0648b6c6efe614ee8c285 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 10:51:23 -0500 Subject: [PATCH 24/65] Keep the focused item visible when navigating the search-to-add results with the arrow keys --- modules/ui/search_add.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index bf50cfbd1..708df885f 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -159,7 +159,17 @@ export function uiSearchAdd(context) { function focusListItem(selection) { if (!selection.empty()) { selection.classed('focused', true); - //selection.nodes()[0].scrollIntoView(); + var node = selection.nodes()[0]; + var popoverNode = popover.node(); + var nodeRect = node.getBoundingClientRect(); + + // scroll to keep the focused item visible + if (node.offsetTop < popoverNode.scrollTop) { + popoverNode.scrollTop = node.offsetTop; + + } else if (node.offsetTop + node.offsetHeight > popoverNode.scrollTop + popoverNode.offsetHeight) { + popoverNode.scrollTop = node.offsetTop + node.offsetHeight - popoverNode.offsetHeight; + } } } From 9a7ad8a5cb0568eeab201bb6c960a060941dfa45 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 12:11:31 -0500 Subject: [PATCH 25/65] Add disclosure arrow to expandable search results --- css/80_app.css | 8 +++++--- modules/ui/search_add.js | 31 ++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index a699a8479..0ac12ee94 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -659,9 +659,11 @@ button.add-note svg.icon { .search-add .list-item.disabled .label { opacity: 0.55; } -.search-add .subsection .list-item .label { - /*font-weight: normal; - letter-spacing: 0.1;*/ +[dir='ltr'] .search-add .list-item .label .icon.inline { + margin-left: 0; +} +[dir='rtl'] .search-add .list-item .label .icon.inline { + margin-right: 0; } .search-add .list-item > *:not(button) { pointer-events: none; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 708df885f..ec258fd96 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -54,12 +54,13 @@ export function uiSearchAdd(context) { .on('keydown', function(){ // up/down arrow key navigation + var nextFocus, + priorFocus; if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) { d3_event.preventDefault(); d3_event.stopPropagation(); - var nextFocus, - priorFocus = popover.selectAll('.list .list-item.focused'); + priorFocus = popover.selectAll('.list .list-item.focused'); if (priorFocus.empty()) { nextFocus = popover.selectAll('.list > .list-item:first-child'); } else { @@ -78,9 +79,9 @@ export function uiSearchAdd(context) { d3_event.preventDefault(); d3_event.stopPropagation(); - var priorFocus = popover.selectAll('.list .list-item.focused'); + priorFocus = popover.selectAll('.list .list-item.focused'); if (!priorFocus.empty()) { - var nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling); + nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling); if (!nextFocus.empty() && !nextFocus.classed('list-item')) { nextFocus = nextFocus.selectAll('.list-item:last-child'); } @@ -240,8 +241,17 @@ export function uiSearchAdd(context) { .sizeClass('small') ); }); - row.append('div') - .attr('class', 'label') + var label = row.append('div') + .attr('class', 'label'); + + label.each(function(d) { + if (!d.geometry) { + d3_select(this) + .call(svgIcon((textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline')); + } + }); + + label.append('span') .text(function(d) { if (d.isSubitem) { return t('modes.add_' + d.geometry + '.title'); @@ -307,15 +317,22 @@ export function uiSearchAdd(context) { var selection = d3_select(this); if (selection.classed('disabled')) return; + var listItemNode = selection.node().closest('.list-item'); + var shouldExpand = !selection.classed('expanded'); selection.classed('expanded', shouldExpand); + var iconName = shouldExpand ? + '#iD-icon-down' : (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'); + d3_select(listItemNode).selectAll('.label svg.icon use') + .attr('href', iconName); + if (shouldExpand) { var subitems = geometries.map(function(geometry) { return AddablePresetItem(preset, geometry, true); }); - var selector = '#' + selection.node().closest('.list-item').id + ' + *'; + var selector = '#' + listItemNode.id + ' + *'; subsection = d3_selectAll('.search-add .popover .list').insert('div', selector) .attr('class', 'subsection'); var subitemsEnter = subsection.selectAll('.list-item') From 0aa95bf8b0f36429d6dc2c8eb123abffcd3692e0 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 12:45:38 -0500 Subject: [PATCH 26/65] Make category items expandable --- modules/ui/search_add.js | 77 +++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index ec258fd96..8f6b92a2e 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -298,51 +298,70 @@ export function uiSearchAdd(context) { }); } + function chooseExpandable(item, itemSelection) { + + var shouldExpand = !itemSelection.classed('expanded'); + + itemSelection.classed('expanded', shouldExpand); + + var iconName = shouldExpand ? + '#iD-icon-down' : (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'); + itemSelection.selectAll('.label svg.icon use') + .attr('href', iconName); + + if (shouldExpand) { + var subitems = item.subitems(); + var selector = '#' + itemSelection.node().id + ' + *'; + item.subsection = d3_selectAll('.search-add .popover .list').insert('div', selector) + .attr('class', 'subsection'); + var subitemsEnter = item.subsection.selectAll('.list-item') + .data(subitems) + .enter(); + drawItems(subitemsEnter); + updateForFeatureHiddenState(); + } else { + item.subsection.remove(); + } + } + function CategoryItem(preset) { var item = {}; + item.subsection = d3_select(null); item.preset = preset; item.choose = function() { + var selection = d3_select(this); + if (selection.classed('disabled')) return; + chooseExpandable(item, d3_select(selection.node().closest('.list-item'))); }; + item.subitems = function() { + return preset.members.collection.map(function(preset) { + var supportedGeometry = preset.geometry.filter(function(geometry) { + return ['point', 'line', 'area'].indexOf(geometry) !== -1; + }).sort(); + if (supportedGeometry.length === 1) { + return AddablePresetItem(preset, supportedGeometry[0]); + } + return MultiGeometryPresetItem(preset, supportedGeometry); + }); + } return item; } function MultiGeometryPresetItem(preset, geometries) { - var subsection = d3_select(null); - var item = {}; + item.subsection = d3_select(null); item.preset = preset; item.geometries = geometries; item.choose = function() { var selection = d3_select(this); if (selection.classed('disabled')) return; - - var listItemNode = selection.node().closest('.list-item'); - - var shouldExpand = !selection.classed('expanded'); - - selection.classed('expanded', shouldExpand); - - var iconName = shouldExpand ? - '#iD-icon-down' : (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'); - d3_select(listItemNode).selectAll('.label svg.icon use') - .attr('href', iconName); - - if (shouldExpand) { - var subitems = geometries.map(function(geometry) { - return AddablePresetItem(preset, geometry, true); - }); - var selector = '#' + listItemNode.id + ' + *'; - subsection = d3_selectAll('.search-add .popover .list').insert('div', selector) - .attr('class', 'subsection'); - var subitemsEnter = subsection.selectAll('.list-item') - .data(subitems) - .enter(); - drawItems(subitemsEnter); - updateForFeatureHiddenState(); - } else { - subsection.remove(); - } + chooseExpandable(item, d3_select(selection.node().closest('.list-item'))); + }; + item.subitems = function() { + return geometries.map(function(geometry) { + return AddablePresetItem(preset, geometry, true); + }); }; return item; } From 734731bfda4e3049911fcbc91c6ef572d702d711 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 13:39:30 -0500 Subject: [PATCH 27/65] Make categories not geometry-specific --- css/80_app.css | 3 + data/presets.yaml | 14 +---- data/presets/categories.json | 60 ++++--------------- data/presets/categories/barrier.json | 1 - data/presets/categories/building.json | 1 - data/presets/categories/golf-line.json | 11 ---- .../categories/{golf-area.json => golf.json} | 7 ++- data/presets/categories/landuse.json | 1 - data/presets/categories/natural-line.json | 9 --- data/presets/categories/natural-point.json | 11 ---- .../{natural-area.json => natural.json} | 9 ++- data/presets/categories/path.json | 1 - data/presets/categories/rail.json | 8 ++- data/presets/categories/restriction.json | 1 - data/presets/categories/road.json | 1 - data/presets/categories/route.json | 1 - data/presets/categories/utility.json | 1 - data/presets/categories/water-area.json | 11 ---- .../{water-line.json => water.json} | 7 ++- data/presets/defaults.json | 10 ++-- dist/locales/en.json | 18 +----- modules/presets/category.js | 11 ++++ modules/ui/preset_list.js | 5 +- 23 files changed, 63 insertions(+), 139 deletions(-) delete mode 100644 data/presets/categories/golf-line.json rename data/presets/categories/{golf-area.json => golf.json} (66%) delete mode 100644 data/presets/categories/natural-line.json delete mode 100644 data/presets/categories/natural-point.json rename data/presets/categories/{natural-area.json => natural.json} (63%) delete mode 100644 data/presets/categories/water-area.json rename data/presets/categories/{water-line.json => water.json} (56%) diff --git a/css/80_app.css b/css/80_app.css index 0ac12ee94..b7c914476 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1194,6 +1194,9 @@ a.hide-toggle { .preset-icon-iD { transform: scale(1); } +.preset-icon-iD.framed { + transform: scale(0.74); +} .preset-icon-iD.framed.line-geom { transform: translateY(-30%) scale(0.74); } diff --git a/data/presets.yaml b/data/presets.yaml index 10167ae48..28b08d5ff 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -5,17 +5,11 @@ en: name: Barrier Features category-building: name: Building Features - category-golf-area: - name: Golf Features - category-golf-line: + category-golf: name: Golf Features category-landuse: name: Land Use Features - category-natural-area: - name: Natural Features - category-natural-line: - name: Natural Features - category-natural-point: + category-natural: name: Natural Features category-path: name: Path Features @@ -29,9 +23,7 @@ en: name: Route Features category-utility: name: Utility Features - category-water-area: - name: Water Features - category-water-line: + category-water: name: Water Features fields: access: diff --git a/data/presets/categories.json b/data/presets/categories.json index b4343a4a2..463cad012 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -2,7 +2,6 @@ "categories": { "category-barrier": { "icon": "maki-roadblock", - "geometry": "line", "name": "Barrier Features", "members": [ "barrier/fence", @@ -16,7 +15,6 @@ }, "category-building": { "icon": "maki-building", - "geometry": "area", "name": "Building Features", "members": [ "building", @@ -29,9 +27,8 @@ "building/residential" ] }, - "category-golf-area": { + "category-golf": { "icon": "maki-golf", - "geometry": "area", "name": "Golf Features", "members": [ "golf/fairway", @@ -41,14 +38,7 @@ "golf/bunker", "golf/tee", "golf/water_hazard", - "golf/driving_range" - ] - }, - "category-golf-line": { - "icon": "maki-golf", - "geometry": "line", - "name": "Golf Features", - "members": [ + "golf/driving_range", "golf/hole", "golf/cartpath", "golf/cartpath_service", @@ -57,7 +47,6 @@ }, "category-landuse": { "icon": "maki-landuse", - "geometry": "area", "name": "Land Use Features", "members": [ "landuse/residential", @@ -74,9 +63,8 @@ "landuse/religious" ] }, - "category-natural-area": { + "category-natural": { "icon": "maki-natural", - "geometry": "area", "name": "Natural Features", "members": [ "natural/water", @@ -88,20 +76,9 @@ "natural/bare_rock", "natural/beach", "natural/cave_entrance", - "natural/glacier" - ] - }, - "category-natural-line": { - "icon": "maki-natural", - "geometry": "line", - "name": "Natural Features", - "members": ["natural/coastline", "natural/tree_row"] - }, - "category-natural-point": { - "icon": "maki-natural", - "geometry": "point", - "name": "Natural Features", - "members": [ + "natural/glacier", + "natural/coastline", + "natural/tree_row", "natural/peak", "natural/cliff", "natural/beach", @@ -110,7 +87,6 @@ }, "category-path": { "icon": "iD-category-path", - "geometry": "line", "name": "Path Features", "members": [ "highway/footway/marked", @@ -125,20 +101,21 @@ }, "category-rail": { "icon": "iD-category-rail", - "geometry": "line", "name": "Rail Features", "members": [ "railway/rail", "railway/subway", "railway/tram", - "railway/monorail", "railway/disused", - "railway/abandoned" + "railway/level_crossing", + "railway/crossing", + "railway/switch", + "railway/buffer_stop", + "railway/signal" ] }, "category-restriction": { "icon": "iD-restriction", - "geometry": "relation", "name": "Restriction Features", "members": [ "type/restriction/no_left_turn", @@ -154,7 +131,6 @@ }, "category-road": { "icon": "iD-category-roads", - "geometry": "line", "name": "Road Features", "members": [ "highway/residential", @@ -177,7 +153,6 @@ }, "category-route": { "icon": "iD-route", - "geometry": "relation", "name": "Route Features", "members": [ "type/route/road", @@ -201,7 +176,6 @@ }, "category-utility": { "icon": "iD-category-utility", - "geometry": "line", "name": "Utility Features", "members": [ "power/line", @@ -210,22 +184,14 @@ "power/cable/underground" ] }, - "category-water-area": { + "category-water": { "icon": "maki-water", - "geometry": "area", "name": "Water Features", "members": [ "natural/water/lake", "natural/water/pond", "natural/water/reservoir", - "natural/water" - ] - }, - "category-water-line": { - "icon": "iD-category-water", - "geometry": "line", - "name": "Water Features", - "members": [ + "natural/water", "waterway/river", "waterway/stream", "waterway/canal", diff --git a/data/presets/categories/barrier.json b/data/presets/categories/barrier.json index e3fb9e5fe..752db1d27 100644 --- a/data/presets/categories/barrier.json +++ b/data/presets/categories/barrier.json @@ -1,6 +1,5 @@ { "icon": "maki-roadblock", - "geometry": "line", "name": "Barrier Features", "members": [ "barrier/fence", diff --git a/data/presets/categories/building.json b/data/presets/categories/building.json index 05a116721..0b020a285 100644 --- a/data/presets/categories/building.json +++ b/data/presets/categories/building.json @@ -1,6 +1,5 @@ { "icon": "maki-building", - "geometry": "area", "name": "Building Features", "members": [ "building", diff --git a/data/presets/categories/golf-line.json b/data/presets/categories/golf-line.json deleted file mode 100644 index f108f968d..000000000 --- a/data/presets/categories/golf-line.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "icon": "maki-golf", - "geometry": "line", - "name": "Golf Features", - "members": [ - "golf/hole", - "golf/cartpath", - "golf/cartpath_service", - "golf/path" - ] -} diff --git a/data/presets/categories/golf-area.json b/data/presets/categories/golf.json similarity index 66% rename from data/presets/categories/golf-area.json rename to data/presets/categories/golf.json index 4e43047b9..8b175b4ed 100644 --- a/data/presets/categories/golf-area.json +++ b/data/presets/categories/golf.json @@ -1,6 +1,5 @@ { "icon": "maki-golf", - "geometry": "area", "name": "Golf Features", "members": [ "golf/fairway", @@ -10,6 +9,10 @@ "golf/bunker", "golf/tee", "golf/water_hazard", - "golf/driving_range" + "golf/driving_range", + "golf/hole", + "golf/cartpath", + "golf/cartpath_service", + "golf/path" ] } diff --git a/data/presets/categories/landuse.json b/data/presets/categories/landuse.json index 7d92de866..e4e6ed9c5 100644 --- a/data/presets/categories/landuse.json +++ b/data/presets/categories/landuse.json @@ -1,6 +1,5 @@ { "icon": "maki-landuse", - "geometry": "area", "name": "Land Use Features", "members": [ "landuse/residential", diff --git a/data/presets/categories/natural-line.json b/data/presets/categories/natural-line.json deleted file mode 100644 index 12f1b1705..000000000 --- a/data/presets/categories/natural-line.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "icon": "maki-natural", - "geometry": "line", - "name": "Natural Features", - "members": [ - "natural/coastline", - "natural/tree_row" - ] -} diff --git a/data/presets/categories/natural-point.json b/data/presets/categories/natural-point.json deleted file mode 100644 index e10d5a1ef..000000000 --- a/data/presets/categories/natural-point.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "icon": "maki-natural", - "geometry": "point", - "name": "Natural Features", - "members": [ - "natural/peak", - "natural/cliff", - "natural/beach", - "natural/cave_entrance" - ] -} diff --git a/data/presets/categories/natural-area.json b/data/presets/categories/natural.json similarity index 63% rename from data/presets/categories/natural-area.json rename to data/presets/categories/natural.json index 607f3b11d..020c8e9ae 100644 --- a/data/presets/categories/natural-area.json +++ b/data/presets/categories/natural.json @@ -1,6 +1,5 @@ { "icon": "maki-natural", - "geometry": "area", "name": "Natural Features", "members": [ "natural/water", @@ -12,6 +11,12 @@ "natural/bare_rock", "natural/beach", "natural/cave_entrance", - "natural/glacier" + "natural/glacier", + "natural/coastline", + "natural/tree_row", + "natural/peak", + "natural/cliff", + "natural/beach", + "natural/cave_entrance" ] } diff --git a/data/presets/categories/path.json b/data/presets/categories/path.json index 275e726f3..df2713284 100644 --- a/data/presets/categories/path.json +++ b/data/presets/categories/path.json @@ -1,6 +1,5 @@ { "icon": "iD-category-path", - "geometry": "line", "name": "Path Features", "members": [ "highway/footway/marked", diff --git a/data/presets/categories/rail.json b/data/presets/categories/rail.json index f7489a689..c37347bba 100644 --- a/data/presets/categories/rail.json +++ b/data/presets/categories/rail.json @@ -1,13 +1,15 @@ { "icon": "iD-category-rail", - "geometry": "line", "name": "Rail Features", "members": [ "railway/rail", "railway/subway", "railway/tram", - "railway/monorail", "railway/disused", - "railway/abandoned" + "railway/level_crossing", + "railway/crossing", + "railway/switch", + "railway/buffer_stop", + "railway/signal" ] } diff --git a/data/presets/categories/restriction.json b/data/presets/categories/restriction.json index bf5bb5d06..52d52f2f3 100644 --- a/data/presets/categories/restriction.json +++ b/data/presets/categories/restriction.json @@ -1,6 +1,5 @@ { "icon": "iD-restriction", - "geometry": "relation", "name": "Restriction Features", "members": [ "type/restriction/no_left_turn", diff --git a/data/presets/categories/road.json b/data/presets/categories/road.json index eaabdf505..eedbb8b71 100644 --- a/data/presets/categories/road.json +++ b/data/presets/categories/road.json @@ -1,6 +1,5 @@ { "icon": "iD-category-roads", - "geometry": "line", "name": "Road Features", "members": [ "highway/residential", diff --git a/data/presets/categories/route.json b/data/presets/categories/route.json index 6c05b0b03..35a16337f 100644 --- a/data/presets/categories/route.json +++ b/data/presets/categories/route.json @@ -1,6 +1,5 @@ { "icon": "iD-route", - "geometry": "relation", "name": "Route Features", "members": [ "type/route/road", diff --git a/data/presets/categories/utility.json b/data/presets/categories/utility.json index 63aae92d0..1509c4ab1 100644 --- a/data/presets/categories/utility.json +++ b/data/presets/categories/utility.json @@ -1,6 +1,5 @@ { "icon": "iD-category-utility", - "geometry": "line", "name": "Utility Features", "members": [ "power/line", diff --git a/data/presets/categories/water-area.json b/data/presets/categories/water-area.json deleted file mode 100644 index 99c83125a..000000000 --- a/data/presets/categories/water-area.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "icon": "maki-water", - "geometry": "area", - "name": "Water Features", - "members": [ - "natural/water/lake", - "natural/water/pond", - "natural/water/reservoir", - "natural/water" - ] -} diff --git a/data/presets/categories/water-line.json b/data/presets/categories/water.json similarity index 56% rename from data/presets/categories/water-line.json rename to data/presets/categories/water.json index 7a7c8c3ff..06c7f4818 100644 --- a/data/presets/categories/water-line.json +++ b/data/presets/categories/water.json @@ -1,8 +1,11 @@ { - "icon": "iD-category-water", - "geometry": "line", + "icon": "maki-water", "name": "Water Features", "members": [ + "natural/water/lake", + "natural/water/pond", + "natural/water/reservoir", + "natural/water", "waterway/river", "waterway/stream", "waterway/canal", diff --git a/data/presets/defaults.json b/data/presets/defaults.json index 71247c4da..8d2de9016 100644 --- a/data/presets/defaults.json +++ b/data/presets/defaults.json @@ -3,8 +3,8 @@ "area": [ "category-landuse", "category-building", - "category-water-area", - "category-natural-area", + "category-water", + "category-natural", "leisure/park", "amenity/hospital", "amenity/place_of_worship", @@ -16,14 +16,14 @@ "category-road", "category-rail", "category-path", - "category-water-line", + "category-water", "category-barrier", - "category-natural-line", + "category-natural", "category-utility", "line" ], "point": [ - "category-natural-point", + "category-natural", "leisure/park", "amenity/hospital", "amenity/place_of_worship", diff --git a/dist/locales/en.json b/dist/locales/en.json index 50f0bea75..58f9d3a84 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2065,22 +2065,13 @@ "category-building": { "name": "Building Features" }, - "category-golf-area": { - "name": "Golf Features" - }, - "category-golf-line": { + "category-golf": { "name": "Golf Features" }, "category-landuse": { "name": "Land Use Features" }, - "category-natural-area": { - "name": "Natural Features" - }, - "category-natural-line": { - "name": "Natural Features" - }, - "category-natural-point": { + "category-natural": { "name": "Natural Features" }, "category-path": { @@ -2101,10 +2092,7 @@ "category-utility": { "name": "Utility Features" }, - "category-water-area": { - "name": "Water Features" - }, - "category-water-line": { + "category-water": { "name": "Water Features" } }, diff --git a/modules/presets/category.js b/modules/presets/category.js index 3a538aae0..582ef71ca 100644 --- a/modules/presets/category.js +++ b/modules/presets/category.js @@ -14,6 +14,17 @@ export function presetCategory(id, category, all) { })); + category.geometry = category.members.collection.reduce(function(geometries, preset) { + for (var index in preset.geometry) { + var geometry = preset.geometry[index]; + if (geometries.indexOf(geometry) === -1) { + geometries.push(geometry); + } + } + return geometries; + }, []); + + category.matchGeometry = function(geometry) { return category.geometry.indexOf(geometry) >= 0; }; diff --git a/modules/ui/preset_list.js b/modules/ui/preset_list.js index 8aecda79b..1e8ab2d46 100644 --- a/modules/ui/preset_list.js +++ b/modules/ui/preset_list.js @@ -339,11 +339,12 @@ export function uiPresetList(context) { .style('padding-bottom', '0px'); } else { shown = true; - sublist.call(drawList, preset.members); + var members = preset.members.matchGeometry(context.geometry(_entityID)); + sublist.call(drawList, members); box.transition() .duration(200) .style('opacity', '1') - .style('max-height', 200 + preset.members.collection.length * 190 + 'px') + .style('max-height', 200 + members.collection.length * 190 + 'px') .style('padding-bottom', '10px'); } }; From 929a27b384edd89f00ac58dc8225d0d82e406381 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 13:56:34 -0500 Subject: [PATCH 28/65] Allow arrow key navigation out of expanded subsections --- css/80_app.css | 2 +- modules/ui/search_add.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index b7c914476..2613a02fc 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -680,7 +680,7 @@ button.add-note svg.icon { .search-add .list-item button.choose:focus { background: #fff; } -.search-add .list-item.focused:not(.disabled) button { +.search-add .list-item.focused:not(.disabled) button.choose { background: #e8ebff; } .search-add .list-item button.choose.disabled { diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 8f6b92a2e..258f50cd6 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -69,6 +69,12 @@ export function uiSearchAdd(context) { nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling) .selectAll('.list-item:first-child'); } + if (nextFocus.empty()) { + var parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); + if (parentSubsection && parentSubsection.nextElementSibling) { + nextFocus = d3_select(parentSubsection.nextElementSibling); + } + } } if (!nextFocus.empty()) { focusListItem(nextFocus); @@ -81,10 +87,17 @@ export function uiSearchAdd(context) { priorFocus = popover.selectAll('.list .list-item.focused'); if (!priorFocus.empty()) { + nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling); if (!nextFocus.empty() && !nextFocus.classed('list-item')) { nextFocus = nextFocus.selectAll('.list-item:last-child'); } + if (nextFocus.empty()) { + var parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); + if (parentSubsection && parentSubsection.previousElementSibling) { + nextFocus = d3_select(parentSubsection.previousElementSibling); + } + } if (!nextFocus.empty()) { focusListItem(nextFocus); priorFocus.classed('focused', false); From c8dc155a274b22523dfe0c4f77888c8c160da406 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 14:08:55 -0500 Subject: [PATCH 29/65] Add expansion of multigeometry features within categories --- modules/ui/search_add.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 258f50cd6..1cf0bc396 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -28,9 +28,11 @@ export function uiSearchAdd(context) { var presets; var search = d3_select(null), popover = d3_select(null), list = d3_select(null); + var shownGeometry = ['point', 'line', 'area']; + function searchAdd(selection) { - presets = context.presets().matchAnyGeometry(['point', 'line', 'area']); + presets = context.presets().matchAnyGeometry(shownGeometry); var searchWrap = selection .append('div') @@ -194,7 +196,7 @@ export function uiSearchAdd(context) { return CategoryItem(preset); } else if (preset.visible()) { var supportedGeometry = preset.geometry.filter(function(geometry) { - return ['point', 'line', 'area'].indexOf(geometry) !== -1; + return shownGeometry.indexOf(geometry) !== -1; }).sort(); if (supportedGeometry.length === 1) { return AddablePresetItem(preset, supportedGeometry[0]); @@ -325,7 +327,7 @@ export function uiSearchAdd(context) { if (shouldExpand) { var subitems = item.subitems(); var selector = '#' + itemSelection.node().id + ' + *'; - item.subsection = d3_selectAll('.search-add .popover .list').insert('div', selector) + item.subsection = d3_select(itemSelection.node().parentElement).insert('div', selector) .attr('class', 'subsection'); var subitemsEnter = item.subsection.selectAll('.list-item') .data(subitems) @@ -347,9 +349,9 @@ export function uiSearchAdd(context) { chooseExpandable(item, d3_select(selection.node().closest('.list-item'))); }; item.subitems = function() { - return preset.members.collection.map(function(preset) { + return preset.members.matchAnyGeometry(shownGeometry).collection.map(function(preset) { var supportedGeometry = preset.geometry.filter(function(geometry) { - return ['point', 'line', 'area'].indexOf(geometry) !== -1; + return shownGeometry.indexOf(geometry) !== -1; }).sort(); if (supportedGeometry.length === 1) { return AddablePresetItem(preset, supportedGeometry[0]); From 1a6d42c9cd6ab198b5136b7fe523f1b820865d44 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 14:17:33 -0500 Subject: [PATCH 30/65] Update water categories and icons --- data/presets/categories.json | 11 +++++++---- data/presets/categories/water.json | 11 +++++++---- data/presets/presets.json | 6 +++--- data/presets/presets/natural/water/canal.json | 2 +- data/presets/presets/natural/water/river.json | 2 +- data/presets/presets/natural/water/stream.json | 2 +- data/taginfo.json | 6 +++--- 7 files changed, 23 insertions(+), 17 deletions(-) diff --git a/data/presets/categories.json b/data/presets/categories.json index 463cad012..e3e48b015 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -185,18 +185,21 @@ ] }, "category-water": { - "icon": "maki-water", + "icon": "iD-category-water", "name": "Water Features", "members": [ "natural/water/lake", "natural/water/pond", "natural/water/reservoir", "natural/water", - "waterway/river", "waterway/stream", + "natural/water/stream", + "waterway/drain", + "waterway/river", + "natural/water/river", "waterway/canal", - "waterway/ditch", - "waterway/drain" + "natural/water/canal", + "waterway/ditch" ] } } diff --git a/data/presets/categories/water.json b/data/presets/categories/water.json index 06c7f4818..e45e89ce6 100644 --- a/data/presets/categories/water.json +++ b/data/presets/categories/water.json @@ -1,15 +1,18 @@ { - "icon": "maki-water", + "icon": "iD-category-water", "name": "Water Features", "members": [ "natural/water/lake", "natural/water/pond", "natural/water/reservoir", "natural/water", - "waterway/river", "waterway/stream", + "natural/water/stream", + "waterway/drain", + "waterway/river", + "natural/water/river", "waterway/canal", - "waterway/ditch", - "waterway/drain" + "natural/water/canal", + "waterway/ditch" ] } diff --git a/data/presets/presets.json b/data/presets/presets.json index 2624a4dd6..1f1e58ea6 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -677,12 +677,12 @@ "natural/volcano": {"icon": "maki-volcano", "fields": ["name", "elevation", "volcano/status", "volcano/type"], "geometry": ["point", "vertex"], "tags": {"natural": "volcano"}, "terms": ["mountain", "crater"], "name": "Volcano"}, "natural/water": {"icon": "maki-water", "fields": ["name", "water", "intermittent"], "moreFields": ["salt", "tidal"], "geometry": ["area"], "tags": {"natural": "water"}, "name": "Water"}, "natural/water/basin": {"icon": "maki-water", "fields": ["name", "basin", "intermittent_yes"], "geometry": ["area"], "tags": {"natural": "water", "water": "basin"}, "reference": {"key": "water", "value": "basin"}, "terms": ["detention", "drain", "overflow", "rain", "retention"], "name": "Basin"}, - "natural/water/canal": {"icon": "maki-water", "fields": ["{natural/water}", "salt"], "geometry": ["area"], "tags": {"natural": "water", "water": "canal"}, "reference": {"key": "water", "value": "canal"}, "name": "Canal"}, + "natural/water/canal": {"icon": "iD-waterway-canal", "fields": ["{natural/water}", "salt"], "geometry": ["area"], "tags": {"natural": "water", "water": "canal"}, "reference": {"key": "water", "value": "canal"}, "name": "Canal"}, "natural/water/lake": {"icon": "maki-water", "fields": ["{natural/water}", "salt", "tidal"], "geometry": ["area"], "tags": {"natural": "water", "water": "lake"}, "reference": {"key": "water", "value": "lake"}, "terms": ["lakelet", "loch", "mere"], "name": "Lake"}, "natural/water/pond": {"icon": "maki-water", "fields": ["{natural/water}", "salt"], "geometry": ["area"], "tags": {"natural": "water", "water": "pond"}, "reference": {"key": "water", "value": "pond"}, "terms": ["lakelet", "millpond", "tarn", "pool", "mere"], "name": "Pond"}, "natural/water/reservoir": {"icon": "maki-water", "geometry": ["area"], "tags": {"natural": "water", "water": "reservoir"}, "reference": {"key": "water", "value": "reservoir"}, "name": "Reservoir"}, - "natural/water/river": {"icon": "maki-water", "fields": ["{natural/water}", "tidal"], "geometry": ["area"], "tags": {"natural": "water", "water": "river"}, "reference": {"key": "water", "value": "river"}, "terms": ["beck", "branch", "brook", "course", "creek", "estuary", "rill", "riverbank", "rivulet", "run", "runnel", "stream", "tributary", "watercourse"], "name": "River"}, - "natural/water/stream": {"icon": "maki-water", "fields": ["{natural/water}"], "geometry": ["area"], "tags": {"natural": "water", "water": "stream"}, "reference": {"key": "water", "value": "stream"}, "terms": ["beck", "branch", "brook", "burn", "course", "creek", "current", "drift", "flood", "flow", "freshet", "race", "rill", "rindle", "rivulet", "run", "runnel", "rush", "spate", "spritz", "surge", "tide", "torrent", "tributary", "watercourse"], "name": "Stream"}, + "natural/water/river": {"icon": "iD-waterway-river", "fields": ["{natural/water}", "tidal"], "geometry": ["area"], "tags": {"natural": "water", "water": "river"}, "reference": {"key": "water", "value": "river"}, "terms": ["beck", "branch", "brook", "course", "creek", "estuary", "rill", "riverbank", "rivulet", "run", "runnel", "stream", "tributary", "watercourse"], "name": "River"}, + "natural/water/stream": {"icon": "iD-waterway-stream", "fields": ["{natural/water}"], "geometry": ["area"], "tags": {"natural": "water", "water": "stream"}, "reference": {"key": "water", "value": "stream"}, "terms": ["beck", "branch", "brook", "burn", "course", "creek", "current", "drift", "flood", "flow", "freshet", "race", "rill", "rindle", "rivulet", "run", "runnel", "rush", "spate", "spritz", "surge", "tide", "torrent", "tributary", "watercourse"], "name": "Stream"}, "natural/wetland": {"icon": "maki-wetland", "fields": ["wetland"], "geometry": ["point", "area"], "tags": {"natural": "wetland"}, "terms": ["bog", "marsh", "reedbed", "swamp", "tidalflat"], "name": "Wetland"}, "natural/wood": {"icon": "maki-park-alt1", "fields": ["name", "leaf_type", "leaf_cycle"], "geometry": ["point", "area"], "tags": {"natural": "wood"}, "terms": ["tree"], "name": "Wood"}, "noexit/yes": {"icon": "maki-barrier", "geometry": ["vertex"], "terms": ["no exit", "road end", "dead end"], "tags": {"noexit": "yes"}, "reference": {"key": "noexit", "value": "*"}, "name": "No Exit"}, diff --git a/data/presets/presets/natural/water/canal.json b/data/presets/presets/natural/water/canal.json index 510d4fb6b..96505bb92 100644 --- a/data/presets/presets/natural/water/canal.json +++ b/data/presets/presets/natural/water/canal.json @@ -1,5 +1,5 @@ { - "icon": "maki-water", + "icon": "iD-waterway-canal", "fields": [ "{natural/water}", "salt" diff --git a/data/presets/presets/natural/water/river.json b/data/presets/presets/natural/water/river.json index 3847098a0..5909ba6d6 100644 --- a/data/presets/presets/natural/water/river.json +++ b/data/presets/presets/natural/water/river.json @@ -1,5 +1,5 @@ { - "icon": "maki-water", + "icon": "iD-waterway-river", "fields": [ "{natural/water}", "tidal" diff --git a/data/presets/presets/natural/water/stream.json b/data/presets/presets/natural/water/stream.json index 2f57eda40..a7b9425f8 100644 --- a/data/presets/presets/natural/water/stream.json +++ b/data/presets/presets/natural/water/stream.json @@ -1,5 +1,5 @@ { - "icon": "maki-water", + "icon": "iD-waterway-stream", "fields": [ "{natural/water}" ], diff --git a/data/taginfo.json b/data/taginfo.json index ed1cbe2a5..53a8b4149 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -655,12 +655,12 @@ {"key": "natural", "value": "tree", "description": "🄿 Tree", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/park-15.svg?sanitize=true"}, {"key": "natural", "value": "volcano", "description": "🄿 Volcano", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/volcano-15.svg?sanitize=true"}, {"key": "water", "value": "basin", "description": "🄿 Basin", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, - {"key": "water", "value": "canal", "description": "🄿 Canal", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, + {"key": "water", "value": "canal", "description": "🄿 Canal", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/waterway-canal.svg?sanitize=true"}, {"key": "water", "value": "lake", "description": "🄿 Lake", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, {"key": "water", "value": "pond", "description": "🄿 Pond", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, {"key": "water", "value": "reservoir", "description": "🄿 Reservoir", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, - {"key": "water", "value": "river", "description": "🄿 River", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, - {"key": "water", "value": "stream", "description": "🄿 Stream", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/water-15.svg?sanitize=true"}, + {"key": "water", "value": "river", "description": "🄿 River", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/waterway-river.svg?sanitize=true"}, + {"key": "water", "value": "stream", "description": "🄿 Stream", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/waterway-stream.svg?sanitize=true"}, {"key": "natural", "value": "wetland", "description": "🄿 Wetland", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/wetland-15.svg?sanitize=true"}, {"key": "natural", "value": "wood", "description": "🄿 Wood", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/park-alt1-15.svg?sanitize=true"}, {"key": "noexit", "value": "yes", "description": "🄿 No Exit", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/barrier-15.svg?sanitize=true"}, From 5e02523396da35ecd932c2da0a7ca47a21fd54f7 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 14:37:00 -0500 Subject: [PATCH 31/65] Scroll to show all when expanding a result Fix lint warnings --- modules/ui/search_add.js | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 1cf0bc396..e481b1df4 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -13,13 +13,10 @@ import { } from '../modes'; import { t, textDirection } from '../util/locale'; -import { actionChangePreset } from '../actions/index'; -import { operationDelete } from '../operations/index'; import { svgIcon } from '../svg/index'; import { tooltip } from '../util/tooltip'; import { uiPresetFavorite } from './preset_favorite'; import { uiPresetIcon } from './preset_icon'; -import { uiTagReference } from './tag_reference'; import { utilKeybinding, utilNoAuto, utilRebind } from '../util'; @@ -57,7 +54,8 @@ export function uiSearchAdd(context) { // up/down arrow key navigation var nextFocus, - priorFocus; + priorFocus, + parentSubsection; if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) { d3_event.preventDefault(); d3_event.stopPropagation(); @@ -72,7 +70,7 @@ export function uiSearchAdd(context) { .selectAll('.list-item:first-child'); } if (nextFocus.empty()) { - var parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); + parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); if (parentSubsection && parentSubsection.nextElementSibling) { nextFocus = d3_select(parentSubsection.nextElementSibling); } @@ -95,7 +93,7 @@ export function uiSearchAdd(context) { nextFocus = nextFocus.selectAll('.list-item:last-child'); } if (nextFocus.empty()) { - var parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); + parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); if (parentSubsection && parentSubsection.previousElementSibling) { nextFocus = d3_select(parentSubsection.previousElementSibling); } @@ -133,7 +131,6 @@ export function uiSearchAdd(context) { }) .on('input', function () { var value = search.property('value'); - //list.classed('filtered', value.length); if (value.length) { popover.selectAll('.subsection').remove(); var results = presets.search(value); @@ -142,7 +139,7 @@ export function uiSearchAdd(context) { .classed('focused', false); focusListItem(popover.selectAll('.list > .list-item:first-child')); } else { - //list.call(drawList, context.presets().defaults(geometry, 36)); + popover.selectAll('.list > *').remove(); } }); @@ -175,17 +172,23 @@ export function uiSearchAdd(context) { function focusListItem(selection) { if (!selection.empty()) { selection.classed('focused', true); - var node = selection.nodes()[0]; - var popoverNode = popover.node(); - var nodeRect = node.getBoundingClientRect(); - // scroll to keep the focused item visible - if (node.offsetTop < popoverNode.scrollTop) { - popoverNode.scrollTop = node.offsetTop; + scrollPopoverToShow(selection) + } + } - } else if (node.offsetTop + node.offsetHeight > popoverNode.scrollTop + popoverNode.offsetHeight) { - popoverNode.scrollTop = node.offsetTop + node.offsetHeight - popoverNode.offsetHeight; - } + function scrollPopoverToShow(selection) { + if (selection.empty()) return; + + var node = selection.nodes()[0]; + var popoverNode = popover.node(); + + if (node.offsetTop < popoverNode.scrollTop) { + popoverNode.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; } } @@ -231,13 +234,13 @@ export function uiSearchAdd(context) { } return id; }) - .on('mouseover', function(d) { + .on('mouseover', function() { list.selectAll('.list-item.focused') .classed('focused', false); d3_select(this) .classed('focused', true); }) - .on('mouseout', function(d) { + .on('mouseout', function() { d3_select(this) .classed('focused', false); }); @@ -334,6 +337,7 @@ export function uiSearchAdd(context) { .enter(); drawItems(subitemsEnter); updateForFeatureHiddenState(); + scrollPopoverToShow(item.subsection); } else { item.subsection.remove(); } @@ -358,7 +362,7 @@ export function uiSearchAdd(context) { } return MultiGeometryPresetItem(preset, supportedGeometry); }); - } + }; return item; } From b828ada0e2e366dd4668caec3a7efbfcdac105f9 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 17:04:35 -0500 Subject: [PATCH 32/65] Don't allow adding point-only features as vertices --- data/presets/categories.json | 2 +- data/presets/categories/water.json | 2 +- modules/behavior/draw.js | 6 +++++- modules/behavior/hover.js | 18 +++++++++++++++--- modules/modes/add_area.js | 9 ++------- modules/modes/add_line.js | 9 ++------- modules/modes/add_point.js | 10 +++------- modules/ui/modes.js | 3 +-- modules/ui/search_add.js | 1 - 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/data/presets/categories.json b/data/presets/categories.json index e3e48b015..04b22f394 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -185,7 +185,7 @@ ] }, "category-water": { - "icon": "iD-category-water", + "icon": "maki-water", "name": "Water Features", "members": [ "natural/water/lake", diff --git a/data/presets/categories/water.json b/data/presets/categories/water.json index e45e89ce6..e34dd397a 100644 --- a/data/presets/categories/water.json +++ b/data/presets/categories/water.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-water", + "icon": "maki-water", "name": "Water Features", "members": [ "natural/water/lake", diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index d12eaadd0..49a243905 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -127,11 +127,15 @@ export function behaviorDraw(context) { var d = datum(); var target = d && d.properties && d.properties.entity; + var mode = context.mode(); + + var allowsSnappingToWay = (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex')); + if (target && target.type === 'node' && allowsVertex(target)) { // Snap to a node dispatch.call('clickNode', this, target, d); return; - } else if (target && target.type === 'way') { // Snap to a way + } else if (target && target.type === 'way' && allowsSnappingToWay) { // Snap to a way var choice = geoChooseEdge( context.childNodes(target), context.mouse(), context.projection, context.activeID() ); diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index 63b26751b..6cc286b93 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -107,6 +107,15 @@ export function behaviorHover(context) { return d.geometry(context.graph()) === 'vertex' || context.presets().allowsVertex(d, context.graph()); } + function modeAllowsHover(target) { + var mode = context.mode(); + if (mode.id === 'add-point') { + return mode.preset.matchGeometry('vertex') || + (target.type !== 'way' && target.geometry(context.graph()) !== 'vertex'); + } + return true; + } + function enter(datum) { if (datum === _target) return; _target = datum; @@ -144,17 +153,20 @@ export function behaviorHover(context) { } } + var mode = context.mode(); + // Update hover state and dispatch event if (entity && entity.id !== _newNodeId) { // If drawing a way, don't hover on a node that was just placed. #3974 - var mode = context.mode() && context.mode().id; - if ((mode === 'draw-line' || mode === 'draw-area') && !_newNodeId && entity.type === 'node') { + + if ((mode.id === 'draw-line' || mode.id === 'draw-area') && !_newNodeId && entity.type === 'node') { _newNodeId = entity.id; return; } var suppressed = (_altDisables && d3_event && d3_event.altKey) || - (entity.type === 'node' && _ignoreVertex && !allowsVertex(entity)); + (entity.type === 'node' && _ignoreVertex && !allowsVertex(entity)) || + !modeAllowsHover(entity); _selection.selectAll(selector) .classed(suppressed ? 'hover-suppressed' : 'hover', true); diff --git a/modules/modes/add_area.js b/modules/modes/add_area.js index 0be3637db..891cd0631 100644 --- a/modules/modes/add_area.js +++ b/modules/modes/add_area.js @@ -10,13 +10,8 @@ import { modeDrawArea } from './index'; import { osmNode, osmWay } from '../osm'; -export function modeAddArea(context, customMode) { - var mode = customMode || { - id: 'add-area', - button: 'area', - title: t('modes.add_area.title'), - description: t('modes.add_area.description') - }; +export function modeAddArea(context, mode) { + mode.id = 'add-area'; var behavior = behaviorAddWay(context) .tail(t('modes.add_area.tail')) diff --git a/modules/modes/add_line.js b/modules/modes/add_line.js index 032c96e3b..c4e6801f1 100644 --- a/modules/modes/add_line.js +++ b/modules/modes/add_line.js @@ -10,13 +10,8 @@ import { modeDrawLine } from './index'; import { osmNode, osmWay } from '../osm'; -export function modeAddLine(context, customMode) { - var mode = customMode || { - id: 'add-line', - button: 'line', - title: t('modes.add_line.title'), - description: t('modes.add_line.description') - }; +export function modeAddLine(context, mode) { + mode.id = 'add-line'; var behavior = behaviorAddWay(context) .tail(t('modes.add_line.tail')) diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index b9dbf7057..1b8b7792e 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -6,13 +6,9 @@ import { osmNode } from '../osm'; import { actionAddMidpoint } from '../actions'; -export function modeAddPoint(context, customMode) { - var mode = customMode || { - id: 'add-point', - button: 'point', - title: t('modes.add_point.title'), - description: t('modes.add_point.description') - }; +export function modeAddPoint(context, mode) { + + mode.id = 'add-point'; var behavior = behaviorDraw(context) .tail(t('modes.add_point.tail')) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index f7728c6ae..15956cd44 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -89,7 +89,6 @@ export function uiModes(context) { tooltipTitleID = 'modes.add_preset.' + d.geom + '.title'; } var favoriteMode = { - id: markerClass, button: markerClass, title: presetName, description: t(tooltipTitleID, { feature: presetName }), @@ -121,7 +120,7 @@ export function uiModes(context) { var buttonsEnter = buttons.enter() .append('button') .attr('tabindex', -1) - .attr('class', function(d) { return d.id + ' add-button bar-button'; }) + .attr('class', function(d) { return d.button + ' add-button bar-button'; }) .on('click.mode-buttons', function(d) { if (!enabled(d)) return; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index e481b1df4..a44ad7ced 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -396,7 +396,6 @@ export function uiSearchAdd(context) { var markerClass = 'add-preset add-' + geometry + ' add-preset-' + preset.name().replace(/\s+/g, '_') + '-' + geometry; var modeInfo = { - id: markerClass, button: markerClass, preset: preset, geometry: geometry From 6e0f5c68230b4ae01017a29b90452ae75e075bbb Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 17:38:08 -0500 Subject: [PATCH 33/65] Allow adding vertex-only features from the serach-to-add field Allow favoriting vertex presets Don't allow adding vertex-only presets as points Rename Other preset to Vertex --- data/core.yaml | 4 ++++ data/presets.yaml | 5 +++-- data/presets/presets.json | 2 +- data/presets/presets/vertex.json | 5 ++++- dist/locales/en.json | 10 +++++++-- modules/behavior/draw.js | 8 +++---- modules/ui/modes.js | 2 +- modules/ui/preset_favorite.js | 2 +- modules/ui/search_add.js | 36 +++++++++++++++++--------------- 9 files changed, 45 insertions(+), 29 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 722410a62..b049cff91 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -23,6 +23,8 @@ en: title: Point description: "Add restaurants, monuments, postal boxes or other points to the map." tail: Click on the map to add a point. + add_vertex: + title: Vertex add_note: title: Note description: "Spotted an issue? Let other mappers know." @@ -35,6 +37,8 @@ en: title: "Add {feature} as a line" area: title: "Add {feature} as an area" + vertex: + title: "Add {feature} as a vertex" browse: title: Browse description: Pan and zoom the map. diff --git a/data/presets.yaml b/data/presets.yaml index 28b08d5ff..4754acec9 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -6944,8 +6944,9 @@ en: name: Waterway terms: '' vertex: - name: Other - terms: '' + name: Vertex + # 'terms: other' + terms: '' waterway: # waterway=* name: Waterway diff --git a/data/presets/presets.json b/data/presets/presets.json index 1f1e58ea6..6667af5eb 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -1060,7 +1060,7 @@ "type/route/tram": {"icon": "iD-route-tram", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "tram"}, "name": "Tram Route"}, "type/site": {"icon": "iD-relation", "fields": ["name", "site"], "geometry": ["relation"], "tags": {"type": "site"}, "name": "Site"}, "type/waterway": {"icon": "iD-route-water", "fields": ["name", "waterway", "ref"], "geometry": ["relation"], "tags": {"type": "waterway"}, "name": "Waterway"}, - "vertex": {"moreFields": ["name"], "geometry": ["vertex"], "tags": {}, "name": "Other", "matchScore": 0.1}, + "vertex": {"moreFields": ["name"], "geometry": ["vertex"], "terms": ["other"], "tags": {}, "name": "Vertex", "matchScore": 0.1}, "waterway/riverbank": {"icon": "maki-water", "geometry": ["area"], "tags": {"waterway": "riverbank"}, "name": "Riverbank", "searchable": false}, "waterway/boatyard": {"icon": "maki-harbor", "fields": ["name", "operator"], "moreFields": ["address", "website", "phone", "email", "fax", "wheelchair"], "geometry": ["area", "vertex", "point"], "tags": {"waterway": "boatyard"}, "name": "Boatyard"}, "waterway/canal": {"icon": "iD-waterway-canal", "fields": ["name", "width", "intermittent"], "moreFields": ["salt"], "geometry": ["line"], "tags": {"waterway": "canal"}, "name": "Canal"}, diff --git a/data/presets/presets/vertex.json b/data/presets/presets/vertex.json index 85c3ddbc6..228e00fd3 100644 --- a/data/presets/presets/vertex.json +++ b/data/presets/presets/vertex.json @@ -5,7 +5,10 @@ "geometry": [ "vertex" ], + "terms": [ + "other" + ], "tags": {}, - "name": "Other", + "name": "Vertex", "matchScore": 0.1 } diff --git a/dist/locales/en.json b/dist/locales/en.json index 58f9d3a84..19736eda0 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -29,6 +29,9 @@ "description": "Add restaurants, monuments, postal boxes or other points to the map.", "tail": "Click on the map to add a point." }, + "add_vertex": { + "title": "Vertex" + }, "add_note": { "title": "Note", "description": "Spotted an issue? Let other mappers know.", @@ -44,6 +47,9 @@ }, "area": { "title": "Add {feature} as an area" + }, + "vertex": { + "title": "Add {feature} as a vertex" } }, "browse": { @@ -8127,8 +8133,8 @@ "terms": "" }, "vertex": { - "name": "Other", - "terms": "" + "name": "Vertex", + "terms": "other" }, "waterway/riverbank": { "name": "Riverbank", diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index 49a243905..8959569f4 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -129,13 +129,11 @@ export function behaviorDraw(context) { var mode = context.mode(); - var allowsSnappingToWay = (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex')); - if (target && target.type === 'node' && allowsVertex(target)) { // Snap to a node dispatch.call('clickNode', this, target, d); return; - } else if (target && target.type === 'way' && allowsSnappingToWay) { // Snap to a way + } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) { // Snap to a way var choice = geoChooseEdge( context.childNodes(target), context.mouse(), context.projection, context.activeID() ); @@ -144,8 +142,10 @@ export function behaviorDraw(context) { dispatch.call('clickWay', this, choice.loc, edge, d); return; } + } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) { + dispatch.call('click', this, context.map().mouseCoordinates(), d); } - dispatch.call('click', this, context.map().mouseCoordinates(), d); + } diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 15956cd44..75d304100 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -82,7 +82,7 @@ export function uiModes(context) { } var presetName = t('presets.presets.' + preset.id + '.name'); var relevantMatchingGeometry = preset.geometry.filter(function(geometry) { - return ['point', 'line', 'area'].indexOf(geometry) !== -1; + return ['vertex', 'point', 'line', 'area'].indexOf(geometry) !== -1; }); var tooltipTitleID = 'modes.add_preset.title'; if (relevantMatchingGeometry.length !== 1) { diff --git a/modules/ui/preset_favorite.js b/modules/ui/preset_favorite.js index d100b6e18..00440cefc 100644 --- a/modules/ui/preset_favorite.js +++ b/modules/ui/preset_favorite.js @@ -15,7 +15,7 @@ export function uiPresetFavorite(preset, geom, context, klass) { presetFavorite.button = function(selection) { - var canFavorite = geom !== 'vertex' && geom !== 'relation'; + var canFavorite = geom !== 'relation'; _button = selection.selectAll('.preset-favorite-button') .data(canFavorite ? [0] : []); diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index a44ad7ced..b9a307cb1 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -25,7 +25,7 @@ export function uiSearchAdd(context) { var presets; var search = d3_select(null), popover = d3_select(null), list = d3_select(null); - var shownGeometry = ['point', 'line', 'area']; + var shownGeometry = ['vertex', 'point', 'line', 'area']; function searchAdd(selection) { @@ -192,20 +192,28 @@ export function uiSearchAdd(context) { } } + function itemForPreset(preset) { + var supportedGeometry = preset.geometry.filter(function(geometry) { + return shownGeometry.indexOf(geometry) !== -1; + }).sort(); + var vertexIndex = supportedGeometry.indexOf('vertex'); + if (vertexIndex !== -1 && supportedGeometry.indexOf('point') !== -1) { + // both point and vertex allowed, just show point + supportedGeometry.splice(vertexIndex, 1); + } + if (supportedGeometry.length === 1) { + return AddablePresetItem(preset, supportedGeometry[0]); + } + return MultiGeometryPresetItem(preset, supportedGeometry); + } + function drawList(list, presets) { var collection = presets.collection.map(function(preset) { if (preset.members) { return CategoryItem(preset); - } else if (preset.visible()) { - var supportedGeometry = preset.geometry.filter(function(geometry) { - return shownGeometry.indexOf(geometry) !== -1; - }).sort(); - if (supportedGeometry.length === 1) { - return AddablePresetItem(preset, supportedGeometry[0]); - } - return MultiGeometryPresetItem(preset, supportedGeometry); } + return itemForPreset(preset); }); var items = list.selectAll('.list-item') @@ -279,7 +287,7 @@ export function uiSearchAdd(context) { row.each(function(d) { if (d.geometry) { - var presetFavorite = uiPresetFavorite(d.preset,d.geometry, context, 'accessory'); + var presetFavorite = uiPresetFavorite(d.preset, d.geometry, context, 'accessory'); d3_select(this).call(presetFavorite.button); } }); @@ -354,13 +362,7 @@ export function uiSearchAdd(context) { }; item.subitems = function() { return preset.members.matchAnyGeometry(shownGeometry).collection.map(function(preset) { - var supportedGeometry = preset.geometry.filter(function(geometry) { - return shownGeometry.indexOf(geometry) !== -1; - }).sort(); - if (supportedGeometry.length === 1) { - return AddablePresetItem(preset, supportedGeometry[0]); - } - return MultiGeometryPresetItem(preset, supportedGeometry); + return itemForPreset(preset); }); }; return item; From d99fe79b6e036c9b3758e6aa6a0364290ba76f81 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 17:47:20 -0500 Subject: [PATCH 34/65] Always show generic presets when searching --- modules/presets/collection.js | 9 +++++++-- modules/ui/search_add.js | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/presets/collection.js b/modules/presets/collection.js index efc439038..08a3b65e2 100644 --- a/modules/presets/collection.js +++ b/modules/presets/collection.js @@ -152,8 +152,13 @@ export function presetCollection(collection) { ).slice(0, maxSearchResults - 1); if (geometry) { - var other = presets.item(geometry); - results = results.concat(other); + if (typeof geometry === 'string') { + results.push(presets.item(geometry)); + } else { + geometry.forEach(function(geom) { + results.push(presets.item(geom)); + }); + } } return presetCollection(_uniq(results)); diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index b9a307cb1..2734a0639 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -25,7 +25,7 @@ export function uiSearchAdd(context) { var presets; var search = d3_select(null), popover = d3_select(null), list = d3_select(null); - var shownGeometry = ['vertex', 'point', 'line', 'area']; + var shownGeometry = ['area', 'line', 'point', 'vertex']; function searchAdd(selection) { @@ -133,7 +133,7 @@ export function uiSearchAdd(context) { var value = search.property('value'); if (value.length) { popover.selectAll('.subsection').remove(); - var results = presets.search(value); + var results = presets.search(value, shownGeometry); list.call(drawList, results); popover.selectAll('.list .list-item.focused') .classed('focused', false); From 6df14474fe9b9bb30312d6551eadef78ee422077 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 17:50:35 -0500 Subject: [PATCH 35/65] Fix stale favorite preset buttons --- modules/ui/modes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 75d304100..040c01ffa 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -110,7 +110,7 @@ export function uiModes(context) { var data = favoriteModes; var buttons = selection.selectAll('button.add-button') - .data(data, function(d) { return d.id; }); + .data(data, function(d) { return d.button; }); // exit buttons.exit() From 62aea31192038d353716bac8543f25f0252de57d Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 4 Mar 2019 17:55:02 -0500 Subject: [PATCH 36/65] Fix an issue with preset names in favorite button labels --- modules/ui/modes.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 040c01ffa..ef317c0a2 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -74,13 +74,13 @@ export function uiModes(context) { var favoritePresets = context.getFavoritePresets(); var favoriteModes = favoritePresets.map(function(d) { var preset = context.presets().item(d.id); - var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + preset.name() - .replace(/\s+/g, '_') - + '-' + d.geom; //replace spaces with underscores to avoid css interpretation + var presetName = preset.name(); + var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + presetName.replace(/\s+/g, '_') + + '-' + d.geom; // replace spaces with underscores to avoid css interpretation if (preset.isFallback()) { markerClass += ' add-generic-preset'; } - var presetName = t('presets.presets.' + preset.id + '.name'); + var relevantMatchingGeometry = preset.geometry.filter(function(geometry) { return ['vertex', 'point', 'line', 'area'].indexOf(geometry) !== -1; }); From be8bf1fac79f53274f619cbc71f5201f4c90b3bf Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 09:19:00 -0500 Subject: [PATCH 37/65] Use the number keys as shortcuts for adding favorite presets --- modules/ui/modes.js | 47 ++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index ef317c0a2..5af3c3e64 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -42,19 +42,6 @@ export function uiModes(context) { context.container() .classed('mode-' + exited.id, false); }); -/* - modes.forEach(function(mode) { - context.keybinding().on(mode.key, function() { - if (!enabled(mode)) return; - - if (mode.id === context.mode().id) { - context.enter(modeBrowse(context)); - } else { - context.enter(mode); - } - }); - }); -*/ var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true }); @@ -70,9 +57,13 @@ export function uiModes(context) { function update() { - // add favorite presets to modes + + for (var i = 0; i <= 9; i++) { + context.keybinding().off(i.toString()); + } + var favoritePresets = context.getFavoritePresets(); - var favoriteModes = favoritePresets.map(function(d) { + var favoriteModes = favoritePresets.map(function(d, index) { var preset = context.presets().item(d.id); var presetName = preset.name(); var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + presetName.replace(/\s+/g, '_') @@ -96,15 +87,35 @@ export function uiModes(context) { preset: preset, geometry: d.geom }; + var mode; switch (d.geom) { case 'point': case 'vertex': - return modeAddPoint(context, favoriteMode); + mode = modeAddPoint(context, favoriteMode); + break; case 'line': - return modeAddLine(context, favoriteMode); + mode = modeAddLine(context, favoriteMode); + break; case 'area': - return modeAddArea(context, favoriteMode); + mode = modeAddArea(context, favoriteMode); } + var keyCode = index + 1; + if (keyCode <= 10) { + if (keyCode === 10) { + keyCode = 0; + } + context.keybinding().on(keyCode.toString(), function() { + if (!enabled(mode)) return; + + if (mode.button === context.mode().button) { + context.enter(modeBrowse(context)); + } else { + context.enter(mode); + } + }); + } + + return mode; }); var data = favoriteModes; From 062872c4f0c4b87e7481fbce84afd4c4efa17cb5 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 09:39:55 -0500 Subject: [PATCH 38/65] Merge tags instead of adding new point when adding a point to a point Fix lint error --- modules/modes/add_point.js | 22 ++++++++++++++++++++-- modules/ui/modes.js | 1 - modules/ui/search_add.js | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index 1b8b7792e..d3c490d72 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -1,5 +1,7 @@ +import _clone from 'lodash-es/clone'; + import { t } from '../util/locale'; -import { actionAddEntity } from '../actions'; +import { actionAddEntity, actionChangeTags } from '../actions'; import { behaviorDraw } from '../behavior'; import { modeBrowse, modeSelect } from './index'; import { osmNode } from '../osm'; @@ -53,7 +55,23 @@ export function modeAddPoint(context, mode) { function addNode(node) { - add(node.loc); + + if (Object.keys(defaultTags).length === 0) { + enterSelectMode(node); + return; + } + + var tags = _clone(node.tags); + for (var key in defaultTags) { + tags[key] = defaultTags[key]; + } + + context.perform( + actionChangeTags(node.id, tags), + t('operations.add.annotation.point') + ); + + enterSelectMode(node); } diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 5af3c3e64..3f15702dc 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -83,7 +83,6 @@ export function uiModes(context) { button: markerClass, title: presetName, description: t(tooltipTitleID, { feature: presetName }), - key: '', preset: preset, geometry: d.geom }; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 2734a0639..32f579677 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -173,7 +173,7 @@ export function uiSearchAdd(context) { if (!selection.empty()) { selection.classed('focused', true); // scroll to keep the focused item visible - scrollPopoverToShow(selection) + scrollPopoverToShow(selection); } } From 46037c0cb6a776d063941e952699a2f9a71158cb Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 11:21:38 -0500 Subject: [PATCH 39/65] Add tab key as shortcut for focusing the search-to-add field Add tab navigation of the search results --- modules/ui/search_add.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 32f579677..db8e8bcd3 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -56,7 +56,8 @@ export function uiSearchAdd(context) { var nextFocus, priorFocus, parentSubsection; - if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) { + if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] || + d3_event.keyCode === utilKeybinding.keyCodes['tab'] && !d3_event.shiftKey) { d3_event.preventDefault(); d3_event.stopPropagation(); @@ -81,7 +82,8 @@ export function uiSearchAdd(context) { priorFocus.classed('focused', false); } - } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) { + } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑'] || + d3_event.keyCode === utilKeybinding.keyCodes['tab'] && d3_event.shiftKey) { d3_event.preventDefault(); d3_event.stopPropagation(); @@ -162,7 +164,7 @@ export function uiSearchAdd(context) { context.features().on('change.search-add', updateForFeatureHiddenState); - context.keybinding().on('1', function() { + context.keybinding().on('tab', function() { search.node().focus(); d3_event.preventDefault(); d3_event.stopPropagation(); From fc6bf79ed1382f087f8579e3ba50f5a494957cbc Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 12:04:13 -0500 Subject: [PATCH 40/65] Make Add Note shortcut translatable --- data/core.yaml | 1 + dist/locales/en.json | 3 ++- modules/modes/add_note.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index b049cff91..183f5e7cc 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -29,6 +29,7 @@ en: title: Note description: "Spotted an issue? Let other mappers know." tail: Click on the map to add a note. + key: N add_preset: title: "Add {feature}" point: diff --git a/dist/locales/en.json b/dist/locales/en.json index 6a4db5140..ae188d242 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -35,7 +35,8 @@ "add_note": { "title": "Note", "description": "Spotted an issue? Let other mappers know.", - "tail": "Click on the map to add a note." + "tail": "Click on the map to add a note.", + "key": "N" }, "add_preset": { "title": "Add {feature}", diff --git a/modules/modes/add_note.js b/modules/modes/add_note.js index c8d1376c4..d7e105113 100644 --- a/modules/modes/add_note.js +++ b/modules/modes/add_note.js @@ -11,7 +11,7 @@ export function modeAddNote(context) { button: 'note', title: t('modes.add_note.title'), description: t('modes.add_note.description'), - key: 'N' + key: t('modes.add_note.key') }; var behavior = behaviorDraw(context) From 61194e4a2176e4815e2ccdea528ced45b6196501 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 12:15:05 -0500 Subject: [PATCH 41/65] Don't add margin to empty top bar items --- css/80_app.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index 2613a02fc..ecb3aa015 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -464,7 +464,7 @@ button[disabled].action:hover { justify-content: flex-end; } -.tool-group > div { +.tool-group > div:not(:empty) { display: flex; margin: 0 5px; } From e22f3359f5aa79923eb24d6ae266ed977e4feb40 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 12:27:42 -0500 Subject: [PATCH 42/65] Allow up to 10 favorite presets Show favorite preset keyboard shortcut in tooltip --- modules/core/context.js | 4 ++-- modules/ui/modes.js | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/core/context.js b/modules/core/context.js index 2efa2c4f1..3cacaed24 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -325,8 +325,8 @@ export function coreContext() { return !(d.id === preset.id && d.geom === geom); }); } else { - // only allow 3 favorites - if (favs.length === 3) { + // only allow 10 favorites + if (favs.length === 10) { // remove the last favorite (first in, first out) favs.pop(); } diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 3f15702dc..dece898cd 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -86,6 +86,13 @@ export function uiModes(context) { preset: preset, geometry: d.geom }; + var keyCode = index + 1; + if (keyCode <= 10) { + if (keyCode === 10) { + keyCode = 0; + } + favoriteMode.key = keyCode.toString(); + } var mode; switch (d.geom) { case 'point': @@ -98,12 +105,9 @@ export function uiModes(context) { case 'area': mode = modeAddArea(context, favoriteMode); } - var keyCode = index + 1; - if (keyCode <= 10) { - if (keyCode === 10) { - keyCode = 0; - } - context.keybinding().on(keyCode.toString(), function() { + + if (mode.key) { + context.keybinding().on(mode.key, function() { if (!enabled(mode)) return; if (mode.button === context.mode().button) { From 8602cbdaffd7755dd035cd3d400119915df8b572 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 12:49:47 -0500 Subject: [PATCH 43/65] Make building-as-point a separate, unsearchable preset --- data/presets.yaml | 3 +++ data/presets/presets.json | 3 ++- data/presets/presets/_building_point.json | 19 +++++++++++++++++++ data/presets/presets/building.json | 1 - data/taginfo.json | 2 +- dist/locales/en.json | 4 ++++ 6 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 data/presets/presets/_building_point.json diff --git a/data/presets.yaml b/data/presets.yaml index f8c4339fd..07e525675 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -3510,6 +3510,9 @@ en: # building=warehouse name: Warehouse terms: '' + building_point: + # building=* + name: Building camp_site/camp_pitch: # camp_site=camp_pitch name: Camp Pitch diff --git a/data/presets/presets.json b/data/presets/presets.json index a26445e59..6c6296a1f 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -5,6 +5,7 @@ "amenity": {"fields": ["amenity"], "geometry": ["point", "vertex", "line", "area"], "tags": {"amenity": "*"}, "searchable": false, "name": "Amenity"}, "attraction": {"icon": "maki-star", "fields": ["name", "attraction", "operator", "opening_hours"], "moreFields": ["opening_hours", "fee", "payment_multi", "address", "website", "phone", "email", "fax"], "geometry": ["point", "vertex", "line", "area"], "tags": {"attraction": "*"}, "searchable": false, "name": "Attraction"}, "boundary": {"fields": ["boundary"], "geometry": ["line"], "tags": {"boundary": "*"}, "searchable": false, "name": "Boundary"}, + "building_point": {"icon": "maki-home", "fields": ["{building}"], "moreFields": ["{building}"], "geometry": ["point"], "tags": {"building": "*"}, "matchScore": 0.6, "searchable": false, "terms": [], "name": "Building"}, "circular": {"geometry": ["vertex", "line"], "fields": ["name"], "tags": {"junction": "circular"}, "name": "Traffic Circle", "searchable": false}, "embankment": {"geometry": ["line"], "tags": {"embankment": "yes"}, "name": "Embankment", "matchScore": 0.2, "searchable": false}, "highway": {"fields": ["name", "highway"], "geometry": ["point", "vertex", "line", "area"], "tags": {"highway": "*"}, "searchable": false, "name": "Highway"}, @@ -264,7 +265,7 @@ "boundary/administrative": {"name": "Administrative Boundary", "geometry": ["line"], "tags": {"boundary": "administrative"}, "fields": ["name", "admin_level"]}, "bridge/support": {"icon": "fas-archway", "fields": ["bridge/support"], "moreFields": ["material", "seamark/type"], "geometry": ["point", "vertex", "area"], "tags": {"bridge:support": "*"}, "name": "Bridge Support"}, "bridge/support/pier": {"icon": "fas-archway", "fields": ["bridge/support"], "moreFields": ["material", "seamark/type"], "geometry": ["point", "vertex", "area"], "tags": {"bridge:support": "pier"}, "name": "Bridge Pier"}, - "building": {"icon": "maki-home", "fields": ["name", "building", "levels", "height", "address"], "moreFields": ["architect", "building/material", "layer", "roof/colour", "smoking", "wheelchair"], "geometry": ["point", "area"], "tags": {"building": "*"}, "matchScore": 0.6, "terms": [], "name": "Building"}, + "building": {"icon": "maki-home", "fields": ["name", "building", "levels", "height", "address"], "moreFields": ["architect", "building/material", "layer", "roof/colour", "smoking", "wheelchair"], "geometry": ["area"], "tags": {"building": "*"}, "matchScore": 0.6, "terms": [], "name": "Building"}, "building/bunker": {"geometry": ["area"], "tags": {"building": "bunker"}, "matchScore": 0.5, "name": "Bunker", "searchable": false}, "building/entrance": {"icon": "maki-entrance-alt1", "fields": [], "moreFields": [], "geometry": ["vertex"], "tags": {"building": "entrance"}, "name": "Entrance/Exit", "searchable": false}, "building/train_station": {"icon": "maki-building", "geometry": ["point", "vertex", "area"], "tags": {"building": "train_station"}, "matchScore": 0.5, "name": "Train Station Building", "searchable": false}, diff --git a/data/presets/presets/_building_point.json b/data/presets/presets/_building_point.json new file mode 100644 index 000000000..0cce45b4f --- /dev/null +++ b/data/presets/presets/_building_point.json @@ -0,0 +1,19 @@ +{ + "icon": "maki-home", + "fields": [ + "{building}" + ], + "moreFields": [ + "{building}" + ], + "geometry": [ + "point" + ], + "tags": { + "building": "*" + }, + "matchScore": 0.6, + "searchable": false, + "terms": [], + "name": "Building" +} diff --git a/data/presets/presets/building.json b/data/presets/presets/building.json index 771e7129c..7484840cd 100644 --- a/data/presets/presets/building.json +++ b/data/presets/presets/building.json @@ -16,7 +16,6 @@ "wheelchair" ], "geometry": [ - "point", "area" ], "tags": { diff --git a/data/taginfo.json b/data/taginfo.json index 53a8b4149..aea9fb5e7 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -8,6 +8,7 @@ {"key": "amenity", "description": "🄿 Amenity (unsearchable), 🄵 Type", "object_types": ["node", "way", "area"]}, {"key": "attraction", "description": "🄿 Attraction (unsearchable), 🄵 Type", "object_types": ["node", "way", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/star-15.svg?sanitize=true"}, {"key": "boundary", "description": "🄿 Boundary (unsearchable), 🄵 Type", "object_types": ["way"]}, + {"key": "building", "description": "🄿 Building (unsearchable), 🄿 Building, 🄵 Building", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/home-15.svg?sanitize=true"}, {"key": "junction", "value": "circular", "description": "🄿 Traffic Circle (unsearchable), 🄵 Junction", "object_types": ["node", "way"]}, {"key": "embankment", "value": "yes", "description": "🄿 Embankment (unsearchable)", "object_types": ["way"]}, {"key": "highway", "description": "🄿 Highway (unsearchable), 🄵 Type", "object_types": ["node", "way", "area"]}, @@ -261,7 +262,6 @@ {"key": "boundary", "value": "administrative", "description": "🄿 Administrative Boundary", "object_types": ["way"]}, {"key": "bridge:support", "description": "🄿 Bridge Support, 🄵 Type", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-archway.svg?sanitize=true"}, {"key": "bridge:support", "value": "pier", "description": "🄿 Bridge Pier", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-archway.svg?sanitize=true"}, - {"key": "building", "description": "🄿 Building, 🄵 Building", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/home-15.svg?sanitize=true"}, {"key": "building", "value": "bunker", "description": "🄿 Bunker (unsearchable)", "object_types": ["area"]}, {"key": "building", "value": "entrance", "description": "🄿 Entrance/Exit (unsearchable)", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/entrance-alt1-15.svg?sanitize=true"}, {"key": "building", "value": "train_station", "description": "🄿 Train Station Building (unsearchable)", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/building-15.svg?sanitize=true"}, diff --git a/dist/locales/en.json b/dist/locales/en.json index ae188d242..b502c017f 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -3913,6 +3913,10 @@ "name": "Boundary", "terms": "" }, + "building_point": { + "name": "Building", + "terms": "" + }, "circular": { "name": "Traffic Circle", "terms": "" From b39cbe38151479ee7e3593cf0ad9f1e6a552458b Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 12:50:46 -0500 Subject: [PATCH 44/65] Fix lint errors --- modules/ui/search_add.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index db8e8bcd3..8ce069f9b 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -57,7 +57,7 @@ export function uiSearchAdd(context) { priorFocus, parentSubsection; if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] || - d3_event.keyCode === utilKeybinding.keyCodes['tab'] && !d3_event.shiftKey) { + d3_event.keyCode === utilKeybinding.keyCodes.tab && !d3_event.shiftKey) { d3_event.preventDefault(); d3_event.stopPropagation(); @@ -83,7 +83,7 @@ export function uiSearchAdd(context) { } } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑'] || - d3_event.keyCode === utilKeybinding.keyCodes['tab'] && d3_event.shiftKey) { + d3_event.keyCode === utilKeybinding.keyCodes.tab && d3_event.shiftKey) { d3_event.preventDefault(); d3_event.stopPropagation(); From ea70ec45b38ce982fd3ddf38fca5693018766306 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 13:04:53 -0500 Subject: [PATCH 45/65] Update favorite preset button state automatically --- modules/ui/preset_favorite.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/ui/preset_favorite.js b/modules/ui/preset_favorite.js index 00440cefc..2cc7b43b4 100644 --- a/modules/ui/preset_favorite.js +++ b/modules/ui/preset_favorite.js @@ -31,24 +31,24 @@ export function uiPresetFavorite(preset, geom, context, klass) { .merge(_button); _button - .classed('active', function() { - return context.isFavoritePreset(preset, geom); - }) .on('click', function () { d3_event.stopPropagation(); d3_event.preventDefault(); - //update state of favorite icon - d3_select(this) - .classed('active', function() { - return !d3_select(this).classed('active'); - }); - - context.favoritePreset(preset, geom); + context.favoritePreset(preset, geom); + update(); }); + update(); }; + function update() { + _button + .classed('active', context.isFavoritePreset(preset, geom)); + } + + context.on('favoritePreset.button-' + preset.id.replace(/[^a-zA-Z\d:]/g, '-') + '-' + geom, update); + return presetFavorite; } From 310b9e439d1e25e3856c478f487f2cae7e47b961 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 17:13:48 -0500 Subject: [PATCH 46/65] Add disabled styling to disabled favorite preset buttons --- css/80_app.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index ecb3aa015..c7d5302b9 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -480,6 +480,9 @@ button[disabled].action:hover { .tool-group button.add-preset:not(.add-generic-preset) { padding: 0; } +.tool-group button.add-preset.disabled .preset-icon-container { + opacity: 0.5; +} .tool-group button.bar-button .icon { flex: 0 0 20px; } From 59cca2a7de01c016e465fa8a5b9bccce49eda5ab Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 18:00:00 -0500 Subject: [PATCH 47/65] Drag favorite presets buttons out of the top bar to remove --- css/80_app.css | 8 ++++++++ modules/ui/modes.js | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index c7d5302b9..62d2674ea 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -491,6 +491,14 @@ button[disabled].action:hover { padding: 0 5px; } +.tool-group button.dragging { + opacity: 0.75; + z-index: 200; +} +.tool-group button.dragging .tooltip { + display: none; +} + button.save .count { display: inline-block; border: 0px solid #ccc; diff --git a/modules/ui/modes.js b/modules/ui/modes.js index dece898cd..f95d2f780 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -1,6 +1,7 @@ import _debounce from 'lodash-es/debounce'; -import { select as d3_select } from 'd3-selection'; +import { drag as d3_drag } from 'd3-drag'; +import { event as d3_event, select as d3_select } from 'd3-selection'; import { modeAddArea, @@ -168,6 +169,39 @@ export function uiModes(context) { ); } }); + + var dragOrigin; + + buttonsEnter.call(d3_drag() + .on('start', function(d) { + //if (d3_select(this).classed('disabled')) return; + dragOrigin = { + x: d3_event.x, + y: d3_event.y + }; + }) + .on('drag', function(d) { + var x = d3_event.x - dragOrigin.x, + y = d3_event.y - dragOrigin.y; + + d3_select(this) + .classed('dragging', true) + .style('transform', 'translate(' + x + 'px, ' + y + 'px)'); + }) + .on('end', function(d) { + + d3_select(this) + .style('transform', null) + .classed('dragging', false); + + var y = d3_event.y - dragOrigin.y; + if (y > 50) { + // dragged out of the top bar, remove the favorite + context.favoritePreset(d.preset, d.geometry); + } + }) + ); + /* buttonsEnter .append('span') From 651d5432f2098e83798f6c31f250d44b5babdb7f Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 18:04:50 -0500 Subject: [PATCH 48/65] Fix cursor in preset icons --- css/80_app.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index 62d2674ea..e269310fe 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1158,7 +1158,6 @@ a.hide-toggle { } .preset-icon-fill { - cursor: inherit; margin: auto; position: absolute; width: 100%; @@ -1166,6 +1165,10 @@ a.hide-toggle { left: 0; top: 0; } +.preset-icon-container svg, +.preset-icon-container svg > * { + cursor: inherit !important; +} .preset-icon-fill path.area.stroke { fill: transparent; } From 6428a9055acc1a1d2510733d263b99b31fc59ed9 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 18:13:10 -0500 Subject: [PATCH 49/65] Add remove cursor to buttons dragged out of the top bar --- css/80_app.css | 3 +++ modules/ui/modes.js | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index e269310fe..270034907 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -498,6 +498,9 @@ button[disabled].action:hover { .tool-group button.dragging .tooltip { display: none; } +.tool-group button.dragging.removing { + cursor: url(img/cursor-select-remove.png), pointer; +} button.save .count { display: inline-block; diff --git a/modules/ui/modes.js b/modules/ui/modes.js index f95d2f780..ce904ad9e 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -186,13 +186,15 @@ export function uiModes(context) { d3_select(this) .classed('dragging', true) + .classed('removing', y > 50) .style('transform', 'translate(' + x + 'px, ' + y + 'px)'); }) .on('end', function(d) { d3_select(this) - .style('transform', null) - .classed('dragging', false); + .classed('dragging', false) + .classed('removing', false) + .style('transform', null); var y = d3_event.y - dragOrigin.y; if (y > 50) { From 944c116b0b43bb72b9f703a62f1325cfbfb1c2db Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 18:15:58 -0500 Subject: [PATCH 50/65] Fix lint warnings --- modules/ui/modes.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index ce904ad9e..09718eb4b 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -173,14 +173,13 @@ export function uiModes(context) { var dragOrigin; buttonsEnter.call(d3_drag() - .on('start', function(d) { - //if (d3_select(this).classed('disabled')) return; + .on('start', function() { dragOrigin = { x: d3_event.x, y: d3_event.y }; }) - .on('drag', function(d) { + .on('drag', function() { var x = d3_event.x - dragOrigin.x, y = d3_event.y - dragOrigin.y; From 776c0556e1d46efd8ef1ec828dba67aaf904ef25 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 20:21:54 -0500 Subject: [PATCH 51/65] Add drag-and-drop reordering of favorite presets --- modules/core/context.js | 18 +++++++++---- modules/ui/modes.js | 56 ++++++++++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/modules/core/context.js b/modules/core/context.js index 3cacaed24..072d1c549 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -316,6 +316,12 @@ export function coreContext() { context.getFavoritePresets = function() { return JSON.parse(context.storage('favorite_presets')) || []; }; + function setFavoritePresets(favs) { + context.storage('favorite_presets', JSON.stringify(favs)); + + //and call update on modes + dispatch.call('favoritePreset'); + } context.favoritePreset = function(preset, geom) { var favs = context.getFavoritePresets(); @@ -333,18 +339,20 @@ export function coreContext() { // prepend array favs.unshift({id: preset.id, geom: geom}); } - - context.storage('favorite_presets', JSON.stringify(favs)); - - //and call update on modes - dispatch.call('favoritePreset'); + setFavoritePresets(favs); }; + context.isFavoritePreset = function(preset, geom) { var favs = context.getFavoritePresets(); return favs.some(function(d) { return d.id === preset.id && d.geom === geom; }); }; + context.moveFavoritePreset = function(fromIndex, toIndex) { + var favs = context.getFavoritePresets(); + favs.splice(toIndex, 0, favs.splice(fromIndex, 1)[0]); + setFavoritePresets(favs); + }; /* Map */ var map; diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 09718eb4b..54427c418 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -125,7 +125,7 @@ export function uiModes(context) { var data = favoriteModes; var buttons = selection.selectAll('button.add-button') - .data(data, function(d) { return d.button; }); + .data(data, function(d, index) { return d.button + index; }); // exit buttons.exit() @@ -170,7 +170,7 @@ export function uiModes(context) { } }); - var dragOrigin; + var dragOrigin, targetIndex; buttonsEnter.call(d3_drag() .on('start', function() { @@ -179,45 +179,65 @@ export function uiModes(context) { y: d3_event.y }; }) - .on('drag', function() { + .on('drag', function(d, index) { var x = d3_event.x - dragOrigin.x, y = d3_event.y - dragOrigin.y; d3_select(this) .classed('dragging', true) - .classed('removing', y > 50) - .style('transform', 'translate(' + x + 'px, ' + y + 'px)'); + .classed('removing', y > 50); + + targetIndex = null; + + selection.selectAll('button.add-preset') + .style('transform', function(d2, index2) { + if (index === index2) { + return 'translate(' + x + 'px, ' + y + 'px)'; + } else if (y > 50) { + if (index2 > index) { + return 'translateX(-100%)'; + } + } else if (index2 > index && d3_event.x > d3_select(this).node().offsetLeft) { + if (targetIndex === null || index2 > targetIndex) { + targetIndex = index2; + } + return 'translateX(-100%)'; + } else if (index2 < index && d3_event.x < d3_select(this).node().offsetLeft + d3_select(this).node().offsetWidth) { + if (targetIndex === null || index2 < targetIndex) { + targetIndex = index2; + } + return 'translateX(100%)'; + } + return null; + }); }) - .on('end', function(d) { + .on('end', function(d, index) { d3_select(this) .classed('dragging', false) - .classed('removing', false) + .classed('removing', false); + + selection.selectAll('button.add-preset') .style('transform', null); var y = d3_event.y - dragOrigin.y; if (y > 50) { // dragged out of the top bar, remove the favorite context.favoritePreset(d.preset, d.geometry); + } else if (targetIndex !== null) { + // dragged to a new position, reorder + context.moveFavoritePreset(index, targetIndex); } }) ); - /* - buttonsEnter - .append('span') - .attr('class', 'label') - .text(function(mode) { return mode.title; }); - */ - // if we are adding/removing the buttons, check if toolbar has overflowed - if (buttons.enter().size() || buttons.exit().size()) { - context.ui().checkOverflow('#bar', true); - } - // update buttons = buttons .merge(buttonsEnter) .classed('disabled', function(d) { return !enabled(d); }); + + // check if toolbar has overflowed + context.ui().checkOverflow('#bar', true); } }; } From 9a539023dfb53ec756d2cbbf0acc13ff8c0675db Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 5 Mar 2019 20:29:55 -0500 Subject: [PATCH 52/65] Fix an issue where favorite buttons could reorder unexpectedly --- modules/ui/modes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 54427c418..9d0e6beb3 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -178,6 +178,7 @@ export function uiModes(context) { x: d3_event.x, y: d3_event.y }; + targetIndex = null; }) .on('drag', function(d, index) { var x = d3_event.x - dragOrigin.x, From 261856582648e824b75035c9d9d3ffc3021f9fc5 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 08:49:17 -0500 Subject: [PATCH 53/65] Make favorites list last in, first out --- modules/core/context.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core/context.js b/modules/core/context.js index 072d1c549..f38b0cfbb 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -333,11 +333,11 @@ export function coreContext() { } else { // only allow 10 favorites if (favs.length === 10) { - // remove the last favorite (first in, first out) + // remove the last favorite (last in, first out) favs.pop(); } - // prepend array - favs.unshift({id: preset.id, geom: geom}); + // append array + favs.push({id: preset.id, geom: geom}); } setFavoritePresets(favs); }; From 17a3e0f6be810257b0eb76603427403800707d67 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 09:30:22 -0500 Subject: [PATCH 54/65] Update preset categories --- data/presets.yaml | 16 ++-- data/presets/categories.json | 81 ++++++++++++------- data/presets/categories/path.json | 6 +- data/presets/categories/rail.json | 15 ++-- .../categories/{road.json => road_major.json} | 18 ++--- data/presets/categories/road_minor.json | 12 +++ data/presets/categories/road_service.json | 12 +++ data/presets/categories/water.json | 18 ++--- data/presets/categories/waterway.json | 14 ++++ data/presets/defaults.json | 5 +- dist/locales/en.json | 19 +++-- 11 files changed, 141 insertions(+), 75 deletions(-) rename data/presets/categories/{road.json => road_major.json} (58%) create mode 100644 data/presets/categories/road_minor.json create mode 100644 data/presets/categories/road_service.json create mode 100644 data/presets/categories/waterway.json diff --git a/data/presets.yaml b/data/presets.yaml index 07e525675..4596b550b 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -12,19 +12,25 @@ en: category-natural: name: Natural Features category-path: - name: Path Features + name: Paths category-rail: - name: Rail Features + name: Rails category-restriction: name: Restriction Features - category-road: - name: Road Features + category-road_major: + name: Major Roads + category-road_minor: + name: Minor Roads + category-road_service: + name: Service Roads category-route: name: Route Features category-utility: name: Utility Features category-water: - name: Water Features + name: Water Bodies + category-waterway: + name: Waterways fields: access: # 'access=*, foot=*, motor_vehicle=*, bicycle=*, horse=*' diff --git a/data/presets/categories.json b/data/presets/categories.json index 04b22f394..791906165 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -87,13 +87,13 @@ }, "category-path": { "icon": "iD-category-path", - "name": "Path Features", + "name": "Paths", "members": [ + "highway/path", + "highway/footway", "highway/footway/marked", "highway/footway/sidewalk", "highway/steps", - "highway/path", - "highway/footway", "highway/cycleway", "highway/bridleway", "highway/pedestrian_line" @@ -101,17 +101,16 @@ }, "category-rail": { "icon": "iD-category-rail", - "name": "Rail Features", + "name": "Rails", "members": [ "railway/rail", - "railway/subway", - "railway/tram", "railway/disused", - "railway/level_crossing", - "railway/crossing", - "railway/switch", - "railway/buffer_stop", - "railway/signal" + "railway/tram", + "railway/subway", + "railway/narrow_gauge", + "railway/light_rail", + "railway/monorail", + "railway/funicular" ] }, "category-restriction": { @@ -129,28 +128,46 @@ "type/restriction" ] }, - "category-road": { + "category-road_major": { "icon": "iD-category-roads", - "name": "Road Features", + "name": "Major Roads", "members": [ - "highway/residential", "highway/motorway", + "highway/motorway_link", "highway/trunk", + "highway/trunk_link", "highway/primary", + "highway/primary_link", "highway/secondary", + "highway/secondary_link", "highway/tertiary", - "highway/living_street", + "highway/tertiary_link" + ] + }, + "category-road_minor": { + "icon": "iD-category-roads", + "name": "Minor Roads", + "members": [ "highway/unclassified", + "highway/residential", + "highway/living_street", "highway/service", "highway/track", - "highway/motorway_link", - "highway/trunk_link", - "highway/primary_link", - "highway/secondary_link", - "highway/tertiary_link", "highway/road" ] }, + "category-road_service": { + "icon": "iD-category-roads", + "name": "Service Roads", + "members": [ + "highway/service", + "highway/service/parking_aisle", + "highway/service/driveway", + "highway/service/alley", + "highway/service/emergency_access", + "highway/service/drive-through" + ] + }, "category-route": { "icon": "iD-route", "name": "Route Features", @@ -186,20 +203,28 @@ }, "category-water": { "icon": "maki-water", - "name": "Water Features", + "name": "Water Bodies", "members": [ - "natural/water/lake", - "natural/water/pond", - "natural/water/reservoir", "natural/water", + "natural/water/pond", + "natural/water/basin", + "natural/water/lake", + "natural/water/reservoir", + "natural/bay" + ] + }, + "category-waterway": { + "icon": "iD-waterway-stream", + "name": "Waterways", + "members": [ "waterway/stream", - "natural/water/stream", "waterway/drain", "waterway/river", - "natural/water/river", "waterway/canal", - "natural/water/canal", - "waterway/ditch" + "waterway/ditch", + "natural/water/stream", + "natural/water/river", + "natural/water/canal" ] } } diff --git a/data/presets/categories/path.json b/data/presets/categories/path.json index df2713284..9c10901fd 100644 --- a/data/presets/categories/path.json +++ b/data/presets/categories/path.json @@ -1,12 +1,12 @@ { "icon": "iD-category-path", - "name": "Path Features", + "name": "Paths", "members": [ + "highway/path", + "highway/footway", "highway/footway/marked", "highway/footway/sidewalk", "highway/steps", - "highway/path", - "highway/footway", "highway/cycleway", "highway/bridleway", "highway/pedestrian_line" diff --git a/data/presets/categories/rail.json b/data/presets/categories/rail.json index c37347bba..6fccacabf 100644 --- a/data/presets/categories/rail.json +++ b/data/presets/categories/rail.json @@ -1,15 +1,14 @@ { "icon": "iD-category-rail", - "name": "Rail Features", + "name": "Rails", "members": [ "railway/rail", - "railway/subway", - "railway/tram", "railway/disused", - "railway/level_crossing", - "railway/crossing", - "railway/switch", - "railway/buffer_stop", - "railway/signal" + "railway/tram", + "railway/subway", + "railway/narrow_gauge", + "railway/light_rail", + "railway/monorail", + "railway/funicular" ] } diff --git a/data/presets/categories/road.json b/data/presets/categories/road_major.json similarity index 58% rename from data/presets/categories/road.json rename to data/presets/categories/road_major.json index eedbb8b71..834dade3d 100644 --- a/data/presets/categories/road.json +++ b/data/presets/categories/road_major.json @@ -1,22 +1,16 @@ { "icon": "iD-category-roads", - "name": "Road Features", + "name": "Major Roads", "members": [ - "highway/residential", "highway/motorway", - "highway/trunk", - "highway/primary", - "highway/secondary", - "highway/tertiary", - "highway/living_street", - "highway/unclassified", - "highway/service", - "highway/track", "highway/motorway_link", + "highway/trunk", "highway/trunk_link", + "highway/primary", "highway/primary_link", + "highway/secondary", "highway/secondary_link", - "highway/tertiary_link", - "highway/road" + "highway/tertiary", + "highway/tertiary_link" ] } diff --git a/data/presets/categories/road_minor.json b/data/presets/categories/road_minor.json new file mode 100644 index 000000000..ff66bfacf --- /dev/null +++ b/data/presets/categories/road_minor.json @@ -0,0 +1,12 @@ +{ + "icon": "iD-category-roads", + "name": "Minor Roads", + "members": [ + "highway/unclassified", + "highway/residential", + "highway/living_street", + "highway/service", + "highway/track", + "highway/road" + ] +} diff --git a/data/presets/categories/road_service.json b/data/presets/categories/road_service.json new file mode 100644 index 000000000..5686395b9 --- /dev/null +++ b/data/presets/categories/road_service.json @@ -0,0 +1,12 @@ +{ + "icon": "iD-category-roads", + "name": "Service Roads", + "members": [ + "highway/service", + "highway/service/parking_aisle", + "highway/service/driveway", + "highway/service/alley", + "highway/service/emergency_access", + "highway/service/drive-through" + ] +} diff --git a/data/presets/categories/water.json b/data/presets/categories/water.json index e34dd397a..1a4eda92a 100644 --- a/data/presets/categories/water.json +++ b/data/presets/categories/water.json @@ -1,18 +1,12 @@ { "icon": "maki-water", - "name": "Water Features", + "name": "Water Bodies", "members": [ - "natural/water/lake", - "natural/water/pond", - "natural/water/reservoir", "natural/water", - "waterway/stream", - "natural/water/stream", - "waterway/drain", - "waterway/river", - "natural/water/river", - "waterway/canal", - "natural/water/canal", - "waterway/ditch" + "natural/water/pond", + "natural/water/basin", + "natural/water/lake", + "natural/water/reservoir", + "natural/bay" ] } diff --git a/data/presets/categories/waterway.json b/data/presets/categories/waterway.json new file mode 100644 index 000000000..0b531eb4d --- /dev/null +++ b/data/presets/categories/waterway.json @@ -0,0 +1,14 @@ +{ + "icon": "iD-waterway-stream", + "name": "Waterways", + "members": [ + "waterway/stream", + "waterway/drain", + "waterway/river", + "waterway/canal", + "waterway/ditch", + "natural/water/stream", + "natural/water/river", + "natural/water/canal" + ] +} diff --git a/data/presets/defaults.json b/data/presets/defaults.json index 8d2de9016..7668b15c2 100644 --- a/data/presets/defaults.json +++ b/data/presets/defaults.json @@ -13,10 +13,11 @@ "area" ], "line": [ - "category-road", + "category-road_major", + "category-road_minor", "category-rail", "category-path", - "category-water", + "category-waterway", "category-barrier", "category-natural", "category-utility", diff --git a/dist/locales/en.json b/dist/locales/en.json index b502c017f..6573e444f 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2082,16 +2082,22 @@ "name": "Natural Features" }, "category-path": { - "name": "Path Features" + "name": "Paths" }, "category-rail": { - "name": "Rail Features" + "name": "Rails" }, "category-restriction": { "name": "Restriction Features" }, - "category-road": { - "name": "Road Features" + "category-road_major": { + "name": "Major Roads" + }, + "category-road_minor": { + "name": "Minor Roads" + }, + "category-road_service": { + "name": "Service Roads" }, "category-route": { "name": "Route Features" @@ -2100,7 +2106,10 @@ "name": "Utility Features" }, "category-water": { - "name": "Water Features" + "name": "Water Bodies" + }, + "category-waterway": { + "name": "Waterways" } }, "fields": { From e0d439a6117c6be1472103f6082912994b11d099 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 09:42:56 -0500 Subject: [PATCH 55/65] Reorder presets in major roads category --- data/presets/categories.json | 8 ++++---- data/presets/categories/road_major.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/presets/categories.json b/data/presets/categories.json index 791906165..65969b484 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -133,14 +133,14 @@ "name": "Major Roads", "members": [ "highway/motorway", - "highway/motorway_link", "highway/trunk", - "highway/trunk_link", "highway/primary", - "highway/primary_link", "highway/secondary", - "highway/secondary_link", "highway/tertiary", + "highway/motorway_link", + "highway/trunk_link", + "highway/primary_link", + "highway/secondary_link", "highway/tertiary_link" ] }, diff --git a/data/presets/categories/road_major.json b/data/presets/categories/road_major.json index 834dade3d..1553d728a 100644 --- a/data/presets/categories/road_major.json +++ b/data/presets/categories/road_major.json @@ -3,14 +3,14 @@ "name": "Major Roads", "members": [ "highway/motorway", - "highway/motorway_link", "highway/trunk", - "highway/trunk_link", "highway/primary", - "highway/primary_link", "highway/secondary", - "highway/secondary_link", "highway/tertiary", + "highway/motorway_link", + "highway/trunk_link", + "highway/primary_link", + "highway/secondary_link", "highway/tertiary_link" ] } From 827ef0ece7a61e844e3987cd7b5cf80c0063a302 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 10:47:42 -0500 Subject: [PATCH 56/65] Fix search results popover rounded corners in Chrome --- css/80_app.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index 270034907..510fa8f70 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -640,6 +640,8 @@ button.add-note svg.icon { border-radius: 6px; overflow-y: auto; max-width: 350px; + /* ensure corners are rounded in Chrome */ + -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC); } .search-add .popover::-webkit-scrollbar { /* don't overlap rounded corners */ From 36ad1b02427148e9bc44ac6b28a7072c9ef9fc68 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 11:39:17 -0500 Subject: [PATCH 57/65] Use "building" label instead of "area" when adding POIs as buildings --- data/core.yaml | 2 ++ dist/locales/en.json | 3 +++ modules/presets/index.js | 4 ++++ modules/ui/modes.js | 6 +++++- modules/ui/search_add.js | 29 +++++++++++++++++++---------- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 183f5e7cc..e2f925081 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -38,6 +38,8 @@ en: title: "Add {feature} as a line" area: title: "Add {feature} as an area" + building: + title: "Add {feature} as a building" vertex: title: "Add {feature} as a vertex" browse: diff --git a/dist/locales/en.json b/dist/locales/en.json index 6573e444f..731472aec 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -49,6 +49,9 @@ "area": { "title": "Add {feature} as an area" }, + "building": { + "title": "Add {feature} as a building" + }, "vertex": { "title": "Add {feature} as a vertex" } diff --git a/modules/presets/index.js b/modules/presets/index.js index 44a3694d6..d8b14f52e 100644 --- a/modules/presets/index.js +++ b/modules/presets/index.js @@ -268,6 +268,10 @@ export function presetIndex() { return presetCollection(_uniq(rec.concat(def).concat(all.item(geometry)))); }; + all.recent = function(geometries, n) { + return presetCollection(_recent.matchAnyGeometry(geometries).collection.slice(0, n - 1)); + } + all.choose = function(preset) { if (preset.searchable !== false) { _recent = presetCollection(_uniq([preset].concat(_recent.collection))); diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 9d0e6beb3..394720fcf 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -78,7 +78,11 @@ export function uiModes(context) { }); var tooltipTitleID = 'modes.add_preset.title'; if (relevantMatchingGeometry.length !== 1) { - tooltipTitleID = 'modes.add_preset.' + d.geom + '.title'; + if (preset.setTags({}, d.geom).building) { + tooltipTitleID = 'modes.add_preset.building.title'; + } else { + tooltipTitleID = 'modes.add_preset.' + d.geom + '.title'; + } } var favoriteMode = { button: markerClass, diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 8ce069f9b..4df8f27f7 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -132,17 +132,22 @@ export function uiSearchAdd(context) { } }) .on('input', function () { + + popover.selectAll('.subsection').remove(); + var value = search.property('value'); + var results; if (value.length) { - popover.selectAll('.subsection').remove(); - var results = presets.search(value, shownGeometry); - list.call(drawList, results); - popover.selectAll('.list .list-item.focused') - .classed('focused', false); - focusListItem(popover.selectAll('.list > .list-item:first-child')); + results = presets.search(value, shownGeometry); } else { - popover.selectAll('.list > *').remove(); + results = context.presets().recent(shownGeometry, 36); } + + list.call(drawList, results); + + popover.selectAll('.list .list-item.focused') + .classed('focused', false); + focusListItem(popover.selectAll('.list > .list-item:first-child')); }); searchWrap @@ -195,6 +200,9 @@ export function uiSearchAdd(context) { } function itemForPreset(preset) { + if (preset.members) { + return CategoryItem(preset); + } var supportedGeometry = preset.geometry.filter(function(geometry) { return shownGeometry.indexOf(geometry) !== -1; }).sort(); @@ -212,9 +220,6 @@ export function uiSearchAdd(context) { function drawList(list, presets) { var collection = presets.collection.map(function(preset) { - if (preset.members) { - return CategoryItem(preset); - } return itemForPreset(preset); }); @@ -282,6 +287,9 @@ export function uiSearchAdd(context) { label.append('span') .text(function(d) { if (d.isSubitem) { + if (d.preset.setTags({}, d.geometry).building) { + return t('presets.presets.building.name'); + } return t('modes.add_' + d.geometry + '.title'); } return d.preset.name(); @@ -417,6 +425,7 @@ export function uiSearchAdd(context) { mode = modeAddArea(context, modeInfo); } search.node().blur(); + context.presets().choose(preset); context.enter(mode); }; return item; From 7da13f50332fac8908882d9a336ef8653846062c Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 11:46:05 -0500 Subject: [PATCH 58/65] Don't show "as point" in favorites tooltip when only point or vertex allowed --- modules/ui/modes.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 394720fcf..9b46f2796 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -73,11 +73,16 @@ export function uiModes(context) { markerClass += ' add-generic-preset'; } - var relevantMatchingGeometry = preset.geometry.filter(function(geometry) { + var supportedGeometry = preset.geometry.filter(function(geometry) { return ['vertex', 'point', 'line', 'area'].indexOf(geometry) !== -1; }); + var vertexIndex = supportedGeometry.indexOf('vertex'); + if (vertexIndex !== -1 && supportedGeometry.indexOf('point') !== -1) { + // both point and vertex allowed, just combine them + supportedGeometry.splice(vertexIndex, 1); + } var tooltipTitleID = 'modes.add_preset.title'; - if (relevantMatchingGeometry.length !== 1) { + if (supportedGeometry.length !== 1) { if (preset.setTags({}, d.geom).building) { tooltipTitleID = 'modes.add_preset.building.title'; } else { From 209cb57d71b0f163a005baedb138506f16c4f2ea Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 11:55:46 -0500 Subject: [PATCH 59/65] Escape key to blur search field --- modules/ui/search_add.js | 176 ++++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 84 deletions(-) diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 4df8f27f7..892030a5a 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -41,72 +41,6 @@ export function uiSearchAdd(context) { .attr('placeholder', t('modes.add_feature.title')) .attr('type', 'search') .call(utilNoAuto) - .on('keypress', function() { - // enter/return - if (d3_event.keyCode === 13) { - popover.selectAll('.list .list-item.focused button.choose') - .each(function(d) { d.choose.call(this); }); - d3_event.preventDefault(); - d3_event.stopPropagation(); - } - }) - .on('keydown', function(){ - // up/down arrow key navigation - - var nextFocus, - priorFocus, - parentSubsection; - if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] || - d3_event.keyCode === utilKeybinding.keyCodes.tab && !d3_event.shiftKey) { - d3_event.preventDefault(); - d3_event.stopPropagation(); - - priorFocus = popover.selectAll('.list .list-item.focused'); - if (priorFocus.empty()) { - nextFocus = popover.selectAll('.list > .list-item:first-child'); - } else { - nextFocus = popover.selectAll('.list .list-item.focused + .list-item'); - if (nextFocus.empty()) { - nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling) - .selectAll('.list-item:first-child'); - } - if (nextFocus.empty()) { - parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); - if (parentSubsection && parentSubsection.nextElementSibling) { - nextFocus = d3_select(parentSubsection.nextElementSibling); - } - } - } - if (!nextFocus.empty()) { - focusListItem(nextFocus); - priorFocus.classed('focused', false); - } - - } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑'] || - d3_event.keyCode === utilKeybinding.keyCodes.tab && d3_event.shiftKey) { - d3_event.preventDefault(); - d3_event.stopPropagation(); - - priorFocus = popover.selectAll('.list .list-item.focused'); - if (!priorFocus.empty()) { - - nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling); - if (!nextFocus.empty() && !nextFocus.classed('list-item')) { - nextFocus = nextFocus.selectAll('.list-item:last-child'); - } - if (nextFocus.empty()) { - parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); - if (parentSubsection && parentSubsection.previousElementSibling) { - nextFocus = d3_select(parentSubsection.previousElementSibling); - } - } - if (!nextFocus.empty()) { - focusListItem(nextFocus); - priorFocus.classed('focused', false); - } - } - } - }) .on('mousedown', function() { search.attr('clicking', true); }) @@ -131,24 +65,9 @@ export function uiSearchAdd(context) { search.attr('focusing', null); } }) - .on('input', function () { - - popover.selectAll('.subsection').remove(); - - var value = search.property('value'); - var results; - if (value.length) { - results = presets.search(value, shownGeometry); - } else { - results = context.presets().recent(shownGeometry, 36); - } - - list.call(drawList, results); - - popover.selectAll('.list .list-item.focused') - .classed('focused', false); - focusListItem(popover.selectAll('.list > .list-item:first-child')); - }); + .on('keypress', keypress) + .on('keydown', keydown) + .on('input', searchInput); searchWrap .call(svgIcon('#iD-icon-search', 'search-icon pre-text')); @@ -176,6 +95,95 @@ export function uiSearchAdd(context) { }); } + function keypress() { + if (d3_event.keyCode === utilKeybinding.keyCodes.enter) { + popover.selectAll('.list .list-item.focused button.choose') + .each(function(d) { d.choose.call(this); }); + d3_event.preventDefault(); + d3_event.stopPropagation(); + } + } + + function keydown() { + + var nextFocus, + priorFocus, + parentSubsection; + if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] || + d3_event.keyCode === utilKeybinding.keyCodes.tab && !d3_event.shiftKey) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + + priorFocus = popover.selectAll('.list .list-item.focused'); + if (priorFocus.empty()) { + nextFocus = popover.selectAll('.list > .list-item:first-child'); + } else { + nextFocus = popover.selectAll('.list .list-item.focused + .list-item'); + if (nextFocus.empty()) { + nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling) + .selectAll('.list-item:first-child'); + } + if (nextFocus.empty()) { + parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); + if (parentSubsection && parentSubsection.nextElementSibling) { + nextFocus = d3_select(parentSubsection.nextElementSibling); + } + } + } + if (!nextFocus.empty()) { + focusListItem(nextFocus); + priorFocus.classed('focused', false); + } + + } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑'] || + d3_event.keyCode === utilKeybinding.keyCodes.tab && d3_event.shiftKey) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + + priorFocus = popover.selectAll('.list .list-item.focused'); + if (!priorFocus.empty()) { + + nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling); + if (!nextFocus.empty() && !nextFocus.classed('list-item')) { + nextFocus = nextFocus.selectAll('.list-item:last-child'); + } + if (nextFocus.empty()) { + parentSubsection = priorFocus.nodes()[0].closest('.list .subsection'); + if (parentSubsection && parentSubsection.previousElementSibling) { + nextFocus = d3_select(parentSubsection.previousElementSibling); + } + } + if (!nextFocus.empty()) { + focusListItem(nextFocus); + priorFocus.classed('focused', false); + } + } + } else if (d3_event.keyCode === utilKeybinding.keyCodes.esc) { + search.node().blur(); + d3_event.preventDefault(); + d3_event.stopPropagation(); + } + } + + function searchInput() { + + popover.selectAll('.subsection').remove(); + + var value = search.property('value'); + var results; + if (value.length) { + results = presets.search(value, shownGeometry); + } else { + results = context.presets().recent(shownGeometry, 36); + } + + list.call(drawList, results); + + popover.selectAll('.list .list-item.focused') + .classed('focused', false); + focusListItem(popover.selectAll('.list > .list-item:first-child')); + } + function focusListItem(selection) { if (!selection.empty()) { selection.classed('focused', true); From 9c19a1d714e31308c53e09d7c3a6b6eccc7666c8 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 12:19:44 -0500 Subject: [PATCH 60/65] Nicer display of name suggestion index presets --- css/80_app.css | 5 ++++- modules/presets/index.js | 2 +- modules/ui/modes.js | 4 ++-- modules/ui/search_add.js | 36 ++++++++++++++++++++++++++---------- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 510fa8f70..86f375afd 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -668,9 +668,12 @@ button.add-note svg.icon { position: relative; display: flex; align-items: center; - line-height: 1.2em; + line-height: 1.3em; width: 100%; } +.search-add .list-item .label .namepart:nth-child(2) { + font-weight: normal; +} .search-add .list-item.disabled .preset-icon-container, .search-add .list-item.disabled .label { opacity: 0.55; diff --git a/modules/presets/index.js b/modules/presets/index.js index d8b14f52e..37a8b9338 100644 --- a/modules/presets/index.js +++ b/modules/presets/index.js @@ -270,7 +270,7 @@ export function presetIndex() { all.recent = function(geometries, n) { return presetCollection(_recent.matchAnyGeometry(geometries).collection.slice(0, n - 1)); - } + }; all.choose = function(preset) { if (preset.searchable !== false) { diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 9b46f2796..c82f3d918 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -66,7 +66,7 @@ export function uiModes(context) { var favoritePresets = context.getFavoritePresets(); var favoriteModes = favoritePresets.map(function(d, index) { var preset = context.presets().item(d.id); - var presetName = preset.name(); + var presetName = preset.name().split(' – ')[0]; var markerClass = 'add-preset add-' + d.geom + ' add-preset-' + presetName.replace(/\s+/g, '_') + '-' + d.geom; // replace spaces with underscores to avoid css interpretation if (preset.isFallback()) { @@ -92,7 +92,7 @@ export function uiModes(context) { var favoriteMode = { button: markerClass, title: presetName, - description: t(tooltipTitleID, { feature: presetName }), + description: t(tooltipTitleID, { feature: '' + presetName + '' }), preset: preset, geometry: d.geom }; diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 892030a5a..ae2de60a0 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -292,16 +292,32 @@ export function uiSearchAdd(context) { } }); - label.append('span') - .text(function(d) { - if (d.isSubitem) { - if (d.preset.setTags({}, d.geometry).building) { - return t('presets.presets.building.name'); - } - return t('modes.add_' + d.geometry + '.title'); - } - return d.preset.name(); - }); + label.each(function(d) { + + if ((d.geometry && !d.isSubitem) || d.geometries) { + // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc) + d3_select(this) + .append('div') + .attr('class', 'label-inner') + .selectAll('.namepart') + .data(d.preset.name().split(' – ')) + .enter() + .append('div') + .attr('class', 'namepart') + .text(function(d) { return d; }); + } else { + d3_select(this).append('span') + .text(function(d) { + if (d.isSubitem) { + if (d.preset.setTags({}, d.geometry).building) { + return t('presets.presets.building.name'); + } + return t('modes.add_' + d.geometry + '.title'); + } + return d.preset.name(); + }); + } + }); row.each(function(d) { if (d.geometry) { From 0dfd65787b30ecba93e5310f1592c9af7658b378 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 13:08:22 -0500 Subject: [PATCH 61/65] Fix issue with stale vertex icon --- modules/ui/preset_icon.js | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/modules/ui/preset_icon.js b/modules/ui/preset_icon.js index 9bcd404fb..bad8214fe 100644 --- a/modules/ui/preset_icon.js +++ b/modules/ui/preset_icon.js @@ -24,7 +24,7 @@ export function uiPresetIcon() { else if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex'; else if (isSmall() && geom === 'point') - return null; + return ''; else return 'maki-marker-stroked'; } @@ -150,9 +150,9 @@ export function uiPresetIcon() { var p = preset.apply(this, arguments); var geom = geometry ? geometry.apply(this, arguments) : null; var picon = getIcon(p, geom); - var isMaki = picon ? /^maki-/.test(picon) : false; - var isTemaki = picon ? /^temaki-/.test(picon) : false; - var isFa = picon ? /^fa[srb]-/.test(picon) : false; + var isMaki = /^maki-/.test(picon); + var isTemaki = /^temaki-/.test(picon); + var isFa = /^fa[srb]-/.test(picon); var isiDIcon = !(isMaki || isTemaki || isFa); var isCategory = !p.setTags; var drawLine = geom === 'line' && !isCategory; @@ -231,21 +231,18 @@ export function uiPresetIcon() { .call(svgIcon('')) .merge(icon); - if (picon) { + icon + .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')) + .classed('framed', isFramed) + .classed('preset-icon-iD', isiDIcon); - icon - .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')) - .classed('framed', isFramed) - .classed('preset-icon-iD', isiDIcon); + icon.selectAll('svg') + .attr('class', function() { + return 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses); + }); - icon.selectAll('svg') - .attr('class', function() { - return 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses); - }); - - icon.selectAll('use') - .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : '')); - } + icon.selectAll('use') + .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : '')); } From 137aefb4403d60e310fb58ff84b51712f73ffa52 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 13:31:16 -0500 Subject: [PATCH 62/65] Add tooltip to search field Make search shortcut translatable --- css/80_app.css | 3 +++ data/core.yaml | 2 ++ dist/locales/en.json | 4 +++- modules/ui/search_add.js | 14 ++++++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 86f375afd..d769d858b 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -609,6 +609,9 @@ button.add-note svg.icon { border-radius: 20px 0 0 20px; background: #fff; } +.search-add .search-wrap.focused .tooltip { + display: none; +} .search-add .search-wrap:nth-last-child(2) { border-radius: 20px; border: none; diff --git a/data/core.yaml b/data/core.yaml index e2f925081..236f002c0 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -11,6 +11,8 @@ en: modes: add_feature: title: Add a feature + description: "Search for features to add to the map." + key: Tab add_area: title: Area description: "Add parks, buildings, lakes or other areas to the map." diff --git a/dist/locales/en.json b/dist/locales/en.json index 731472aec..21bd47a37 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -12,7 +12,9 @@ }, "modes": { "add_feature": { - "title": "Add a feature" + "title": "Add a feature", + "description": "Search for features to add to the map.", + "key": "Tab" }, "add_area": { "title": "Area", diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index ae2de60a0..4ed356420 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -15,6 +15,7 @@ import { import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg/index'; import { tooltip } from '../util/tooltip'; +import { uiTooltipHtml } from './tooltipHtml'; import { uiPresetFavorite } from './preset_favorite'; import { uiPresetIcon } from './preset_icon'; import { utilKeybinding, utilNoAuto, utilRebind } from '../util'; @@ -31,9 +32,16 @@ export function uiSearchAdd(context) { presets = context.presets().matchAnyGeometry(shownGeometry); + var key = t('modes.add_feature.key'); + var searchWrap = selection .append('div') - .attr('class', 'search-wrap'); + .attr('class', 'search-wrap') + .call(tooltip() + .placement('bottom') + .html(true) + .title(function(d) { return uiTooltipHtml(t('modes.add_feature.description'), key); }) + ); search = searchWrap .append('input') @@ -48,6 +56,7 @@ export function uiSearchAdd(context) { search.attr('clicking', null); }) .on('focus', function() { + searchWrap.classed('focused', true); if (search.attr('clicking')) { search.attr('focusing', true); search.attr('clicking', null); @@ -57,6 +66,7 @@ export function uiSearchAdd(context) { popover.classed('hide', false); }) .on('blur', function() { + searchWrap.classed('focused', false); popover.classed('hide', true); }) .on('click', function() { @@ -88,7 +98,7 @@ export function uiSearchAdd(context) { context.features().on('change.search-add', updateForFeatureHiddenState); - context.keybinding().on('tab', function() { + context.keybinding().on(key, function() { search.node().focus(); d3_event.preventDefault(); d3_event.stopPropagation(); From 34f12789bb46fd27259d15abc7963a835ac80a61 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 13:56:48 -0500 Subject: [PATCH 63/65] Update shortcut help for new shortcuts --- data/core.yaml | 2 ++ data/shortcuts.json | 16 +++++++--------- dist/locales/en.json | 2 ++ modules/ui/shortcuts.js | 7 ++++++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 236f002c0..5cba09816 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1654,10 +1654,12 @@ en: title: "Editing" drawing: title: "Drawing" + focus_add_feature: "Focus the feature search field" add_point: "'Add point' mode" add_line: "'Add line' mode" add_area: "'Add area' mode" add_note: "'Add note' mode" + add_favorite: "Add a favorite feature" place_point: "Place a point or note" disable_snap: "Hold to disable point snapping" stop_line: "Finish drawing a line or area" diff --git a/data/shortcuts.json b/data/shortcuts.json index 376cb9a94..484de5af1 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -157,19 +157,17 @@ "text": "shortcuts.editing.drawing.title" }, { - "shortcuts": ["1"], - "text": "shortcuts.editing.drawing.add_point" + "shortcuts": ["modes.add_feature.key"], + "text": "shortcuts.editing.drawing.focus_add_feature" }, { - "shortcuts": ["2"], - "text": "shortcuts.editing.drawing.add_line" + "shortcuts": ["1", "2", "3"], + "text": "shortcuts.editing.drawing.add_favorite", + "separator": ",", + "suffix": "…" }, { - "shortcuts": ["3"], - "text": "shortcuts.editing.drawing.add_area" - }, - { - "shortcuts": ["4"], + "shortcuts": ["modes.add_note.key"], "text": "shortcuts.editing.drawing.add_note" }, { diff --git a/dist/locales/en.json b/dist/locales/en.json index 21bd47a37..477ec20ae 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1997,10 +1997,12 @@ "title": "Editing", "drawing": { "title": "Drawing", + "focus_add_feature": "Focus the feature search field", "add_point": "'Add point' mode", "add_line": "'Add line' mode", "add_area": "'Add area' mode", "add_note": "'Add note' mode", + "add_favorite": "Add a favorite feature", "place_point": "Place a point or note", "disable_snap": "Hold to disable point snapping", "stop_line": "Finish drawing a line or area" diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index 252375c4b..8842d96b8 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -189,7 +189,8 @@ export function uiShortcuts(context) { return _uniq(arr).map(function(s) { return { shortcut: s, - separator: d.separator + separator: d.separator, + suffix: d.suffix }; }); }) @@ -212,6 +213,10 @@ export function uiShortcuts(context) { selection .append('span') .text(d.separator || '\u00a0' + t('shortcuts.or') + '\u00a0'); + } else if (i === nodes.length - 1 && d.suffix) { + selection + .append('span') + .text(d.suffix); } }); From ca8f4994824a63b8bbf285d0c4b6bb4fa087cf97 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 14:59:21 -0500 Subject: [PATCH 64/65] Right-to-left layout support for drag-and-drop favorites, favorite shortcuts, and feature search --- css/80_app.css | 11 ++++++++++- modules/ui/modes.js | 38 +++++++++++++++++++++++++++++--------- modules/ui/search_add.js | 2 +- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index d769d858b..d5a647638 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -634,6 +634,10 @@ button.add-note svg.icon { top: 10px; pointer-events: none; } +[dir='rtl'] .search-add .search-icon { + left: auto; + right: 10px; +} .search-add .popover { max-height: 250px; width: 100%; @@ -723,9 +727,14 @@ button.add-note svg.icon { color: #666; } .search-add .subsection { - padding-left: 6px; background-color: #CBCBCB; } +[dir='ltr'] .search-add .subsection { + padding-left: 6px; +} +[dir='rtl'] .search-add .subsection { + padding-right: 6px; +} /* Header for modals / panes ------------------------------------------------------- */ .header { diff --git a/modules/ui/modes.js b/modules/ui/modes.js index c82f3d918..238581e8e 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -11,7 +11,7 @@ import { } from '../modes'; import { svgIcon } from '../svg'; -import { t } from '../util/locale'; +import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; import { uiPresetIcon } from './preset_icon'; import { uiTooltipHtml } from './tooltipHtml'; @@ -96,13 +96,26 @@ export function uiModes(context) { preset: preset, geometry: d.geom }; - var keyCode = index + 1; - if (keyCode <= 10) { - if (keyCode === 10) { + var keyCode; + if (textDirection === 'ltr') { + // use number row order: 1 2 3 4 5 6 7 8 9 0 + if (index === 9) { keyCode = 0; + } else if (index < 10) { + keyCode = index + 1; } + } else { + // use number row order from right to left + if (index === 0) { + keyCode = 0; + } else if (index < 10) { + keyCode = 10 - index; + } + } + if (keyCode !== null) { favoriteMode.key = keyCode.toString(); } + var mode; switch (d.geom) { case 'point': @@ -201,22 +214,29 @@ export function uiModes(context) { selection.selectAll('button.add-preset') .style('transform', function(d2, index2) { + var node = d3_select(this).node(); if (index === index2) { return 'translate(' + x + 'px, ' + y + 'px)'; } else if (y > 50) { if (index2 > index) { - return 'translateX(-100%)'; + return 'translateX(' + (textDirection === 'rtl' ? '' : '-') + '100%)'; } - } else if (index2 > index && d3_event.x > d3_select(this).node().offsetLeft) { + } else if (index2 > index && ( + (d3_event.x > node.offsetLeft && textDirection === 'ltr') || + (d3_event.x < node.offsetLeft + node.offsetWidth && textDirection === 'rtl') + )) { if (targetIndex === null || index2 > targetIndex) { targetIndex = index2; } - return 'translateX(-100%)'; - } else if (index2 < index && d3_event.x < d3_select(this).node().offsetLeft + d3_select(this).node().offsetWidth) { + return 'translateX(' + (textDirection === 'rtl' ? '' : '-') + '100%)'; + } else if (index2 < index && ( + (d3_event.x < node.offsetLeft + node.offsetWidth && textDirection === 'ltr') || + (d3_event.x > node.offsetLeft && textDirection === 'rtl') + )) { if (targetIndex === null || index2 < targetIndex) { targetIndex = index2; } - return 'translateX(100%)'; + return 'translateX(' + (textDirection === 'rtl' ? '-' : '') + '100%)'; } return null; }); diff --git a/modules/ui/search_add.js b/modules/ui/search_add.js index 4ed356420..24d45d503 100644 --- a/modules/ui/search_add.js +++ b/modules/ui/search_add.js @@ -40,7 +40,7 @@ export function uiSearchAdd(context) { .call(tooltip() .placement('bottom') .html(true) - .title(function(d) { return uiTooltipHtml(t('modes.add_feature.description'), key); }) + .title(function() { return uiTooltipHtml(t('modes.add_feature.description'), key); }) ); search = searchWrap From 24e0a1996a73d8d09b85ac0b06fac36edcdefd5e Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 6 Mar 2019 15:50:02 -0500 Subject: [PATCH 65/65] Replace redundant category icons --- data/presets/categories.json | 12 ++++++------ data/presets/categories/path.json | 2 +- data/presets/categories/rail.json | 2 +- data/presets/categories/road_major.json | 2 +- data/presets/categories/road_minor.json | 2 +- data/presets/categories/road_service.json | 2 +- data/presets/categories/utility.json | 2 +- svg/iD-sprite/presets/category-path.svg | 5 ----- svg/iD-sprite/presets/category-rail.svg | 5 ----- svg/iD-sprite/presets/category-roads.svg | 5 ----- svg/iD-sprite/presets/category-utility.svg | 8 -------- svg/iD-sprite/presets/category-water.svg | 6 ------ 12 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 svg/iD-sprite/presets/category-path.svg delete mode 100644 svg/iD-sprite/presets/category-rail.svg delete mode 100644 svg/iD-sprite/presets/category-roads.svg delete mode 100644 svg/iD-sprite/presets/category-utility.svg delete mode 100644 svg/iD-sprite/presets/category-water.svg diff --git a/data/presets/categories.json b/data/presets/categories.json index 65969b484..b7ecc20dc 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -86,7 +86,7 @@ ] }, "category-path": { - "icon": "iD-category-path", + "icon": "temaki-pedestrian", "name": "Paths", "members": [ "highway/path", @@ -100,7 +100,7 @@ ] }, "category-rail": { - "icon": "iD-category-rail", + "icon": "iD-railway-rail", "name": "Rails", "members": [ "railway/rail", @@ -129,7 +129,7 @@ ] }, "category-road_major": { - "icon": "iD-category-roads", + "icon": "iD-highway-unclassified", "name": "Major Roads", "members": [ "highway/motorway", @@ -145,7 +145,7 @@ ] }, "category-road_minor": { - "icon": "iD-category-roads", + "icon": "iD-highway-unclassified", "name": "Minor Roads", "members": [ "highway/unclassified", @@ -157,7 +157,7 @@ ] }, "category-road_service": { - "icon": "iD-category-roads", + "icon": "iD-highway-service", "name": "Service Roads", "members": [ "highway/service", @@ -192,7 +192,7 @@ ] }, "category-utility": { - "icon": "iD-category-utility", + "icon": "iD-power-line", "name": "Utility Features", "members": [ "power/line", diff --git a/data/presets/categories/path.json b/data/presets/categories/path.json index 9c10901fd..d48958cde 100644 --- a/data/presets/categories/path.json +++ b/data/presets/categories/path.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-path", + "icon": "temaki-pedestrian", "name": "Paths", "members": [ "highway/path", diff --git a/data/presets/categories/rail.json b/data/presets/categories/rail.json index 6fccacabf..89b226b35 100644 --- a/data/presets/categories/rail.json +++ b/data/presets/categories/rail.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-rail", + "icon": "iD-railway-rail", "name": "Rails", "members": [ "railway/rail", diff --git a/data/presets/categories/road_major.json b/data/presets/categories/road_major.json index 1553d728a..0a0d912a1 100644 --- a/data/presets/categories/road_major.json +++ b/data/presets/categories/road_major.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-roads", + "icon": "iD-highway-unclassified", "name": "Major Roads", "members": [ "highway/motorway", diff --git a/data/presets/categories/road_minor.json b/data/presets/categories/road_minor.json index ff66bfacf..5b7d5aeaa 100644 --- a/data/presets/categories/road_minor.json +++ b/data/presets/categories/road_minor.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-roads", + "icon": "iD-highway-unclassified", "name": "Minor Roads", "members": [ "highway/unclassified", diff --git a/data/presets/categories/road_service.json b/data/presets/categories/road_service.json index 5686395b9..632345405 100644 --- a/data/presets/categories/road_service.json +++ b/data/presets/categories/road_service.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-roads", + "icon": "iD-highway-service", "name": "Service Roads", "members": [ "highway/service", diff --git a/data/presets/categories/utility.json b/data/presets/categories/utility.json index 1509c4ab1..215e331fb 100644 --- a/data/presets/categories/utility.json +++ b/data/presets/categories/utility.json @@ -1,5 +1,5 @@ { - "icon": "iD-category-utility", + "icon": "iD-power-line", "name": "Utility Features", "members": [ "power/line", diff --git a/svg/iD-sprite/presets/category-path.svg b/svg/iD-sprite/presets/category-path.svg deleted file mode 100644 index 8d4070adf..000000000 --- a/svg/iD-sprite/presets/category-path.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/svg/iD-sprite/presets/category-rail.svg b/svg/iD-sprite/presets/category-rail.svg deleted file mode 100644 index b9369f2a2..000000000 --- a/svg/iD-sprite/presets/category-rail.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/svg/iD-sprite/presets/category-roads.svg b/svg/iD-sprite/presets/category-roads.svg deleted file mode 100644 index e298e81a2..000000000 --- a/svg/iD-sprite/presets/category-roads.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/svg/iD-sprite/presets/category-utility.svg b/svg/iD-sprite/presets/category-utility.svg deleted file mode 100644 index e819114fc..000000000 --- a/svg/iD-sprite/presets/category-utility.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/svg/iD-sprite/presets/category-water.svg b/svg/iD-sprite/presets/category-water.svg deleted file mode 100644 index 47da3fd11..000000000 --- a/svg/iD-sprite/presets/category-water.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file