From 563aee81e14e27cfa0f3fa8eb0a62f00907b8031 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 4 Apr 2025 10:21:04 +0200 Subject: [PATCH] feat: add keyboard shortcut for redo (ctrl+shift+z) --- umap/static/umap/js/modules/help.js | 8 +- umap/static/umap/js/modules/ui/bar.js | 11 +- umap/static/umap/js/modules/umap.js | 156 +++++++++++++------------- umap/static/umap/js/modules/utils.js | 1 + 4 files changed, 94 insertions(+), 82 deletions(-) diff --git a/umap/static/umap/js/modules/help.js b/umap/static/umap/js/modules/help.js index 5d396a79..c5c2fbad 100644 --- a/umap/static/umap/js/modules/help.js +++ b/umap/static/umap/js/modules/help.js @@ -36,9 +36,13 @@ const SHORTCUTS = { shortcut: 'Modifier+F', label: translate('Search location'), }, - CANCEL: { + UNDO: { shortcut: 'Modifier+Z', - label: translate('Cancel edits'), + label: translate('Cancel last edit'), + }, + REDO: { + shortcut: 'Modifier+Shift+Z', + label: translate('Redo last edit'), }, PREVIEW: { shortcut: 'Modifier+E', diff --git a/umap/static/umap/js/modules/ui/bar.js b/umap/static/umap/js/modules/ui/bar.js index 04659db4..d8f49102 100644 --- a/umap/static/umap/js/modules/ui/bar.js +++ b/umap/static/umap/js/modules/ui/bar.js @@ -126,13 +126,22 @@ export class TopBar extends WithTemplate { this.elements.undo.addEventListener('click', () => this._umap.undo()) this.elements.undo.addEventListener('mouseover', () => { this._umap.tooltip.open({ - content: this._umap.help.displayLabel('CANCEL'), + content: this._umap.help.displayLabel('UNDO'), anchor: this.elements.undo, position: 'bottom', delay: 500, duration: 5000, }) }) + this.elements.redo.addEventListener('mouseover', () => { + this._umap.tooltip.open({ + content: this._umap.help.displayLabel('REDO'), + anchor: this.elements.redo, + position: 'bottom', + delay: 500, + duration: 5000, + }) + }) this.elements.view.addEventListener('click', () => this._umap.disableEdit()) this.elements.view.addEventListener('mouseover', () => { this._umap.tooltip.open({ diff --git a/umap/static/umap/js/modules/umap.js b/umap/static/umap/js/modules/umap.js index 426cc30a..4ff4e091 100644 --- a/umap/static/umap/js/modules/umap.js +++ b/umap/static/umap/js/modules/umap.js @@ -499,90 +499,88 @@ export default class Umap { } initShortcuts() { - const globalShortcuts = (event) => { - if (event.key === 'Escape') { - if (this.importer.dialog.visible) { - this.importer.dialog.close() - } else if (this.editEnabled && this._leafletMap.editTools.drawing()) { - this._leafletMap.editTools.onEscape() - } else if (this._leafletMap.measureTools.enabled()) { - this._leafletMap.measureTools.stopDrawing() - } else if (this.fullPanel?.isOpen()) { - this.fullPanel?.close() - } else if (this.editPanel?.isOpen()) { - this.editPanel?.close() - } else if (this.panel.isOpen()) { - this.panel.close() - } - } - - // From now on, only ctrl/meta shortcut - if (!(event.ctrlKey || event.metaKey) || event.shiftKey) return - - if (event.key === 'f') { - event.stopPropagation() - event.preventDefault() - this.search() - } - - /* Edit mode only shortcuts */ - if (!this.hasEditMode()) return - - // Edit mode Off - if (!this.editEnabled) { - switch (event.key) { - case 'e': - event.stopPropagation() - event.preventDefault() - this.enableEdit() - break - } - return - } - - // Edit mode on - let used = true - switch (event.key) { - case 'e': - if (!this.isDirty) this.disableEdit() - break - case 's': - if (this.isDirty) this.saveAll() - break - case 'z': - if (Utils.isWritable(event.target)) { - used = false - break + const shortcuts = { + Escape: { + do: () => { + if (this.importer.dialog.visible) { + this.importer.dialog.close() + } else if (this.editEnabled && this._leafletMap.editTools.drawing()) { + this._leafletMap.editTools.onEscape() + } else if (this._leafletMap.measureTools.enabled()) { + this._leafletMap.measureTools.stopDrawing() + } else if (this.fullPanel?.isOpen()) { + this.fullPanel?.close() + } else if (this.editPanel?.isOpen()) { + this.editPanel?.close() + } else if (this.panel.isOpen()) { + this.panel.close() } - this.sync._undoManager.undo() - break - case 'm': - this._leafletMap.editTools.startMarker() - break - case 'p': - this._leafletMap.editTools.startPolygon() - break - case 'l': - this._leafletMap.editTools.startPolyline() - break - case 'i': - this.importer.open() - break - case 'o': - this.importer.openFiles() - break - case 'h': - this.help.showGetStarted() - break - default: - used = false - } - if (used) { + }, + }, + 'Ctrl+f': { + do: () => { + this.search() + }, + }, + 'Ctrl+e': { + if: () => this.hasEditMode(), + do: () => { + console.log('doing') + if (!this.editEnabled) this.enableEdit() + else if (!this.isDirty) this.disableEdit() + }, + }, + 'Ctrl+s': { + if: () => this.editEnabled && this.isDirty, + do: () => this.saveAll(), + }, + 'Ctrl+z': { + if: () => this.editEnabled && !Utils.isWritable(event.target), + do: () => this.sync._undoManager.undo(), + }, + 'Ctrl+Shift+Z': { + if: () => this.editEnabled && !Utils.isWritable(event.target), + do: () => this.sync._undoManager.redo(), + }, + 'Ctrl+m': { + if: () => this.editEnabled, + do: () => this._leafletMap.editTools.startMarker(), + }, + 'Ctrl+p': { + if: () => this.editEnabled, + do: () => this._leafletMap.editTools.startPolygon(), + }, + 'Ctrl+l': { + if: () => this.editEnabled, + do: () => this._leafletMap.editTools.startPolyline(), + }, + 'Ctrl+i': { + if: () => this.editEnabled, + do: () => this.importer.open(), + }, + 'Ctrl+o': { + if: () => this.editEnabled, + do: () => this.importer.openFiles(), + }, + 'Ctrl+h': { + if: () => this.editEnabled, + do: () => this.help.showGetStarted(), + }, + } + const onKeyDown = (event) => { + const shiftKey = event.shiftKey ? 'Shift+' : '' + const altKey = event.altKey ? 'Alt+' : '' + const ctrlKey = event.ctrlKey || event.metaKey ? 'Ctrl+' : '' + const combination = `${ctrlKey}${shiftKey}${altKey}${event.key}` + + const shortcut = shortcuts[combination] + if (shortcut && (!shortcut.if || shortcut.if())) { + shortcut.do() event.stopPropagation() event.preventDefault() } } - document.addEventListener('keydown', globalShortcuts) + document.addEventListener('keydown', onKeyDown) } async initDataLayers(datalayers) { diff --git a/umap/static/umap/js/modules/utils.js b/umap/static/umap/js/modules/utils.js index 58e3e1dd..17994485 100644 --- a/umap/static/umap/js/modules/utils.js +++ b/umap/static/umap/js/modules/utils.js @@ -117,6 +117,7 @@ export function escapeHTML(s) { 'dd', 'b', 'i', + 'kbd', ], ADD_ATTR: [ 'target',