diff --git a/css/80_app.css b/css/80_app.css index 4c4745b1e6..d29722c1c3 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -172,7 +172,7 @@ a:visited, a { } a:hover { - color:#597be7; + color: #597be7; } /* Forms @@ -646,7 +646,7 @@ button.save.has-count .count::before { position: absolute; right: 0; top: 0; - height: 59px; + height: 60px; z-index: 50; } @@ -3082,6 +3082,81 @@ img.tile-removing { } +/* Shortcuts Modal +------------------------------------------------------- */ +.modal-shortcuts { + width: 75%; + max-width: 800px; +} + +.modal-shortcuts .modal-section:last-child { + padding-top: 10px; +} + +.modal-shortcuts .tabs-bar { + text-align: center; + padding-bottom: 10px; + font-size: 16px; + font-weight: bold; +} + +.modal-shortcuts .tab { + display: inline-block; + padding: 5px 10px; + margin: 0 5px; + cursor: pointer; + color: #666; +} +.modal-shortcuts .tab.active { + color: #7092ff; +} +.modal-shortcuts .tab:hover { + color: #597be7; + background-color: #efefef; +} + +.modal-shortcuts .shortcut-tab { + display: flex; + flex-flow: row wrap; +} + +.modal-shortcuts .shortcut-column { + flex: 1 1 50%; + width: 50%; +} + +.modal-shortcuts td { + padding-bottom: 5px; +} + +.modal-shortcuts .shortcut-section { + padding: 15px 0 10px 0; +} + +.modal-shortcuts .shortcut-keys { + padding-right: 10px; + color: #767676; + text-align: right; +} + +.modal-shortcuts .shortcut-keys kbd { + display: inline-block; + text-align: center; + padding: 3px 5px; + font-size: 11px; + line-height: 12px; + min-width: 12px; + color: #555; + vertical-align: baseline; + background-color: #fcfcfc; + border: solid 1px #ccc; + margin: 0 2px; + border-bottom-color: #bbb; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #bbb; +} + + /* Save Mode ------------------------------------------------------- */ .mode-save a.user-info { @@ -4015,3 +4090,20 @@ li.hide + li.version .badge .tooltip .tooltip-arrow { [dir='rtl'] .spin-control button.increment{ border-bottom-left-radius: 3px; } +/* modal */ +[dir='rtl'] .modal > button { + position: absolute; + left: 0; + right: unset; + top: 0; +} + +/* shortcuts modal */ + +[dir='rtl'] .kbd-row { + padding-left: 10px; + color: #767676; + text-align: left; + padding-bottom: 5px; + width: 50%; +} diff --git a/data/core.yaml b/data/core.yaml index 0d9695e18a..5961f997e9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -560,7 +560,7 @@ en: ### Using the editor - A list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts). + You can view a list of keyboard shortcuts by pressing the `?` key. roads: | # Roads @@ -1016,3 +1016,71 @@ en: help: "You're now ready to edit OpenStreetMap!{br}You can replay this walkthrough anytime or view more documentation by clicking the {button} Help button." save: "Don't forget to regularly save your changes!" start: "Start mapping!" + shortcuts: + title: "Keyboard shortcuts" + browsing: + title: "Browsing" + navigation: + title: "Navigation" + pan: "Pan map" + pan_more: "Pan map by one screenful" + zoom: "Zoom in / Zoom out" + zoom_more: "Zoom in / Zoom out by a lot" + help: + title: "Help" + help: "Show help/documentation" + keyboard: "Show keyboard shortcuts" + display_options: + title: "Display options" + background: "Show background options" + background_switch: "Switch back to last background" + map_data: "Show map data options" + wireframe: "Toggle wireframe mode" + minimap: "Toggle minimap" + selecting: + title: "Selecting features" + select_one: "Select a single feature" + select_multi: "Select multiple features" + lasso: "Draw a selection lasso around features" + with_selected: + title: "With feature selected" + infobox: "Toggle info / measurement box" + edit_menu: "Toggle edit menu" + vertex_selected: + title: "With node selected" + previous: "Jump to previous node" + next: "Jump to next node" + first: "Jump to first node" + last: "Jump to last node" + change_parent: "Switch parent way" + editing: + title: "Editing" + drawing: + title: "Drawing" + add_point: "'Add point' mode" + add_line: "'Add line' mode" + add_area: "'Add area' mode" + place_point: "Place a point" + disable_snap: "Hold to disable point snapping" + stop_line: "Finish drawing a line or area" + operations: + title: "Operations" + continue_line: "Continue a line at the selected node" + merge: "Combine (merge) selected features" + disconnect: "Disconnect features at the selected node" + split: "Split a line into two at the selected node" + reverse: "Reverse a line" + move: "Move selected features" + rotate: "Rotate selected features" + orthogonalize: "Straighten line / Square area corners" + circularize: "Circularize a closed line or area" + reflect_long: "Reflect features across the longer axis" + reflect_short: "Reflect features across the shorter axis" + delete: "Delete selected features" + commands: + title: "Commands" + copy: "Copy selected features" + paste: "Paste selected features" + undo: "Undo last action" + redo: "Redo last action" + save: "Save changes" diff --git a/data/index.js b/data/index.js index 1fc44aaff1..f2ae2544ba 100644 --- a/data/index.js +++ b/data/index.js @@ -6,6 +6,7 @@ export { dataDeprecated } from './deprecated.json'; export { dataDiscarded } from './discarded.json'; export { dataLocales } from './locales.json'; export { dataPhoneFormats } from './phone-formats.json'; +export { dataShortcuts } from './shortcuts.json'; export { default as dataImperial } from './imperial.json'; export { default as dataDriveLeft } from './drive-left.json'; diff --git a/data/shortcuts.json b/data/shortcuts.json new file mode 100644 index 0000000000..ee2d0650c5 --- /dev/null +++ b/data/shortcuts.json @@ -0,0 +1,204 @@ +{ "dataShortcuts": [ + { + "tab": "browsing", + "text": "shortcuts.browsing.title", + "columns": [ + { + "rows": [ + { + "section": "navigation", + "text": "shortcuts.browsing.navigation.title" + }, { + "shortcut": ["↓", "↑", "←", "→"], + "text": "shortcuts.browsing.navigation.pan" + }, { + "shortcut": ["⇧↓", "⇧↑", "⇧←", "⇧→"], + "text": "shortcuts.browsing.navigation.pan_more" + }, { + "shortcut": ["+", "-"], + "text": "shortcuts.browsing.navigation.zoom" + }, { + "shortcut": ["⌘+", "⌘-"], + "text": "shortcuts.browsing.navigation.zoom_more" + }, + + { + "section": "help", + "text": "shortcuts.browsing.help.title" + }, { + "shortcut": ["H"], + "text": "shortcuts.browsing.help.help" + }, { + "shortcut": ["?"], + "text": "shortcuts.browsing.help.keyboard" + }, + + { + "section": "display_options", + "text": "shortcuts.browsing.display_options.title" + }, { + "shortcut": ["B"], + "text": "shortcuts.browsing.display_options.background" + }, { + "shortcut": ["⌘B"], + "text": "shortcuts.browsing.display_options.background_switch" + }, { + "shortcut": ["F"], + "text": "shortcuts.browsing.display_options.map_data" + }, { + "shortcut": ["W"], + "text": "shortcuts.browsing.display_options.wireframe" + }, { + "shortcut": ["/"], + "text": "shortcuts.browsing.display_options.minimap" + } + ] + }, { + "rows": [ + { + "section": "selecting", + "text": "shortcuts.browsing.selecting.title" + }, { + "shortcut": ["Left-click"], + "text": "shortcuts.browsing.selecting.select_one" + }, { + "shortcut": ["⇧ Left-click"], + "text": "shortcuts.browsing.selecting.select_multi" + }, { + "shortcut": ["⇧ Left-click + drag"], + "text": "shortcuts.browsing.selecting.lasso" + }, { + "shortcut": [], + "text": "" + }, + + { + "section": "with_selected", + "text": "shortcuts.browsing.with_selected.title" + }, { + "shortcut": ["⌘I"], + "text": "shortcuts.browsing.with_selected.infobox" + }, { + "shortcut": ["Right-click", "Space"], + "text": "shortcuts.browsing.with_selected.edit_menu" + }, + + { + "section": "vertex_selected", + "text": "shortcuts.browsing.vertex_selected.title" + }, { + "shortcut": ["[", "↖ PgUp"], + "text": "shortcuts.browsing.vertex_selected.previous" + }, { + "shortcut": ["]", "↘ PgDn"], + "text": "shortcuts.browsing.vertex_selected.next" + }, { + "shortcut": ["{", "⇞ Home"], + "text": "shortcuts.browsing.vertex_selected.first" + }, { + "shortcut": ["}", "⇟ End"], + "text": "shortcuts.browsing.vertex_selected.last" + }, { + "shortcut": ["\\", "↨ Pause"], + "text": "shortcuts.browsing.vertex_selected.change_parent" + } + + ] + } + ] + }, { + "tab": "editing", + "text": "shortcuts.editing.title", + "columns" : [ + { + "rows": [ + { + "section": "drawing", + "text": "shortcuts.editing.drawing.title" + }, { + "shortcut": ["1"], + "text": "shortcuts.editing.drawing.add_point" + }, { + "shortcut": ["2"], + "text": "shortcuts.editing.drawing.add_line" + }, { + "shortcut": ["3"], + "text": "shortcuts.editing.drawing.add_area" + }, { + "shortcut": ["Space"], + "text": "shortcuts.editing.drawing.place_point" + }, { + "shortcut": ["⌥ Alt"], + "text": "shortcuts.editing.drawing.disable_snap" + }, { + "shortcut": ["↵ Enter", "⎋ Esc"], + "text": "shortcuts.editing.drawing.stop_line" + }, + + { + "section": "commands", + "text": "shortcuts.editing.commands.title" + }, { + "shortcut": ["⌘C"], + "text": "shortcuts.editing.commands.copy" + }, { + "shortcut": ["⌘V"], + "text": "shortcuts.editing.commands.paste" + }, { + "shortcut": ["⌘Z"], + "text": "shortcuts.editing.commands.undo" + }, { + "shortcut": ["⌘⇧Z"], + "text": "shortcuts.editing.commands.redo" + }, { + "shortcut": ["⌘S"], + "text": "shortcuts.editing.commands.save" + } + ] + }, { + "rows": [ + { + "section": "operations", + "text": "shortcuts.editing.operations.title" + }, { + "shortcut": ["operations.continue.key"], + "text": "shortcuts.editing.operations.continue_line" + }, { + "shortcut": ["operations.merge.key"], + "text": "shortcuts.editing.operations.merge" + }, { + "shortcut": ["operations.disconnect.key"], + "text": "shortcuts.editing.operations.disconnect" + }, { + "shortcut": ["operations.split.key"], + "text": "shortcuts.editing.operations.split" + }, { + "shortcut": ["operations.reverse.key"], + "text": "shortcuts.editing.operations.reverse" + }, { + "shortcut": ["operations.move.key"], + "text": "shortcuts.editing.operations.move" + }, { + "shortcut": ["operations.rotate.key"], + "text": "shortcuts.editing.operations.rotate" + }, { + "shortcut": ["operations.orthogonalize.key"], + "text": "shortcuts.editing.operations.orthogonalize" + }, { + "shortcut": ["operations.circularize.key"], + "text": "shortcuts.editing.operations.circularize" + }, { + "shortcut": ["operations.reflect.key.long"], + "text": "shortcuts.editing.operations.reflect_long" + }, { + "shortcut": ["operations.reflect.key.short"], + "text": "shortcuts.editing.operations.reflect_short" + }, { + "shortcut": ["⌘⌫", "⌦"], + "text": "shortcuts.editing.operations.delete" + } + ] + } + ] + } +]} diff --git a/dist/locales/en.json b/dist/locales/en.json index d00bcd6640..c3e5eb98b7 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -626,7 +626,7 @@ "help": { "title": "Help", "help": "# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n", - "editing_saving": "# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n", + "editing_saving": "# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nYou can view a list of keyboard shortcuts by pressing the `?` key.\n", "roads": "# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n", "gps": "# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n", "imagery": "# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n", @@ -883,6 +883,86 @@ "start": "Start mapping!" } }, + "shortcuts": { + "title": "Keyboard shortcuts", + "browsing": { + "title": "Browsing", + "navigation": { + "title": "Navigation", + "pan": "Pan map", + "pan_more": "Pan map by one screenful", + "zoom": "Zoom in / Zoom out", + "zoom_more": "Zoom in / Zoom out by a lot" + }, + "help": { + "title": "Help", + "help": "Show help/documentation", + "keyboard": "Show keyboard shortcuts" + }, + "display_options": { + "title": "Display options", + "background": "Show background options", + "background_switch": "Switch back to last background", + "map_data": "Show map data options", + "wireframe": "Toggle wireframe mode", + "minimap": "Toggle minimap" + }, + "selecting": { + "title": "Selecting features", + "select_one": "Select a single feature", + "select_multi": "Select multiple features", + "lasso": "Draw a selection lasso around features" + }, + "with_selected": { + "title": "With feature selected", + "infobox": "Toggle info / measurement box", + "edit_menu": "Toggle edit menu" + }, + "vertex_selected": { + "title": "With node selected", + "previous": "Jump to previous node", + "next": "Jump to next node", + "first": "Jump to first node", + "last": "Jump to last node", + "change_parent": "Switch parent way" + } + }, + "editing": { + "title": "Editing", + "drawing": { + "title": "Drawing", + "add_point": "'Add point' mode", + "add_line": "'Add line' mode", + "add_area": "'Add area' mode", + "place_point": "Place a point", + "disable_snap": "Hold to disable point snapping", + "stop_line": "Finish drawing a line or area" + }, + "operations": { + "title": "Operations", + "continue_line": "Continue a line at the selected node", + "merge": "Combine (merge) selected features", + "disconnect": "Disconnect features at the selected node", + "split": "Split a line into two at the selected node", + "reverse": "Reverse a line", + "move": "Move selected features", + "rotate": "Rotate selected features", + "orthogonalize": "Straighten line / Square area corners", + "circularize": "Circularize a closed line or area", + "reflect_long": "Reflect features across the longer axis", + "reflect_short": "Reflect features across the shorter axis", + "delete": "Delete selected features" + }, + "commands": { + "title": "Commands", + "copy": "Copy selected features", + "paste": "Paste selected features", + "undo": "Undo last action", + "redo": "Redo last action", + "save": "Save changes" + } + } + }, "presets": { "categories": { "category-barrier": { diff --git a/modules/ui/init.js b/modules/ui/init.js index 8908bd27a4..243943a59b 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -24,6 +24,7 @@ import { uiModes } from './modes'; import { uiRestore } from './restore'; import { uiSave } from './save'; import { uiScale } from './scale'; +import { uiShortcuts } from './shortcuts'; import { uiSidebar } from './sidebar'; import { uiSpinner } from './spinner'; import { uiSplash } from './splash'; @@ -283,7 +284,8 @@ export function uiInit(context) { if (!uiInitCounter++) { context.container() .call(uiSplash(context)) - .call(uiRestore(context)); + .call(uiRestore(context)) + .call(uiShortcuts(context)); } var authenticating = uiLoading(context) diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js new file mode 100644 index 0000000000..6179f41b99 --- /dev/null +++ b/modules/ui/shortcuts.js @@ -0,0 +1,166 @@ +import * as d3 from 'd3'; +import { uiCmd } from './cmd'; +import { uiModal } from './modal'; +import { d3keybinding } from '../lib/d3.keybinding.js'; +import { t } from '../util/locale'; +import { dataShortcuts } from '../../data'; + + +export function uiShortcuts() { + var activeTab = 0; + var modalSelection; + var savedSelection; + + var keybinding = d3keybinding('shortcuts') + .on(['?', '⇧/'], function () { + if (modalSelection) { + modalSelection.close(); + modalSelection = null; + return; + } + modalSelection = uiModal(savedSelection); + shortcutsModal(modalSelection); + }); + + d3.select(document) + .call(keybinding); + + + function shortcutsModal(modalSelection) { + modalSelection.select('.modal') + .attr('class', 'modal modal-shortcuts fillL col6'); + + var shortcutsModal = modalSelection.select('.content'); + + shortcutsModal + .append('div') + .attr('class', 'modal-section') + .append('h3') + .text(t('shortcuts.title')); + + shortcutsModal + .call(render); + } + + + function render(selection) { + var wrapper = selection + .selectAll('.wrapper') + .data([0]); + + var wrapperEnter = wrapper + .enter() + .append('div') + .attr('class', 'wrapper modal-section'); + + var tabsBar = wrapperEnter + .append('div') + .attr('class', 'tabs-bar'); + + var shortcutsList = wrapperEnter + .append('div') + .attr('class', 'shortcuts-list'); + + wrapper = wrapper.merge(wrapperEnter); + + var tabs = tabsBar + .selectAll('.tab') + .data(dataShortcuts); + + var tabsEnter = tabs + .enter() + .append('div') + .attr('class', 'tab') + .on('click', function (d, i) { + activeTab = i; + render(selection); + }); + + tabsEnter + .append('span') + .text(function (d) { return t(d.text); }); + + tabs = tabs + .merge(tabsEnter); + + // Update + wrapper.selectAll('.tab') + .classed('active', function (d, i) { + return i === activeTab; + }); + + + var shortcuts = shortcutsList + .selectAll('.shortcut-tab') + .data(dataShortcuts); + + var shortcutsEnter = shortcuts + .enter() + .append('div') + .attr('class', 'shortcut-tab'); + + var columnsEnter = shortcutsEnter + .selectAll('.shortcut-column') + .data(function (d) { return d.columns; }) + .enter() + .append('table') + .attr('class', 'shortcut-column'); + + var rowsEnter = columnsEnter + .selectAll('.shortcut-row') + .data(function (d) { return d.rows; }) + .enter() + .append('tr') + .attr('class', 'shortcut-row'); + + var sectionRows = rowsEnter + .filter(function (d) { return !d.shortcut; }); + + sectionRows + .append('td'); + + sectionRows + .append('td') + .attr('class', 'shortcut-section') + .append('h3') + .text(function (d) { return t(d.text); }); + + var shortcutRows = rowsEnter + .filter(function (d) { return d.shortcut; }); + + shortcutRows + .append('td') + .attr('class', 'shortcut-keys') + .selectAll('kbd') + .data(function (d) { return d.shortcut; }) + .enter() + .append('kbd') + .text(function (d) { + return d.indexOf('.') !== -1 ? uiCmd(t(d)) : uiCmd(d); + }); + + shortcutRows + .append('td') + .attr('class', 'shortcut-desc') + .text(function (d) { return d.text ? t(d.text) : '\u00a0'; }); + + + shortcuts = shortcuts + .merge(shortcutsEnter); + + // Update + wrapper.selectAll('.shortcut-tab') + .style('display', function (d, i) { + return i === activeTab ? 'flex' : 'none'; + }); + } + + + return function(selection, show) { + savedSelection = selection; + if (show) { + modalSelection = uiModal(selection); + shortcutsModal(modalSelection); + } + }; +}