From d8237fa3eb674103b43177bd5f416b4955d7ceee Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 17 Feb 2017 22:51:29 -0500 Subject: [PATCH] Variable names, fix edge avoidance, tooltips placement, RTL --- modules/modes/select.js | 19 +++--- modules/ui/edit_menu.js | 126 +++++++++++++++++++++++----------------- 2 files changed, 83 insertions(+), 62 deletions(-) diff --git a/modules/modes/select.js b/modules/modes/select.js index 681d39de5..655113764 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -153,16 +153,15 @@ export function modeSelect(context, selectedIDs) { suppressMenu = true; } else { var point = context.mouse(), - viewport = geoExtent(context.projection.clipExtent()).polygon(), - offset = [ - viewport[2][0] - point[0], - (viewport[1][1] - 30) - point[1] // 30 to account for the infoblock - ]; + viewport = geoExtent(context.projection.clipExtent()).polygon(); + // offset = [ + // viewport[2][0] - point[0], + // (viewport[1][1] - 30) - point[1] // 30 to account for the infoblock + // ]; if (geoPointInPolygon(point, viewport)) { - editMenu - .center(point) - .offset(offset); + editMenu.center(point); + // .offset(offset); } } } @@ -446,8 +445,8 @@ export function modeSelect(context, selectedIDs) { selectElements(); var show = d3.event; - var rtClick = d3.event && d3.event.type === 'contextmenu'; - + var rtClick = d3.event && d3.event.type === 'contextmenu'; + if (show) { positionMenu(); } diff --git a/modules/ui/edit_menu.js b/modules/ui/edit_menu.js index 7777f4699..490c4bf69 100644 --- a/modules/ui/edit_menu.js +++ b/modules/ui/edit_menu.js @@ -1,20 +1,27 @@ import * as d3 from 'd3'; import { geoRoundCoords } from '../geo/index'; +import { textDirection } from '../util/locale'; import { uiTooltipHtml } from './tooltipHtml'; export function uiEditMenu(context, operations) { - var rect, - menu, + var menu, center = [0, 0], offset = [0, 0], tooltip; - var p = 8, // top padding - l = 10, // left padding - h = 15, // height of icon - m = 4, // top margin - a1 = 2 * m + operations.length * (2 * p + h); + var p = 8, // top padding + m = 4, // top margin + h = 15, // height of icon + vpBottomMargin = 45, // viewport bottom margin + vpSideMargin = 35, // viewport side margin + buttonWidth = 44, + buttonHeight = (2 * p + h), + menuWidth = buttonWidth, + menuHeight = (2 * m) + operations.length * buttonHeight, + menuSideMargin = 10, + tooltipWidth = 200, + tooltipHeight = 200; // a reasonable guess, real height depends on tooltip contents var editMenu = function (selection) { @@ -22,46 +29,48 @@ export function uiEditMenu(context, operations) { selection.node().parentNode.focus(); - function click(operation) { - d3.event.stopPropagation(); - if (operation.disabled()) return; - operation(); - editMenu.close(); + var isRTL = textDirection === 'rtl', + 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 = [ center[0] + offset[0], center[1] + offset[1] ]; + menu = selection .append('g') .attr('class', 'edit-menu') - .attr('transform', 'translate(' + [center[0] + l, center[1]] + ')') + .attr('transform', 'translate(' + origin + ')') .attr('opacity', 0); menu .transition() .attr('opacity', 1); - rect = menu - .append('g') - .attr('class', 'edit-menu-rectangle') - .attr('transform', function () { - var pos = [0, 0]; - if (offset[1] <= a1) { - pos = [0, offset[1] - a1]; - } - return 'translate(' + pos + ')'; - }); - - rect + menu .append('rect') .attr('class', 'edit-menu-background') .attr('x', 4) .attr('rx', 4) .attr('ry', 4) - .attr('width', 44) - .attr('height', a1) + .attr('width', menuWidth) + .attr('height', menuHeight) .attr('stroke-linecap', 'round'); - var button = rect.selectAll() + var button = menu.selectAll('.edit-menu-item') .data(operations) .enter() .append('g') @@ -70,14 +79,15 @@ export function uiEditMenu(context, operations) { .attr('transform', function (d, i) { return 'translate(' + geoRoundCoords([ 0, - m + i * (2 * p + h)]).join(',') + ')'; + m + i * buttonHeight + ]).join(',') + ')'; }); button .append('rect') .attr('x', 4) - .attr('width', 44) - .attr('height', 2 * p + h) + .attr('width', buttonWidth) + .attr('height', buttonHeight) .on('click', click) .on('mousedown', mousedown) .on('mouseover', mouseover) @@ -96,27 +106,44 @@ export function uiEditMenu(context, operations) { .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 + d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869 } function mouseover(d, i) { - var width = 260; - var rect = context.surfaceRect(), - pos = [ - offset[0] < width ? center[0] - 255 : center[0], - offset[1] <= a1 ? m + center[1] - (a1 - offset[1]) : m + center[1] - ], - top = rect.top + i * (2 * p + h) + pos[1], - left = rect.left + (64) + pos[0]; - var j = i; - // fix tooltip overflow on y axis - while (top - center[1] + 90 > offset[1] && j !== 0) { - top = rect.top + (--j) * (2 * p + h) + pos[1]; - } + 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('top', top + 'px') - .style('left', left+ 'px') + .style('left', tipX + 'px') + .style('top', tipY + 'px') .style('display', 'block') .html(uiTooltipHtml(d.tooltip(), d.keys[0], d.title)); } @@ -148,11 +175,6 @@ export function uiEditMenu(context, operations) { return editMenu; }; - editMenu.offset = function (_) { - if (!arguments.length) return offset; - offset = _; - return editMenu; - }; return editMenu; }