From 1faa6ad83964e25df2ec2fb42104e51b8a7ab5d3 Mon Sep 17 00:00:00 2001 From: Brian Hatchl Date: Wed, 20 Feb 2019 15:23:58 -0500 Subject: [PATCH] Favorite preset --- css/55_cursors.css | 1 + css/80_app.css | 22 ++++++++++- data/core.yaml | 2 + dist/locales/en.json | 13 ++---- modules/behavior/draw_way.js | 2 +- modules/core/context.js | 33 +++++++++++++++- modules/modes/add_area.js | 11 +++--- modules/modes/add_line.js | 15 ++++--- modules/modes/add_point.js | 9 +++-- modules/modes/draw_area.js | 4 +- modules/modes/draw_line.js | 4 +- modules/operations/continue.js | 2 +- modules/ui/entity_editor.js | 8 ++++ modules/ui/modes.js | 37 ++++++++++++++++- modules/ui/preset_favorite.js | 57 +++++++++++++++++++++++++++ svg/iD-sprite/icons/icon-favorite.svg | 6 +++ 16 files changed, 193 insertions(+), 33 deletions(-) create mode 100644 modules/ui/preset_favorite.js create mode 100644 svg/iD-sprite/icons/icon-favorite.svg diff --git a/css/55_cursors.css b/css/55_cursors.css index 1a4c8cf7f..70edc9c55 100644 --- a/css/55_cursors.css +++ b/css/55_cursors.css @@ -54,6 +54,7 @@ cursor: url(img/cursor-select-remove.png), pointer; /* FF */ } +[class*=" mode-add-preset-"] #map, .mode-draw-line #map, .mode-draw-area #map, .mode-add-line #map, diff --git a/css/80_app.css b/css/80_app.css index 265b21f9d..f80d96425 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1150,28 +1150,47 @@ a.hide-toggle { background-color: #ececec; } +.preset-list-item button.preset-favorite-button, .preset-list-item button.tag-reference-button { height: 100%; border: 1px solid #ccc; flex: 32px; background: #f6f6f6; } +[dir='ltr'] .preset-list-item button.preset-favorite-button, [dir='ltr'] .preset-list-item button.tag-reference-button { border-left: none; - border-radius: 0 4px 4px 0; + border-radius: 0; } +[dir='rtl'] .preset-list-item button.preset-favorite-button, [dir='rtl'] .preset-list-item button.tag-reference-button { border-right: none; + border-radius: 0; +} +[dir='ltr'] .preset-list-item button:last-child { + border-radius: 0 4px 4px 0; +} +[dir='rtl'] .preset-list-item button:last-child { border-radius: 4px 0 0 4px; } +.preset-list-item button.preset-favorite-button:hover, .preset-list-item button.tag-reference-button:hover { background: #f1f1f1; } +.preset-list-item button.preset-favorite-button .icon, .preset-list-item button.tag-reference-button .icon { opacity: .5; } +.preset-list-item button.preset-favorite-button .icon { + fill-opacity: 0; + stroke-width: 1.6; +} +.preset-list-item button.preset-favorite-button.active .icon { + fill-opacity: inherit; +} + img.tag-reference-wiki-image { float: right; width: 33.3333%; @@ -2474,6 +2493,7 @@ input.key-trap { /* hide and remove from layout */ .inspector-hidden, +.inspector-hover .preset-list-button-wrap .preset-favorite-button, .inspector-hover .preset-list-button-wrap .tag-reference-button, .inspector-hover label input[type="checkbox"], .inspector-hover label input[type="radio"], diff --git a/data/core.yaml b/data/core.yaml index b2011a69d..55b96dc44 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -7,6 +7,7 @@ en: zoom_to: zoom to copy: copy open_wikidata: open on wikidata.org + favorite: add to favorites modes: add_area: title: Area @@ -40,6 +41,7 @@ en: vertex: Added a node to a way. relation: Added a relation. note: Added a note. + title: Add start: annotation: line: Started a line. diff --git a/dist/locales/en.json b/dist/locales/en.json index 9aca0b295..2f24a2eeb 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -7,7 +7,8 @@ "undo": "undo", "zoom_to": "zoom to", "copy": "copy", - "open_wikidata": "open on wikidata.org" + "open_wikidata": "open on wikidata.org", + "favorite": "add to favorites" }, "modes": { "add_area": { @@ -51,7 +52,8 @@ "vertex": "Added a node to a way.", "relation": "Added a relation.", "note": "Added a note." - } + }, + "title": "Add" }, "start": { "annotation": { @@ -8370,13 +8372,6 @@ "description": "Japan GSI ortho Imagery. Usually better than bing, but a bit older.", "name": "Japan GSI ortho Imagery" }, - "gsi.go.jp_airphoto": { - "attribution": { - "text": "GSI Japan" - }, - "description": "Japan GSI airphoto Imagery. Not fully orthorectified, but a bit newer and/or differently covered than GSI ortho Imagery.", - "name": "Japan GSI airphoto Imagery" - }, "gsi.go.jp_std_map": { "attribution": { "text": "GSI Japan" diff --git a/modules/behavior/draw_way.js b/modules/behavior/draw_way.js index 847803d15..dc099acf6 100644 --- a/modules/behavior/draw_way.js +++ b/modules/behavior/draw_way.js @@ -323,7 +323,7 @@ export function behaviorDrawWay(context, wayID, index, mode, startGraph, baselin window.setTimeout(function() { context.map().dblclickEnable(true); }, 1000); - var isNewFeature = !mode.isContinuing; + var isNewFeature = !mode.isContinuing && mode.button.indexOf('add-preset-') === -1; context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature)); }; diff --git a/modules/core/context.js b/modules/core/context.js index 6bc3ae849..4295f9db5 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -57,7 +57,7 @@ export function coreContext() { addTranslation('en', dataEn); setLocale('en'); - var dispatch = d3_dispatch('enter', 'exit', 'change'); + var dispatch = d3_dispatch('enter', 'exit', 'change', 'favoritePreset'); // https://github.com/openstreetmap/iD/issues/772 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9 @@ -312,7 +312,38 @@ export function coreContext() { /* Presets */ var presets; context.presets = function() { return presets; }; + //get favorites from local storage + context.getFavoritePresets = function() { + return JSON.parse(context.storage('favorite_presets')) || []; + } + context.favoritePreset = function(preset, geom) { + var favs = context.getFavoritePresets(); + //add/remove favorites from local storage + if (context.isFavoritePreset(preset, geom)) { + favs = favs.filter(function(d) { + return !(d.id === preset.id && d.geom === geom); + }); + } else { + //only allow 3 favorites + //replace the last one + if (favs.length === 3) { + favs = favs.slice(0,2); + } + favs.push({id: preset.id, geom: geom}); + } + + context.storage('favorite_presets', JSON.stringify(favs)); + + //and call update on modes + dispatch.call('favoritePreset'); + }; + context.isFavoritePreset = function(preset, geom) { + var favs = context.getFavoritePresets(); + return favs.some(function(d) { + return d.id === preset.id && d.geom === geom; + }); + }; /* Map */ var map; diff --git a/modules/modes/add_area.js b/modules/modes/add_area.js index ec2097a3e..9c8f465f0 100644 --- a/modules/modes/add_area.js +++ b/modules/modes/add_area.js @@ -10,8 +10,8 @@ import { modeDrawArea } from './index'; import { osmNode, osmWay } from '../osm'; -export function modeAddArea(context) { - var mode = { +export function modeAddArea(context, customMode, preset) { + var mode = customMode || { id: 'add-area', button: 'area', title: t('modes.add_area.title'), @@ -26,6 +26,7 @@ export function modeAddArea(context) { .on('startFromNode', startFromNode); var defaultTags = { area: 'yes' }; + if (preset) defaultTags = preset.setTags(defaultTags, 'area'); function actionClose(wayId) { @@ -47,7 +48,7 @@ export function modeAddArea(context) { actionClose(way.id) ); - context.enter(modeDrawArea(context, way.id, startGraph, context.graph())); + context.enter(modeDrawArea(context, way.id, startGraph, context.graph(), mode.button)); } @@ -64,7 +65,7 @@ export function modeAddArea(context) { actionAddMidpoint({ loc: loc, edge: edge }, node) ); - context.enter(modeDrawArea(context, way.id, startGraph, context.graph())); + context.enter(modeDrawArea(context, way.id, startGraph, context.graph(), mode.button)); } @@ -78,7 +79,7 @@ export function modeAddArea(context) { actionClose(way.id) ); - context.enter(modeDrawArea(context, way.id, startGraph, context.graph())); + context.enter(modeDrawArea(context, way.id, startGraph, context.graph(), mode.button)); } diff --git a/modules/modes/add_line.js b/modules/modes/add_line.js index 364adbcce..c524f502c 100644 --- a/modules/modes/add_line.js +++ b/modules/modes/add_line.js @@ -10,8 +10,8 @@ import { modeDrawLine } from './index'; import { osmNode, osmWay } from '../osm'; -export function modeAddLine(context) { - var mode = { +export function modeAddLine(context, customMode, preset) { + var mode = customMode || { id: 'add-line', button: 'line', title: t('modes.add_line.title'), @@ -25,11 +25,14 @@ export function modeAddLine(context) { .on('startFromWay', startFromWay) .on('startFromNode', startFromNode); + var defaultTags = {}; + if (preset) defaultTags = preset.setTags(defaultTags, 'line'); + function start(loc) { var startGraph = context.graph(); var node = osmNode({ loc: loc }); - var way = osmWay(); + var way = osmWay({ tags: defaultTags }); context.perform( actionAddEntity(node), @@ -37,7 +40,7 @@ export function modeAddLine(context) { actionAddVertex(way.id, node.id) ); - context.enter(modeDrawLine(context, way.id, startGraph, context.graph())); + context.enter(modeDrawLine(context, way.id, startGraph, context.graph(), mode.button)); } @@ -53,7 +56,7 @@ export function modeAddLine(context) { actionAddMidpoint({ loc: loc, edge: edge }, node) ); - context.enter(modeDrawLine(context, way.id, startGraph, context.graph())); + context.enter(modeDrawLine(context, way.id, startGraph, context.graph(), mode.button)); } @@ -66,7 +69,7 @@ export function modeAddLine(context) { actionAddVertex(way.id, node.id) ); - context.enter(modeDrawLine(context, way.id, startGraph, context.graph())); + context.enter(modeDrawLine(context, way.id, startGraph, context.graph(), mode.button)); } diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index 89623a012..d9024d95d 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -6,8 +6,8 @@ import { osmNode } from '../osm'; import { actionAddMidpoint } from '../actions'; -export function modeAddPoint(context) { - var mode = { +export function modeAddPoint(context, customMode, preset) { + var mode = customMode || { id: 'add-point', button: 'point', title: t('modes.add_point.title'), @@ -23,9 +23,12 @@ export function modeAddPoint(context) { .on('cancel', cancel) .on('finish', cancel); + var defaultTags = {}; + if (preset) defaultTags = preset.setTags(defaultTags, 'point'); + function add(loc) { - var node = osmNode({ loc: loc }); + var node = osmNode({ loc: loc, tags: defaultTags }); context.perform( actionAddEntity(node), diff --git a/modules/modes/draw_area.js b/modules/modes/draw_area.js index 00ef60d50..639773fa7 100644 --- a/modules/modes/draw_area.js +++ b/modules/modes/draw_area.js @@ -2,9 +2,9 @@ import { t } from '../util/locale'; import { behaviorDrawWay } from '../behavior'; -export function modeDrawArea(context, wayId, startGraph, baselineGraph) { +export function modeDrawArea(context, wayId, startGraph, baselineGraph, button) { var mode = { - button: 'area', + button: button, id: 'draw-area' }; diff --git a/modules/modes/draw_line.js b/modules/modes/draw_line.js index ba6748e3e..e6094975b 100644 --- a/modules/modes/draw_line.js +++ b/modules/modes/draw_line.js @@ -2,9 +2,9 @@ import { t } from '../util/locale'; import { behaviorDrawWay } from '../behavior'; -export function modeDrawLine(context, wayID, startGraph, baselineGraph, affix, continuing) { +export function modeDrawLine(context, wayID, startGraph, baselineGraph, button, affix, continuing) { var mode = { - button: 'line', + button: button, id: 'draw-line' }; diff --git a/modules/operations/continue.js b/modules/operations/continue.js index 088bd29ab..173965485 100644 --- a/modules/operations/continue.js +++ b/modules/operations/continue.js @@ -27,7 +27,7 @@ export function operationContinue(selectedIDs, context) { var operation = function() { var candidate = candidateWays()[0]; context.enter( - modeDrawLine(context, candidate.id, context.graph(), context.graph(), candidate.affix(vertex.id), true) + modeDrawLine(context, candidate.id, context.graph(), context.graph(), 'line', candidate.affix(vertex.id), true) ); }; diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index 845ad37e9..7f567424c 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -14,6 +14,7 @@ import { tooltip } from '../util/tooltip'; import { actionChangeTags } from '../actions'; import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; +import { uiPresetFavorite } from './preset_favorite'; import { uiPresetIcon } from './preset_icon'; import { uiQuickLinks } from './quick_links'; import { uiRawMemberEditor } from './raw_member_editor'; @@ -35,6 +36,7 @@ export function uiEntityEditor(context) { var _entityID; var _activePreset; var _tagReference; + var _presetFavorite; var entityIssues = uiEntityIssues(context); var quickLinks = uiQuickLinks(); @@ -137,6 +139,11 @@ export function uiEntityEditor(context) { body = body .merge(bodyEnter); + if (_presetFavorite) { + body.selectAll('.preset-list-button-wrap') + .call(_presetFavorite.button); + } + // update header if (_tagReference) { body.selectAll('.preset-list-button-wrap') @@ -333,6 +340,7 @@ export function uiEntityEditor(context) { _activePreset = val; _tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context) .showing(false); + _presetFavorite = uiPresetFavorite(_activePreset, context.geometry(_entityID), context); } return entityEditor; }; diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 35dfcc347..5a9a74c4f 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -11,6 +11,7 @@ import { } from '../modes'; import { svgIcon } from '../svg'; +import { t } from '../util/locale'; import { tooltip } from '../util/tooltip'; import { uiTooltipHtml } from './tooltipHtml'; @@ -82,7 +83,8 @@ export function uiModes(context) { .on('drawn.modes', debouncedUpdate); context - .on('enter.modes', update); + .on('enter.modes', update) + .on('favoritePreset.modes', update); update(); @@ -91,6 +93,37 @@ export function uiModes(context) { var showNotes = notesEnabled(); var data = showNotes ? modes : modes.slice(0, 3); + // add favorite presets to modes + 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-' + preset.name() + .replace(/\s+/g, '_') + + '-' + d.geom; //replace spaces with underscores to avoid css interpretation + + var favoriteMode = { + id: markerClass, + button: markerClass, + title: t('presets.presets.' + preset.id + '.name'), + description: [t('operations.add.title'), t('presets.presets.' + preset.id + '.name').toLowerCase(), t('geometry.' + d.geom)].join(' '), + key: '', + icon: icon + } + switch (d.geom) { + case 'point': + case 'vertex': + return modeAddPoint(context, favoriteMode, preset); + case 'line': + return modeAddLine(context, favoriteMode, preset); + case 'area': + return modeAddArea(context, favoriteMode, preset); + } + }); + + data = data.concat(favoriteModes); + var buttons = selection.selectAll('button.add-button') .data(data, function(d) { return d.id; }); @@ -125,7 +158,7 @@ export function uiModes(context) { buttonsEnter .each(function(d) { d3_select(this) - .call(svgIcon('#iD-icon-' + d.button)); + .call(svgIcon(d.icon || '#iD-icon-' + d.button)); }); buttonsEnter diff --git a/modules/ui/preset_favorite.js b/modules/ui/preset_favorite.js new file mode 100644 index 000000000..ee8d41921 --- /dev/null +++ b/modules/ui/preset_favorite.js @@ -0,0 +1,57 @@ +import _find from 'lodash-es/find'; +import _omit from 'lodash-es/omit'; + +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; + +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; +import { uiModes } from './modes'; + +export function uiPresetFavorite(preset, geom, context) { + + var presetFavorite = {}; + + var _button = d3_select(null); + + + presetFavorite.button = function(selection) { + var data = (preset.icon) ? [0] : []; + + _button = selection.selectAll('.preset-favorite-button') + .data(data); + + _button.exit().remove(); + + _button = _button.enter() + .insert('button', '.tag-reference-button') + .attr('class', 'preset-favorite-button') + .attr('title', t('icons.favorite')) + .attr('tabindex', -1) + .call(svgIcon('#iD-icon-favorite')) + .merge(_button); + + _button + .classed('active', function(d) { + 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); + + }); + + }; + + return presetFavorite; +} diff --git a/svg/iD-sprite/icons/icon-favorite.svg b/svg/iD-sprite/icons/icon-favorite.svg new file mode 100644 index 000000000..9d49e6404 --- /dev/null +++ b/svg/iD-sprite/icons/icon-favorite.svg @@ -0,0 +1,6 @@ + + + + +