Keep the edit menu open while panning the map but not zooming

This commit is contained in:
Quincy Morgan
2020-06-30 11:50:56 -04:00
parent 8826c95574
commit cf362cdcac
3 changed files with 105 additions and 52 deletions

View File

@@ -641,7 +641,6 @@ export function rendererMap(context) {
function resetTransform() {
if (!_isTransformed) return false;
supersurface.selectAll('.edit-menu').interrupt().remove();
utilSetTransform(supersurface, 0, 0);
_isTransformed = false;
if (context.inIntro()) {

View File

@@ -11,6 +11,7 @@ export function uiEditMenu(context) {
var _operations = [];
// the position the menu should be displayed relative to
var _anchorLoc = [0, 0];
var _anchorLocLonLat = [0, 0];
// a string indicating how the menu was opened
var _triggerType = '';
@@ -18,6 +19,10 @@ export function uiEditMenu(context) {
var _vpBottomMargin = 45; // viewport bottom margin
var _vpSideMargin = 35; // viewport side margin
var _menuTop = false;
var _menuHeight;
var _menuWidth;
// hardcode these values to make menu positioning easier
var _verticalPadding = 4;
@@ -27,6 +32,8 @@ export function uiEditMenu(context) {
// offset the menu slightly from the target location
var _menuSideMargin = 10;
var _tooltips = [];
var editMenu = function(selection) {
var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
@@ -37,59 +44,32 @@ export function uiEditMenu(context) {
if (!ops.length) return;
var offset = [0, 0];
var viewport = context.surfaceRect();
_tooltips = [];
// Position the menu above the anchor for stylus and finger input
// since the mapper's hand likely obscures the screen below the anchor
var menuTop = isTouchMenu;
_menuTop = isTouchMenu;
// Show labels for touch input since there aren't hover tooltips
var showLabels = isTouchMenu;
var buttonHeight = showLabels ? 32 : 34;
var menuWidth;
if (showLabels) {
// Get a general idea of the width based on the length of the label
menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
_menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
return op.title.length;
})));
} else {
menuWidth = 44;
_menuWidth = 44;
}
var menuLeft = displayOnLeft(viewport);
offset[0] = menuLeft ? -1 * (_menuSideMargin + menuWidth) : _menuSideMargin;
var menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
if (menuTop) {
if (_anchorLoc[1] - menuHeight < _vpTopMargin) {
// menu is near top viewport edge, shift downward
offset[1] = -_anchorLoc[1] + _vpTopMargin;
} else {
offset[1] = -menuHeight;
}
} else {
if (_anchorLoc[1] + menuHeight > (viewport.height - _vpBottomMargin)) {
// menu is near bottom viewport edge, shift upwards
offset[1] = -_anchorLoc[1] - menuHeight + viewport.height - _vpBottomMargin;
} else {
offset[1] = 0;
}
}
var origin = geoVecAdd(_anchorLoc, offset);
_menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
_menu = selection
.append('div')
.attr('class', 'edit-menu')
.classed('touch-menu', isTouchMenu)
.style('padding', _verticalPadding + 'px 0')
.style('left', origin[0] + 'px')
.style('top', origin[1] + 'px');
.style('padding', _verticalPadding + 'px 0');
var buttons = _menu.selectAll('.edit-menu-item')
.data(ops);
@@ -107,16 +87,16 @@ export function uiEditMenu(context) {
d3_event.stopPropagation();
});
var tooltipSide = tooltipPosition(viewport, menuLeft);
buttonsEnter.each(function(d) {
var tooltip = uiTooltip()
.heading(d.title)
.title(d.tooltip())
.keys([d.keys[0]]);
_tooltips.push(tooltip);
d3_select(this)
.call(uiTooltip()
.heading(d.title)
.title(d.tooltip())
.keys([d.keys[0]])
.placement(tooltipSide)
)
.call(tooltip)
.append('div')
.attr('class', 'icon-wrap')
.call(svgIcon('#iD-operation-' + d.id, 'operation-icon'));
@@ -135,6 +115,18 @@ export function uiEditMenu(context) {
.merge(buttons)
.classed('disabled', function(d) { return d.disabled(); });
updatePosition();
var initialScale = context.projection.scale();
context.map()
.on('move.edit-menu', function() {
if (initialScale !== context.projection.scale()) {
editMenu.close();
}
})
.on('drawn.edit-menu', function(info) {
if (info.full) updatePosition();
});
var lastPointerUpType;
// `pointerup` is always called before `click`
@@ -169,10 +161,62 @@ export function uiEditMenu(context) {
}
lastPointerUpType = null;
}
};
function updatePosition() {
if (!_menu || _menu.empty()) return;
var anchorLoc = context.projection(_anchorLocLonLat);
var viewport = context.surfaceRect();
if (anchorLoc[0] < 0 ||
anchorLoc[0] > viewport.width ||
anchorLoc[1] < 0 ||
anchorLoc[1] > viewport.height) {
// close the menu if it's gone offscreen
editMenu.close();
return;
}
var menuLeft = displayOnLeft(viewport);
var offset = [0, 0];
offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
if (_menuTop) {
if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
// menu is near top viewport edge, shift downward
offset[1] = -anchorLoc[1] + _vpTopMargin;
} else {
offset[1] = -_menuHeight;
}
} else {
if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
// menu is near bottom viewport edge, shift upwards
offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
} else {
offset[1] = 0;
}
}
var origin = geoVecAdd(anchorLoc, offset);
_menu
.style('left', origin[0] + 'px')
.style('top', origin[1] + 'px');
var tooltipSide = tooltipPosition(viewport, menuLeft);
_tooltips.forEach(function(tooltip) {
tooltip.placement(tooltipSide);
});
function displayOnLeft(viewport) {
if (localizer.textDirection() === 'ltr') {
if ((_anchorLoc[0] + _menuSideMargin + menuWidth) > (viewport.width - _vpSideMargin)) {
if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
// right menu would be too close to the right viewport edge, go left
return true;
}
@@ -180,7 +224,7 @@ export function uiEditMenu(context) {
return false;
} else { // rtl
if ((_anchorLoc[0] - _menuSideMargin - menuWidth) < _vpSideMargin) {
if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
// left menu would be too close to the left viewport edge, go right
return false;
}
@@ -196,7 +240,7 @@ export function uiEditMenu(context) {
// isn't room for right-side tooltips
return 'left';
}
if ((_anchorLoc[0] + _menuSideMargin + menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
// right tooltips would be too close to the right viewport edge, go left
return 'left';
}
@@ -207,7 +251,7 @@ export function uiEditMenu(context) {
if (!menuLeft) {
return 'right';
}
if ((_anchorLoc[0] - _menuSideMargin - menuWidth - _tooltipWidth) < _vpSideMargin) {
if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
// left tooltips would be too close to the left viewport edge, go right
return 'right';
}
@@ -215,16 +259,22 @@ export function uiEditMenu(context) {
return 'left';
}
}
};
}
editMenu.close = function () {
_menu
.remove();
context.map()
.on('move.edit-menu', null)
.on('drawn.edit-menu', null);
_menu.remove();
_tooltips = [];
};
editMenu.anchorLoc = function(val) {
if (!arguments.length) return _anchorLoc;
_anchorLoc = val;
_anchorLocLonLat = context.projection.invert(_anchorLoc);
return editMenu;
};

View File

@@ -146,9 +146,6 @@ export function uiPopover(klass) {
popoverSelection.classed('fade', true);
}
var placement = _placement.apply(this, arguments);
popoverSelection.classed(placement, true);
var display = _displayType.apply(this, arguments);
if (display === 'hover') {
@@ -251,6 +248,13 @@ export function uiPopover(klass) {
var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
var placement = _placement.apply(this, arguments);
popoverSelection
.classed('left', false)
.classed('right', false)
.classed('top', false)
.classed('bottom', false)
.classed(placement, true);
var alignment = _alignment.apply(this, arguments);
var alignFactor = 0.5;
if (alignment === 'leading') {