Files
iD/modules/ui/edit_menu.js
Bryan Housel 81127d71f3 Cache disabled() results in straighten action for consistent response
What could happen was:
- user could right click on a line
- this would trigger `disabled()` checks for each operation buttons
- the line was not fully downloaded, so would return `disabled()` 'not_downloaded'
  (and also start download of the missing tiles)
- then the tooltip would pop into existence, calling `tooltip()`
- which calls `disabled()` again
- but this time it's fine and the `disabled()` is false
- so you'd see a greyed out button but the tooltip said everyting is ok and
  you can click the button anyway

I fixed this by just caching the disabled test.  This is probably ok anyway
because these tests can be expensive, and then the user will see a consistent
message like "The line is not yet fully downloaded".

If the user clicks off the line and back onto it, iD will reenter select mode,
rebuild the menu, redo the disabled test, and they will see the button enabled.
2019-04-10 10:19:23 -04:00

184 lines
5.7 KiB
JavaScript

import { event as d3_event, select as d3_select } from 'd3-selection';
import { geoVecAdd, geoVecFloor } from '../geo';
import { textDirection } from '../util/locale';
import { uiTooltipHtml } from './tooltipHtml';
export function uiEditMenu(context, operations) {
var menu;
var center = [0, 0];
var offset = [0, 0];
var tooltip;
var p = 8; // top padding
var m = 4; // top margin
var h = 15; // height of icon
var vpBottomMargin = 45; // viewport bottom margin
var vpSideMargin = 35; // viewport side margin
var buttonWidth = 44;
var buttonHeight = (2 * p + h);
var menuWidth = buttonWidth;
var menuHeight = (2 * m) + operations.length * buttonHeight;
var menuSideMargin = 10;
var tooltipWidth = 200;
var tooltipHeight = 200; // a reasonable guess, real height depends on tooltip contents
var editMenu = function (selection) {
if (!operations.length) return;
selection.node().parentNode.focus();
var isRTL = textDirection === 'rtl';
var viewport = context.surfaceRect();
if (!isRTL && (center[0] + menuSideMargin + menuWidth) > (viewport.width - vpSideMargin)) {
// menu is going left-to-right and near right viewport edge, go left instead
isRTL = true;
} else if (isRTL && (center[0] - menuSideMargin - menuWidth) < vpSideMargin) {
// menu is going right-to-left and near left viewport edge, go right instead
isRTL = false;
}
offset[0] = (isRTL ? -1 * (menuSideMargin + menuWidth) : menuSideMargin);
if (center[1] + menuHeight > (viewport.height - vpBottomMargin)) {
// menu is near bottom viewport edge, shift upwards
offset[1] = -1 * (center[1] + menuHeight - viewport.height + vpBottomMargin);
}
var origin = geoVecAdd(center, offset);
menu = selection
.append('g')
.attr('class', 'edit-menu')
.attr('transform', 'translate(' + origin + ')')
.attr('opacity', 0);
menu
.transition()
.attr('opacity', 1);
menu
.append('rect')
.attr('class', 'edit-menu-background')
.attr('x', 4)
.attr('rx', 4)
.attr('ry', 4)
.attr('width', menuWidth)
.attr('height', menuHeight)
.attr('stroke-linecap', 'round');
var button = menu.selectAll('.edit-menu-item')
.data(operations);
// enter
var buttonEnter = button.enter()
.append('g')
.attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
.attr('transform', function(d, i) {
return 'translate(' + geoVecFloor([0, m + i * buttonHeight]).join(',') + ')';
});
buttonEnter
.append('rect')
.attr('x', 4)
.attr('width', buttonWidth)
.attr('height', buttonHeight)
.on('click', click)
.on('mousedown', mousedown)
.on('mouseover', mouseover)
.on('mouseout', mouseout);
buttonEnter
.append('use')
.attr('width', '20')
.attr('height', '20')
.attr('transform', function () { return 'translate(' + [2 * p, 5] + ')'; })
.attr('xlink:href', function (d) { return '#iD-operation-' + d.id; });
// update
button = buttonEnter
.merge(button)
.classed('disabled', function(d) { return d.disabled(); });
tooltip = d3_select('#id-container')
.append('div')
.attr('class', 'tooltip-inner edit-menu-tooltip');
function click(operation) {
d3_event.stopPropagation();
if (operation.disabled()) return;
operation();
editMenu.close();
}
function mousedown() {
d3_event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
}
function mouseover(d, i) {
var tipX, tipY;
if (!isRTL) {
tipX = viewport.left + origin[0] + menuSideMargin + menuWidth;
} else {
tipX = viewport.left + origin[0] - 4 - tooltipWidth;
}
if (tipX + tooltipWidth > viewport.right) {
// tip is going left-to-right and near right viewport edge, go left instead
tipX = viewport.left + origin[0] - 4 - tooltipWidth;
} else if (tipX < viewport.left) {
// tip is going right-to-left and near left viewport edge, go right instead
tipX = viewport.left + origin[0] + menuSideMargin + menuWidth;
}
tipY = viewport.top + origin[1] + (i * buttonHeight);
if (tipY + tooltipHeight > viewport.bottom) {
// tip is near bottom viewport edge, shift upwards
tipY -= tipY + tooltipHeight - viewport.bottom;
}
tooltip
.style('left', tipX + 'px')
.style('top', tipY + 'px')
.style('display', 'block')
.html(uiTooltipHtml(d.tooltip(), d.keys[0], d.title));
}
function mouseout() {
tooltip.style('display', 'none');
}
};
editMenu.close = function () {
if (menu) {
menu
.style('pointer-events', 'none')
.transition()
.attr('opacity', 0)
.remove();
}
if (tooltip) {
tooltip.remove();
}
};
editMenu.center = function(val) {
if (!arguments.length) return center;
center = val;
return editMenu;
};
return editMenu;
}