Merge pull request #5914 from openstreetmap/squash-favorite-preset

Favorite preset
This commit is contained in:
Quincy Morgan
2019-02-27 16:06:08 -05:00
committed by GitHub
19 changed files with 293 additions and 46 deletions
+1 -1
View File
@@ -54,6 +54,7 @@
cursor: url(img/cursor-select-remove.png), pointer; /* FF */
}
.mode-add-preset #map,
.mode-draw-line #map,
.mode-draw-area #map,
.mode-add-line #map,
@@ -104,4 +105,3 @@
.turn circle {
cursor: pointer;
}
+51 -2
View File
@@ -472,8 +472,33 @@ button[disabled].action:hover {
padding: 0 10px;
min-width: 30px;
white-space: nowrap;
display: flex;
}
.tool-group button .icon {
[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 > .icon {
flex: 0 0 20px;
}
.tool-group button .label {
@@ -970,6 +995,10 @@ a.hide-toggle {
height: 60px;
text-align: center;
}
#bar .preset-icon-container {
width: 40px;
height: 40px;
}
.preset-icon-line {
margin: auto;
@@ -1111,28 +1140,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%;
@@ -2435,6 +2483,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"],
+9
View File
@@ -7,6 +7,7 @@ en:
zoom_to: zoom to
copy: copy
open_wikidata: open on wikidata.org
favorite: favorite
modes:
add_area:
title: Area
@@ -24,6 +25,14 @@ en:
title: Note
description: "Spotted an issue? Let other mappers know."
tail: Click on the map to add a note.
add_preset:
title: "Add {feature}"
point:
title: "Add {feature} as a point"
line:
title: "Add {feature} as a line"
area:
title: "Add {feature} as an area"
browse:
title: Browse
description: Pan and zoom the map.
+14 -1
View File
@@ -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": "favorite"
},
"modes": {
"add_area": {
@@ -30,6 +31,18 @@
"description": "Spotted an issue? Let other mappers know.",
"tail": "Click on the map to add a note."
},
"add_preset": {
"title": "Add {feature}",
"point": {
"title": "Add {feature} as a point"
},
"line": {
"title": "Add {feature} as a line"
},
"area": {
"title": "Add {feature} as an area"
}
},
"browse": {
"title": "Browse",
"description": "Pan and zoom the map."
+1 -1
View File
@@ -330,7 +330,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));
if (isNewFeature) {
context.validator().validate();
+33 -1
View File
@@ -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,39 @@ 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
if (favs.length === 3) {
// remove the last favorite (first in, first out)
favs.pop();
}
// prepend array
favs.unshift({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;
+6 -5
View File
@@ -10,8 +10,8 @@ import { modeDrawArea } from './index';
import { osmNode, osmWay } from '../osm';
export function modeAddArea(context) {
var mode = {
export function modeAddArea(context, customMode) {
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 (mode.preset) defaultTags = mode.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));
}
+11 -8
View File
@@ -10,8 +10,8 @@ import { modeDrawLine } from './index';
import { osmNode, osmWay } from '../osm';
export function modeAddLine(context) {
var mode = {
export function modeAddLine(context, customMode) {
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 (mode.preset) defaultTags = mode.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,14 +40,14 @@ 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));
}
function startFromWay(loc, edge) {
var startGraph = context.graph();
var node = osmNode({ loc: loc });
var way = osmWay();
var way = osmWay({ tags: defaultTags });
context.perform(
actionAddEntity(node),
@@ -53,20 +56,20 @@ 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));
}
function startFromNode(node) {
var startGraph = context.graph();
var way = osmWay();
var way = osmWay({ tags: defaultTags });
context.perform(
actionAddEntity(way),
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));
}
+9 -6
View File
@@ -6,8 +6,8 @@ import { osmNode } from '../osm';
import { actionAddMidpoint } from '../actions';
export function modeAddPoint(context) {
var mode = {
export function modeAddPoint(context, customMode) {
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 (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
function add(loc) {
var node = osmNode({ loc: loc });
var node = osmNode({ loc: loc, tags: defaultTags });
context.perform(
actionAddEntity(node),
@@ -33,13 +36,13 @@ export function modeAddPoint(context) {
);
context.enter(
modeSelect(context, [node.id]).newFeature(true)
modeSelect(context, [node.id]).newFeature(!mode.preset)
);
}
function addWay(loc, edge) {
var node = osmNode();
var node = osmNode({ tags: defaultTags });
context.perform(
actionAddMidpoint({loc: loc, edge: edge}, node),
@@ -47,7 +50,7 @@ export function modeAddPoint(context) {
);
context.enter(
modeSelect(context, [node.id]).newFeature(true)
modeSelect(context, [node.id]).newFeature(!mode.preset)
);
}
+2 -2
View File
@@ -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'
};
+2 -2
View File
@@ -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'
};
+1 -1
View File
@@ -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)
);
};
+8
View File
@@ -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')
@@ -334,6 +341,7 @@ export function uiEntityEditor(context) {
_tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context)
.showing(false);
}
_presetFavorite = uiPresetFavorite(_activePreset, context.geometry(_entityID), context);
return entityEditor;
};
+55 -3
View File
@@ -11,7 +11,9 @@ import {
} from '../modes';
import { svgIcon } from '../svg';
import { t } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { uiPresetIcon } from './preset_icon';
import { uiTooltipHtml } from './tooltipHtml';
export function uiModes(context) {
@@ -82,7 +84,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 +94,46 @@ 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 add-' + d.geom + ' add-preset-' + preset.name()
.replace(/\s+/g, '_')
+ '-' + d.geom; //replace spaces with underscores to avoid css interpretation
var presetName = t('presets.presets.' + preset.id + '.name');
var relevantMatchingGeometry = preset.geometry.filter(function(geometry) {
return ['point', 'line', 'area'].indexOf(geometry) !== -1;
});
var tooltipTitleID = 'modes.add_preset.title';
if (relevantMatchingGeometry.length !== 1) {
tooltipTitleID = 'modes.add_preset.' + d.geom + '.title';
}
var favoriteMode = {
id: markerClass,
button: markerClass,
title: presetName,
description: t(tooltipTitleID, { feature: presetName }),
key: '',
icon: icon,
preset: preset,
geometry: d.geom
};
switch (d.geom) {
case 'point':
case 'vertex':
return modeAddPoint(context, favoriteMode);
case 'line':
return modeAddLine(context, favoriteMode);
case 'area':
return modeAddArea(context, favoriteMode);
}
});
data = data.concat(favoriteModes);
var buttons = selection.selectAll('button.add-button')
.data(data, function(d) { return d.id; });
@@ -124,8 +167,17 @@ export function uiModes(context) {
buttonsEnter
.each(function(d) {
d3_select(this)
.call(svgIcon('#iD-icon-' + d.button));
if (d.preset) {
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));
}
});
buttonsEnter
+54
View File
@@ -0,0 +1,54 @@
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { t } from '../util/locale';
import { svgIcon } from '../svg';
export function uiPresetFavorite(preset, geom, context) {
var presetFavorite = {};
var _button = d3_select(null);
presetFavorite.button = function(selection) {
var canFavorite = geom !== 'vertex' && geom !== 'relation' && !preset.isFallback();
_button = selection.selectAll('.preset-favorite-button')
.data(canFavorite ? [0] : []);
_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() {
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;
}
+27 -11
View File
@@ -4,7 +4,11 @@ import { svgIcon, svgTagClasses } from '../svg';
import { utilFunctor } from '../util';
export function uiPresetIcon() {
var preset, geometry;
var preset, geometry, sizeClass = 'medium';
function isSmall() {
return sizeClass === 'small';
}
function presetIcon(selection) {
@@ -39,7 +43,8 @@ export function uiPresetIcon() {
}
function renderSquareFill(fillEnter) {
var w = 60, h = 60, l = 40, c1 = (w-l)/2, c2 = c1 + l;
var d = isSmall() ? 40 : 60;
var w = d, h = d, l = d*2/3, c1 = (w-l)/2, c2 = c1 + l;
fillEnter = fillEnter
.append('svg')
.attr('class', 'preset-icon-fill preset-icon-fill-area')
@@ -69,20 +74,24 @@ export function uiPresetIcon() {
}
}
var midCoordinates = [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]];
for (var index in midCoordinates) {
var loc = midCoordinates[index];
fillEnter.append('circle')
.attr('class', 'midpoint')
.attr('cx', loc[0])
.attr('cy', loc[1])
.attr('r', 1.25);
if (!isSmall()) {
var midCoordinates = [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]];
for (var index in midCoordinates) {
var loc = midCoordinates[index];
fillEnter.append('circle')
.attr('class', 'midpoint')
.attr('cx', loc[0])
.attr('cy', loc[1])
.attr('r', 1.25);
}
}
}
function renderLine(lineEnter) {
var d = isSmall() ? 40 : 60;
// draw the line parametrically
var w = 60, h = 60, y = 43, l = 36, r = 2.5;
var w = d, h = d, y = Math.round(d*0.72), l = Math.round(d*0.6), r = 2.5;
var x1 = (w - l)/2, x2 = x1 + l;
lineEnter = lineEnter
@@ -224,5 +233,12 @@ export function uiPresetIcon() {
return presetIcon;
};
presetIcon.sizeClass = function(val) {
if (!arguments.length) return sizeClass;
sizeClass = val;
return presetIcon;
};
return presetIcon;
}
+4 -2
View File
@@ -215,8 +215,10 @@ export function uiSidebar(context) {
var entity = context.entity(id);
// uncollapse the sidebar
if (selection.classed('collapsed')) {
var extent = entity.extent(context.graph());
sidebar.expand(sidebar.intersects(extent));
if (newFeature) {
var extent = entity.extent(context.graph());
sidebar.expand(sidebar.intersects(extent));
}
}
featureListWrap
+1
View File
@@ -145,6 +145,7 @@ export function validationAlmostJunction() {
var validation = function(endHighway, context) {
if (!isHighway(endHighway)) return [];
if (endHighway.isDegenerate()) return [];
var graph = context.graph();
var tree = context.history().tree();
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon stroke="currentColor" fill="currentColor" points="10 13.288998 5.45112425 16.6084521 7.20243278 11.2564465 2.63976443 7.95597629 8.27100837 7.96770898 10 2.60845213 11.7289916 7.96770898 17.3602356 7.95597629 12.7975672 11.2564465 14.5488757 16.6084521"/>
</svg>

After

Width:  |  Height:  |  Size: 460 B