From 9731b93cec7dd7673a7177cfba36924460045853 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 25 Mar 2020 12:22:10 -0700 Subject: [PATCH] Move uiTooltipHtml functionality into uiTooltip Refactor uiEditMenu to use standard UI patterns instead of SVG --- css/80_app.css | 34 +++--- modules/modes/select.js | 6 +- modules/renderer/map.js | 4 +- modules/ui/edit_menu.js | 126 ++++++----------------- modules/ui/feature_info.js | 4 +- modules/ui/geolocate.js | 5 +- modules/ui/index.js | 1 - modules/ui/pane.js | 5 +- modules/ui/panes/help.js | 5 +- modules/ui/sections/background_list.js | 16 ++- modules/ui/sections/data_layers.js | 5 +- modules/ui/sections/map_features.js | 4 +- modules/ui/sections/map_style_options.js | 8 +- modules/ui/tools/modes.js | 5 +- modules/ui/tools/notes.js | 5 +- modules/ui/tools/save.js | 10 +- modules/ui/tools/sidebar_toggle.js | 5 +- modules/ui/tools/undo_redo.js | 9 +- modules/ui/tooltip.js | 85 ++++++++++++--- modules/ui/tooltipHtml.js | 23 ----- modules/ui/zoom.js | 7 +- modules/ui/zoom_to_selection.js | 5 +- 22 files changed, 156 insertions(+), 221 deletions(-) delete mode 100644 modules/ui/tooltipHtml.js diff --git a/css/80_app.css b/css/80_app.css index 39448cc09..5f2d39d8f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -5269,30 +5269,22 @@ li.hide + li.version .badge .tooltip .popover-arrow { /* Contextual Edit Menu ------------------------------------------------------- */ -.edit-menu-tooltip { +.edit-menu { + position: absolute; + display: flex; + flex-direction: column; + background: #fff; + border-radius: 4px; + /* padding set in code */ +} + +.edit-menu .tooltip { width: 200px; } -.edit-menu-background { - fill: #eee; -} - -.edit-menu-item rect { - fill: #eee; - cursor: default; -} - -.edit-menu-item rect:active, -.edit-menu-item rect:hover { - fill: #ccc; -} - -.edit-menu-item.disabled rect { - cursor: not-allowed; -} -.edit-menu-item.disabled rect:hover { - cursor: not-allowed; - fill: #eee; +.edit-menu-item { + border-radius: 0; + /* width and height set in code */ } .edit-menu-item use { diff --git a/modules/modes/select.js b/modules/modes/select.js index 7197905f3..ce7cdf286 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -147,7 +147,7 @@ export function modeSelect(context, selectedIDs) { function closeMenu() { if (editMenu) { - context.surface().call(editMenu.close); + context.map().supersurface.call(editMenu.close); } } @@ -178,13 +178,13 @@ export function modeSelect(context, selectedIDs) { // disable menu if in wide selection, for example if (!context.map().editableDataEnabled()) return; - context.surface().call(editMenu); + context.map().supersurface.call(editMenu); } } function toggleMenu() { - if (context.surface().select('.edit-menu').empty()) { + if (context.map().supersurface.select('.edit-menu').empty()) { positionMenu(); showMenu(); } else { diff --git a/modules/renderer/map.js b/modules/renderer/map.js index eddcb2904..22b31ab8e 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -157,7 +157,7 @@ export function rendererMap(context) { .call(_zoomerPanner.transform, projection.transform()) .on('dblclick.zoom', null); // override d3-zoom dblclick handling - supersurface = selection.append('div') + map.supersurface = supersurface = selection.append('div') .attr('class', 'supersurface') .call(utilSetTransform, 0, 0); @@ -602,7 +602,7 @@ export function rendererMap(context) { function resetTransform() { if (!_isTransformed) return false; - surface.selectAll('.edit-menu').interrupt().remove(); + 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 1795a5cd2..2d2534d8a 100644 --- a/modules/ui/edit_menu.js +++ b/modules/ui/edit_menu.js @@ -1,28 +1,27 @@ import { event as d3_event, select as d3_select } from 'd3-selection'; -import { geoVecAdd, geoVecFloor } from '../geo'; +import { geoVecAdd } from '../geo'; import { textDirection } from '../util/locale'; -import { uiTooltipHtml } from './tooltipHtml'; +import { uiTooltip } from './tooltip'; +import { svgIcon } from '../svg/icon'; 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 + + // hardcode these values to make menu positioning easier var buttonWidth = 44; - var buttonHeight = (2 * p + h); + var buttonHeight = 34; var menuWidth = buttonWidth; - var menuHeight = (2 * m) + operations.length * buttonHeight; + var verticalPadding = 4; + + // offset the menu slightly from the target location var menuSideMargin = 10; - var tooltipWidth = 200; - var tooltipHeight = 200; // a reasonable guess, real height depends on tooltip contents var editMenu = function (selection) { @@ -33,6 +32,8 @@ export function uiEditMenu(context, operations) { var isRTL = textDirection === 'rtl'; var viewport = context.surfaceRect(); + var menuHeight = verticalPadding * 2 + operations.length * buttonHeight; + 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; @@ -51,54 +52,39 @@ export function uiEditMenu(context, operations) { var origin = geoVecAdd(center, offset); menu = selection - .append('g') + .append('div') .attr('class', 'edit-menu') - .attr('transform', 'translate(' + origin + ')') + .style('padding', verticalPadding + 'px 0') + .style('left', origin[0] + 'px') + .style('top', origin[1] + 'px') .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 buttons = menu.selectAll('.edit-menu-item') .data(operations); // enter var buttonsEnter = buttons.enter() - .append('g') + .append('button') .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(',') + ')'; - }); - - buttonsEnter - .append('rect') - .attr('x', 4) - .attr('width', buttonWidth) - .attr('height', buttonHeight) + .style('width', buttonWidth + 'px') + .style('height', buttonHeight + 'px') .on('click', click) - .on('mousedown', mousedown) - .on('mouseover', mouseover) - .on('mouseout', mouseout); + .on('mousedown', mousedown); - buttonsEnter - .append('use') - .attr('class', 'operation-icon') - .attr('width', '20') - .attr('height', '20') - .attr('transform', function () { return 'translate(' + [2 * p, 5] + ')'; }) - .attr('xlink:href', function (d) { return '#iD-operation-' + d.id; }); + buttonsEnter.each(function(d) { + d3_select(this) + .call(svgIcon('#iD-operation-' + d.id, 'operation-icon')) + .call(uiTooltip() + .heading(d.title) + .title(d.tooltip()) + .keys([d.keys[0]]) + .placement('right') + ); + }); // update buttons = buttonsEnter @@ -106,15 +92,6 @@ export function uiEditMenu(context, operations) { .classed('disabled', function(d) { return d.disabled(); }); - tooltip = context.container() - .append('div') - .attr('class', 'popover tooltip edit-menu-tooltip'); - - tooltip - .append('div') - .attr('class', 'popover-inner'); - - function click(operation) { d3_event.stopPropagation(); if (operation.disabled()) return; @@ -125,47 +102,6 @@ export function uiEditMenu(context, operations) { 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') - .selectAll('.popover-inner') - .html(uiTooltipHtml(d.tooltip(), d.keys[0], d.title)); - - // update disabled again, just in case tooltip and disabled state disagree - // https://github.com/openstreetmap/iD/issues/6296#issuecomment-489259027 - d3_select(this.parentNode) - .classed('disabled', d.disabled()); - - } - - function mouseout() { - tooltip.style('display', 'none'); - } }; @@ -177,10 +113,6 @@ export function uiEditMenu(context, operations) { .attr('opacity', 0) .remove(); } - - if (tooltip) { - tooltip.remove(); - } }; diff --git a/modules/ui/feature_info.js b/modules/ui/feature_info.js index 5cdac21bf..abeeca548 100644 --- a/modules/ui/feature_info.js +++ b/modules/ui/feature_info.js @@ -1,7 +1,6 @@ import { event as d3_event } from 'd3-selection'; import { t } from '../util/locale'; -import { uiTooltipHtml } from './tooltipHtml'; import { uiTooltip } from './tooltip'; @@ -22,9 +21,8 @@ export function uiFeatureInfo(context) { if (hiddenList.length) { var tooltipBehavior = uiTooltip() .placement('top') - .html(true) .title(function() { - return uiTooltipHtml(hiddenList.join('
')); + return hiddenList.join('
'); }); selection.append('a') diff --git a/modules/ui/geolocate.js b/modules/ui/geolocate.js index 97f077db5..32b0bc7d9 100644 --- a/modules/ui/geolocate.js +++ b/modules/ui/geolocate.js @@ -7,7 +7,6 @@ import { modeBrowse } from '../modes/browse'; import { svgIcon } from '../svg/icon'; import { uiFlash } from './flash'; import { uiLoading } from './loading'; -import { uiTooltipHtml } from './tooltipHtml'; export function uiGeolocate(context) { var _geolocationOptions = { @@ -89,8 +88,8 @@ export function uiGeolocate(context) { .call(svgIcon('#iD-icon-geolocate', 'light')) .call(uiTooltip() .placement((textDirection === 'rtl') ? 'right' : 'left') - .html(true) - .title(uiTooltipHtml(t('geolocate.title'), t('geolocate.key'))) + .title(t('geolocate.title')) + .keys([t('geolocate.key')]) ); context.keybinding().on(t('geolocate.key'), click); diff --git a/modules/ui/index.js b/modules/ui/index.js index beacd0f76..c1e7abcd5 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -56,7 +56,6 @@ export { uiSuccess } from './success'; export { uiTagReference } from './tag_reference'; export { uiToggle } from './toggle'; export { uiTooltip } from './tooltip'; -export { uiTooltipHtml } from './tooltipHtml'; export { uiVersion } from './version'; export { uiViewOnOSM } from './view_on_osm'; export { uiViewOnKeepRight } from './view_on_keepRight'; diff --git a/modules/ui/pane.js b/modules/ui/pane.js index 7b32955be..a07b0e3d4 100644 --- a/modules/ui/pane.js +++ b/modules/ui/pane.js @@ -6,7 +6,6 @@ import { import { svgIcon } from '../svg/icon'; import { textDirection } from '../util/locale'; import { uiTooltip } from './tooltip'; -import { uiTooltipHtml } from './tooltipHtml'; export function uiPane(id, context) { @@ -74,8 +73,8 @@ export function uiPane(id, context) { if (!_paneTooltip) { _paneTooltip = uiTooltip() .placement((textDirection === 'rtl') ? 'right' : 'left') - .html(true) - .title(uiTooltipHtml(_description, _key)); + .title(_description) + .keys([_key]); } selection diff --git a/modules/ui/panes/help.js b/modules/ui/panes/help.js index b9aace172..163daf212 100644 --- a/modules/ui/panes/help.js +++ b/modules/ui/panes/help.js @@ -4,7 +4,6 @@ import { svgIcon } from '../../svg/icon'; import { uiCmd } from '../cmd'; import { uiIntro } from '../intro/intro'; import { uiShortcuts } from '../shortcuts'; -import { uiTooltipHtml } from '../tooltipHtml'; import { uiPane } from '../pane'; import { t, textDirection } from '../../util/locale'; @@ -365,8 +364,8 @@ export function uiPaneHelp(context) { .append('li') .attr('class', 'shortcuts') .call(uiTooltip() - .html(true) - .title(uiTooltipHtml(t('shortcuts.tooltip'), '?')) + .title(t('shortcuts.tooltip')) + .keys(['?']) .placement('top') ) .append('a') diff --git a/modules/ui/sections/background_list.js b/modules/ui/sections/background_list.js index 3b41011db..3ffef95e1 100644 --- a/modules/ui/sections/background_list.js +++ b/modules/ui/sections/background_list.js @@ -12,7 +12,6 @@ import { uiCmd } from '../cmd'; import { uiSettingsCustomBackground } from '../settings/custom_background'; import { uiMapInMap } from '../map_in_map'; import { uiSection } from '../section'; -import { uiTooltipHtml } from '../tooltipHtml'; export function uiSectionBackgroundList(context) { @@ -56,8 +55,8 @@ export function uiSectionBackgroundList(context) { .attr('class', 'minimap-toggle-item') .append('label') .call(uiTooltip() - .html(true) - .title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key'))) + .title(t('background.minimap.tooltip')) + .keys([t('background.minimap.key')]) .placement('top') ); @@ -79,8 +78,8 @@ export function uiSectionBackgroundList(context) { .attr('class', 'background-panel-toggle-item') .append('label') .call(uiTooltip() - .html(true) - .title(uiTooltipHtml(t('background.panel.tooltip'), uiCmd('⌘⇧' + t('info_panels.background.key')))) + .title(t('background.panel.tooltip')) + .keys([uiCmd('⌘⇧' + t('info_panels.background.key'))]) .placement('top') ); @@ -127,11 +126,8 @@ export function uiSectionBackgroundList(context) { if (d.id === previousBackgroundID()) { item.call(uiTooltip() .placement(placement) - .html(true) - .title(function() { - var tip = '
' + t('background.switch') + '
'; - return uiTooltipHtml(tip, uiCmd('⌘' + t('background.key'))); - }) + .title('
' + t('background.switch') + '
') + .keys([uiCmd('⌘' + t('background.key'))]) ); } else if (description || isOverflowing) { item.call(uiTooltip() diff --git a/modules/ui/sections/data_layers.js b/modules/ui/sections/data_layers.js index 87eeeb78b..e85c76262 100644 --- a/modules/ui/sections/data_layers.js +++ b/modules/ui/sections/data_layers.js @@ -12,7 +12,6 @@ import { modeBrowse } from '../../modes/browse'; import { uiCmd } from '../cmd'; import { uiSection } from '../section'; import { uiSettingsCustomData } from '../settings/custom_data'; -import { uiTooltipHtml } from '../tooltipHtml'; export function uiSectionDataLayers(context) { @@ -95,8 +94,8 @@ export function uiSectionDataLayers(context) { if (d.id === 'osm') { d3_select(this) .call(uiTooltip() - .html(true) - .title(uiTooltipHtml(t('map_data.layers.' + d.id + '.tooltip'), uiCmd('⌥' + t('area_fill.wireframe.key')))) + .title(t('map_data.layers.' + d.id + '.tooltip')) + .keys([uiCmd('⌥' + t('area_fill.wireframe.key'))]) .placement('bottom') ); } else { diff --git a/modules/ui/sections/map_features.js b/modules/ui/sections/map_features.js index 00164e1fc..623536c84 100644 --- a/modules/ui/sections/map_features.js +++ b/modules/ui/sections/map_features.js @@ -1,7 +1,6 @@ import { t } from '../../util/locale'; import { uiTooltip } from '../tooltip'; import { uiSection } from '../section'; -import { uiTooltipHtml } from '../tooltipHtml'; export function uiSectionMapFeatures(context) { @@ -67,14 +66,13 @@ export function uiSectionMapFeatures(context) { var enter = items.enter() .append('li') .call(uiTooltip() - .html(true) .title(function(d) { var tip = t(name + '.' + d + '.tooltip'); if (autoHiddenFeature(d)) { var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden'); tip += '
' + msg + '
'; } - return uiTooltipHtml(tip); + return tip; }) .placement('top') ); diff --git a/modules/ui/sections/map_style_options.js b/modules/ui/sections/map_style_options.js index 9881b70f7..3fd24c701 100644 --- a/modules/ui/sections/map_style_options.js +++ b/modules/ui/sections/map_style_options.js @@ -5,7 +5,6 @@ import { import { t } from '../../util/locale'; import { uiTooltip } from '../tooltip'; import { uiSection } from '../section'; -import { uiTooltipHtml } from '../tooltipHtml'; export function uiSectionMapStyleOptions(context) { @@ -48,12 +47,13 @@ export function uiSectionMapStyleOptions(context) { var enter = items.enter() .append('li') .call(uiTooltip() - .html(true) .title(function(d) { - var tip = t(name + '.' + d + '.tooltip'); + return t(name + '.' + d + '.tooltip'); + }) + .keys(function(d) { var key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null); if (d === 'highlight_edits') key = t('map_data.highlight_edits.key'); - return uiTooltipHtml(tip, key); + return [key]; }) .placement('top') ); diff --git a/modules/ui/tools/modes.js b/modules/ui/tools/modes.js index 7d43f2b32..cba95034a 100644 --- a/modules/ui/tools/modes.js +++ b/modules/ui/tools/modes.js @@ -12,7 +12,6 @@ import { import { t } from '../../util/locale'; import { svgIcon } from '../../svg'; import { uiTooltip } from '../tooltip'; -import { uiTooltipHtml } from '../tooltipHtml'; export function uiToolOldDrawModes(context) { @@ -128,8 +127,8 @@ export function uiToolOldDrawModes(context) { }) .call(uiTooltip() .placement('bottom') - .html(true) - .title(function(d) { return uiTooltipHtml(d.description, d.key); }) + .title(function(d) { return d.description; }) + .keys(function(d) { return [d.key]; }) .scrollContainer(context.container().select('.top-toolbar')) ); diff --git a/modules/ui/tools/notes.js b/modules/ui/tools/notes.js index 7a4c7fcff..ef494f327 100644 --- a/modules/ui/tools/notes.js +++ b/modules/ui/tools/notes.js @@ -10,7 +10,6 @@ import { import { t } from '../../util/locale'; import { svgIcon } from '../../svg'; import { uiTooltip } from '../tooltip'; -import { uiTooltipHtml } from '../tooltipHtml'; export function uiToolNotes(context) { @@ -105,8 +104,8 @@ export function uiToolNotes(context) { }) .call(uiTooltip() .placement('bottom') - .html(true) - .title(function(d) { return uiTooltipHtml(d.description, d.key); }) + .title(function(d) { return d.description; }) + .keys(function(d) { return [d.key]; }) .scrollContainer(context.container().select('.top-toolbar')) ); diff --git a/modules/ui/tools/save.js b/modules/ui/tools/save.js index 1ec4018a6..c1d648545 100644 --- a/modules/ui/tools/save.js +++ b/modules/ui/tools/save.js @@ -5,7 +5,6 @@ import { t } from '../../util/locale'; import { modeSave } from '../../modes'; import { svgIcon } from '../../svg'; import { uiCmd } from '../cmd'; -import { uiTooltipHtml } from '../tooltipHtml'; import { uiTooltip } from '../tooltip'; @@ -59,9 +58,8 @@ export function uiToolSave(context) { if (tooltipBehavior) { tooltipBehavior - .title(uiTooltipHtml( - t(_numChanges > 0 ? 'save.help' : 'save.no_changes'), key) - ); + .title(t(_numChanges > 0 ? 'save.help' : 'save.no_changes')) + .keys([key]); } if (button) { @@ -78,8 +76,8 @@ export function uiToolSave(context) { tool.render = function(selection) { tooltipBehavior = uiTooltip() .placement('bottom') - .html(true) - .title(uiTooltipHtml(t('save.no_changes'), key)) + .title(t('save.no_changes')) + .keys([key]) .scrollContainer(context.container().select('.top-toolbar')); button = selection diff --git a/modules/ui/tools/sidebar_toggle.js b/modules/ui/tools/sidebar_toggle.js index 5416e4b44..38c231dbf 100644 --- a/modules/ui/tools/sidebar_toggle.js +++ b/modules/ui/tools/sidebar_toggle.js @@ -1,6 +1,5 @@ import { t, textDirection } from '../../util/locale'; import { svgIcon } from '../../svg'; -import { uiTooltipHtml } from '../tooltipHtml'; import { uiTooltip } from '../tooltip'; export function uiToolSidebarToggle(context) { @@ -19,8 +18,8 @@ export function uiToolSidebarToggle(context) { }) .call(uiTooltip() .placement('bottom') - .html(true) - .title(uiTooltipHtml(t('sidebar.tooltip'), t('sidebar.key'))) + .title(t('sidebar.tooltip')) + .keys([t('sidebar.key')]) .scrollContainer(context.container().select('.top-toolbar')) ) .call(svgIcon('#iD-icon-sidebar-' + (textDirection === 'rtl' ? 'right' : 'left'))); diff --git a/modules/ui/tools/undo_redo.js b/modules/ui/tools/undo_redo.js index 2a1a3c5f1..3532ef8ec 100644 --- a/modules/ui/tools/undo_redo.js +++ b/modules/ui/tools/undo_redo.js @@ -8,7 +8,6 @@ import { import { t, textDirection } from '../../util/locale'; import { svgIcon } from '../../svg'; import { uiCmd } from '../cmd'; -import { uiTooltipHtml } from '../tooltipHtml'; import { uiTooltip } from '../tooltip'; @@ -40,11 +39,13 @@ export function uiToolUndoRedo(context) { tool.render = function(selection) { var tooltipBehavior = uiTooltip() .placement('bottom') - .html(true) .title(function (d) { - return uiTooltipHtml(d.annotation() ? + return d.annotation() ? t(d.id + '.tooltip', {action: d.annotation()}) : - t(d.id + '.nothing'), d.cmd); + t(d.id + '.nothing'); + }) + .keys(function(d) { + return [d.cmd]; }) .scrollContainer(context.container().select('.top-toolbar')); diff --git a/modules/ui/tooltip.js b/modules/ui/tooltip.js index b093021dd..3cec23c8f 100644 --- a/modules/ui/tooltip.js +++ b/modules/ui/tooltip.js @@ -1,4 +1,5 @@ import { utilFunctor } from '../util/util'; +import { t } from '../util/locale'; import { uiPopover } from './popover'; export function uiTooltip(klass) { @@ -17,34 +18,84 @@ export function uiTooltip(klass) { } return title; }; - var _html = utilFunctor(false); + var _heading = utilFunctor(null); + var _keys = utilFunctor(null); tooltip.title = function(val) { - if (arguments.length) { - _title = utilFunctor(val); - return tooltip; - } else { - return _title; - } + if (!arguments.length) return _title; + _title = utilFunctor(val); + return tooltip; }; + tooltip.heading = function(val) { + if (!arguments.length) return _heading; + _heading = utilFunctor(val); + return tooltip; + }; - tooltip.html = function(val) { - if (arguments.length) { - _html = utilFunctor(val); - return tooltip; - } else { - return _html; - } + tooltip.keys = function(val) { + if (!arguments.length) return _keys; + _keys = utilFunctor(val); + return tooltip; }; tooltip.content(function() { - var content = _title.apply(this, arguments); - var markup = _html.apply(this, arguments); + var heading = _heading.apply(this, arguments); + var text = _title.apply(this, arguments); + var keys = _keys.apply(this, arguments); return function(selection) { - selection[markup ? 'html' : 'text'](content); + + var headingSelect = selection + .selectAll('.tooltip-heading') + .data(heading ? [heading] :[]); + + headingSelect.exit() + .remove(); + + headingSelect.enter() + .append('div') + .attr('class', 'tooltip-heading') + .html(heading); + + var textSelect = selection + .selectAll('.tooltip-text') + .data(text ? [text] :[]); + + textSelect.exit() + .remove(); + + textSelect.enter() + .append('div') + .attr('class', 'tooltip-text') + .html(text); + + var keyhintWrap = selection + .selectAll('.keyhint-wrap') + .data(keys && keys.length ? [0] : []); + + keyhintWrap.exit() + .remove(); + + var keyhintWrapEnter = keyhintWrap.enter() + .append('div') + .attr('class', 'keyhint-wrap'); + + keyhintWrapEnter + .append('span') + .html(t('tooltip_keyhint')); + + keyhintWrap = keyhintWrapEnter.merge(keyhintWrap); + + keyhintWrap.selectAll('kbd.shortcut') + .data(keys && keys.length ? keys : []) + .enter() + .append('kbd') + .attr('class', 'shortcut') + .html(function(d) { + return d; + }); }; }); diff --git a/modules/ui/tooltipHtml.js b/modules/ui/tooltipHtml.js deleted file mode 100644 index 597d1a0d9..000000000 --- a/modules/ui/tooltipHtml.js +++ /dev/null @@ -1,23 +0,0 @@ -import { t } from '../util/locale'; - - -export function uiTooltipHtml(text, keys, heading) { - var s = ''; - - if (heading) { - s += '
' + heading + '
'; - } - if (text) { - s += '
' + text + '
'; - } - if (keys) { - if (!Array.isArray(keys)) keys = [keys]; - s += '
' + t('tooltip_keyhint') + ''; - keys.forEach(function(key) { - s += '' + key + ''; - }); - s += '
'; - } - - return s; -} diff --git a/modules/ui/zoom.js b/modules/ui/zoom.js index 0d6690a7a..eaa99b54e 100644 --- a/modules/ui/zoom.js +++ b/modules/ui/zoom.js @@ -6,7 +6,6 @@ import { import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg/icon'; import { uiCmd } from './cmd'; -import { uiTooltipHtml } from './tooltipHtml'; import { uiTooltip } from './tooltip'; @@ -59,9 +58,11 @@ export function uiZoom(context) { }) .call(uiTooltip() .placement((textDirection === 'rtl') ? 'right' : 'left') - .html(true) .title(function(d) { - return uiTooltipHtml(d.title, d.key); + return d.title; + }) + .keys(function(d) { + return [d.key]; }) ); diff --git a/modules/ui/zoom_to_selection.js b/modules/ui/zoom_to_selection.js index 239b36906..6574eb7d4 100644 --- a/modules/ui/zoom_to_selection.js +++ b/modules/ui/zoom_to_selection.js @@ -2,7 +2,6 @@ import { select as d3_select } from 'd3-selection'; import { t, textDirection } from '../util/locale'; import { uiTooltip } from './tooltip'; -import { uiTooltipHtml } from './tooltipHtml'; import { svgIcon } from '../svg/icon'; export function uiZoomToSelection(context) { @@ -34,8 +33,8 @@ export function uiZoomToSelection(context) { .call(svgIcon('#iD-icon-framed-dot', 'light')) .call(uiTooltip() .placement((textDirection === 'rtl') ? 'right' : 'left') - .html(true) - .title(uiTooltipHtml(t('inspector.zoom_to.title'), t('inspector.zoom_to.key'))) + .title(t('inspector.zoom_to.title')) + .keys([t('inspector.zoom_to.key')]) ); setEnabledState();