diff --git a/css/80_app.css b/css/80_app.css index 53d244740..f4ccd6c3b 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -176,6 +176,7 @@ input[type=email] { height: 30px; border-radius: 4px; text-overflow: ellipsis; + overflow: hidden; } .ideditor[dir='rtl'] textarea, .ideditor[dir='rtl'] input[type=text], @@ -779,9 +780,9 @@ a.hide-toggle { .sidebar-resizer { position: absolute; top: 0; - right: -6px; + right: -10px; + width: 10px; height: 100%; - width: 6px; cursor: col-resize; /* disable drag-to-select */ user-select: none; @@ -911,7 +912,6 @@ a.hide-toggle { width: 100%; } .no-results-item, -.geocode-item, .feature-list-item { width: 100%; position: relative; @@ -920,11 +920,13 @@ a.hide-toggle { } .geocode-item { - width: 50%; + width: 100%; + max-width: 200px; background-color: #ccc; - left: 25%; - margin-top: 30px; - border-radius: 2px; + margin: 30px auto; + padding: 5px; + height: auto; + min-height: 40px; } .ideditor[dir='rtl'] .geocode-item { @@ -2319,7 +2321,7 @@ div.combobox { .more-fields input { margin-left: 10px; - flex: 1 1 50%; + flex: 1 1 auto; } .ideditor[dir='rtl'] .more-fields input { margin-left: auto; diff --git a/modules/behavior/select.js b/modules/behavior/select.js index b49181db9..0d780e111 100644 --- a/modules/behavior/select.js +++ b/modules/behavior/select.js @@ -95,6 +95,8 @@ export function behaviorSelect(context) { if (d3_event.buttons && d3_event.buttons !== 1) return; + context.ui().closeEditMenu(); + _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse')); _downPointers[id] = { diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 7ed26a2cb..273f17812 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -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()) { diff --git a/modules/ui/edit_menu.js b/modules/ui/edit_menu.js index 41703952e..55242ced6 100644 --- a/modules/ui/edit_menu.js +++ b/modules/ui/edit_menu.js @@ -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')); @@ -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; }; diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js index 5bbac52c2..035541afc 100644 --- a/modules/ui/form_fields.js +++ b/modules/ui/form_fields.js @@ -76,11 +76,16 @@ export function uiFormFields(context) { more.exit() .remove(); - more = more.enter() + var moreEnter = more.enter() .append('div') .attr('class', 'more-fields') - .append('label') - .text(t('inspector.add_fields')) + .append('label'); + + moreEnter + .append('span') + .text(t('inspector.add_fields')); + + more = moreEnter .merge(more); diff --git a/modules/ui/popover.js b/modules/ui/popover.js index 0f8a8f634..bf5915570 100644 --- a/modules/ui/popover.js +++ b/modules/ui/popover.js @@ -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') { diff --git a/modules/ui/sections/raw_membership_editor.js b/modules/ui/sections/raw_membership_editor.js index eef0502b3..43b1ff3c5 100644 --- a/modules/ui/sections/raw_membership_editor.js +++ b/modules/ui/sections/raw_membership_editor.js @@ -317,15 +317,27 @@ export function uiSectionRawMembershipEditor(context) { .append('li') .attr('class', 'member-row member-row-new form-field'); - newMembershipEnter + var newLabelEnter = newMembershipEnter .append('label') - .attr('class', 'field-label') + .attr('class', 'field-label'); + + newLabelEnter .append('input') .attr('placeholder', t('inspector.choose_relation')) .attr('type', 'text') .attr('class', 'member-entity-input') .call(utilNoAuto); + newLabelEnter + .append('button') + .attr('tabindex', -1) + .attr('class', 'remove member-delete') + .call(svgIcon('#iD-operation-delete')) + .on('click', function() { + list.selectAll('.member-row-new') + .remove(); + }); + var newWrapEnter = newMembershipEnter .append('div') .attr('class', 'form-field-input-wrap form-field-input-member'); @@ -337,16 +349,6 @@ export function uiSectionRawMembershipEditor(context) { .attr('placeholder', t('inspector.role')) .call(utilNoAuto); - newWrapEnter - .append('button') - .attr('tabindex', -1) - .attr('class', 'remove form-field-button member-delete') - .call(svgIcon('#iD-operation-delete')) - .on('click', function() { - list.selectAll('.member-row-new') - .remove(); - }); - // Update newMembership = newMembership .merge(newMembershipEnter); diff --git a/modules/ui/sidebar.js b/modules/ui/sidebar.js index c6b3a47c8..af3ba323b 100644 --- a/modules/ui/sidebar.js +++ b/modules/ui/sidebar.js @@ -38,7 +38,7 @@ export function uiSidebar(context) { function sidebar(selection) { var container = context.container(); - var minWidth = 280; + var minWidth = 240; var sidebarWidth; var containerWidth; var dragOffset; @@ -59,6 +59,8 @@ export function uiSidebar(context) { function pointerdown() { if (downPointerId) return; + if ('button' in d3_event && d3_event.button !== 0) return; + downPointerId = d3_event.pointerId || 'mouse'; lastClientX = d3_event.clientX;