From ee42f194014444acd60730ac389bf2e5895062be Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 18 Jun 2026 13:15:06 +0200 Subject: [PATCH 1/3] opening a dialog via shortcut now closes the currently opened dialog --- frontend/src/lib/dialog/AlertDialog.svelte | 4 +-- .../eventhandling/closeEventManager.svelte.js | 31 ++++++++++++++++--- .../lib/eventhandling/shortcutStore.svelte.js | 18 ++++++++--- frontend/src/routes/ImportDialog.svelte | 1 + .../src/routes/layout/menu-bar/Edit.svelte | 18 ++++++++--- .../src/routes/layout/menu-bar/File.svelte | 6 ++++ .../src/routes/layout/menu-bar/Help.svelte | 1 + .../src/routes/layout/menu-bar/View.svelte | 2 ++ .../mainpage/classEditor/classEditor.svelte | 2 +- 9 files changed, 68 insertions(+), 15 deletions(-) diff --git a/frontend/src/lib/dialog/AlertDialog.svelte b/frontend/src/lib/dialog/AlertDialog.svelte index d638330a..5843376f 100644 --- a/frontend/src/lib/dialog/AlertDialog.svelte +++ b/frontend/src/lib/dialog/AlertDialog.svelte @@ -33,11 +33,11 @@
diff --git a/frontend/src/lib/eventhandling/closeEventManager.svelte.js b/frontend/src/lib/eventhandling/closeEventManager.svelte.js index e64388f5..8d010c39 100644 --- a/frontend/src/lib/eventhandling/closeEventManager.svelte.js +++ b/frontend/src/lib/eventhandling/closeEventManager.svelte.js @@ -25,12 +25,16 @@ class EventStack { stack; - addEvent(event) { - this.stack.push(event); + /** + * @param {() => void} event - the close handler + * @param {string} [type] - optional category, e.g. "dialog" or "classEditor" + */ + addEvent(event, type = "dialog") { + this.stack.push({ event, type }); } removeEvent(event) { - const idOfEvent = this.stack.indexOf(event); + const idOfEvent = this.stack.findIndex(e => e.event === event); if (idOfEvent === -1) return; this.stack.splice(idOfEvent, 1); } @@ -43,7 +47,26 @@ class EventStack { ); return; } - this.stack.at(-1)(...args); + this.stack.at(-1).event(...args); + } + + /** + * Closes all open events whose type is not in the excludedTypes list. + * Used when opening a new dialog via shortcut: all other dialogs close, + * but e.g. the ClassEditor (type "classEditor") stays open. + * @param {string[]} excludedTypes + */ + closeAllExcept(excludedTypes = []) { + const toClose = this.stack + .filter(e => !excludedTypes.includes(e.type)) + .reverse(); + + for (const { event } of toClose) { + if (event() === false) { + return false; + } + } + return true; } registerActionGuard(fn) { diff --git a/frontend/src/lib/eventhandling/shortcutStore.svelte.js b/frontend/src/lib/eventhandling/shortcutStore.svelte.js index 0ae80ec7..82eea7b7 100644 --- a/frontend/src/lib/eventhandling/shortcutStore.svelte.js +++ b/frontend/src/lib/eventhandling/shortcutStore.svelte.js @@ -15,13 +15,16 @@ * */ +import { eventStack } from "$lib/eventhandling/closeEventManager.svelte.js"; + export const shortcutStore = { /** * @param {string} id - unique identifier * @param {string[] | string[][]} keys - e.g. ["ctrl", "s"] or [["ctrl", "s"], ["ctrl", "shift", "s"]] * @param {() => void} handler + * @param closeDialogs define if the shortcut should close open dialogs (default: false). If true, all open dialogs will be closed before executing the handler. */ - register(id, keys, handler) { + register(id, keys, handler, closeDialogs = false) { // Normalize to an array of key-combinations if (registry[id]) { console.warn( @@ -30,7 +33,7 @@ export const shortcutStore = { } const combinations = Array.isArray(keys[0]) ? keys : [keys]; const normalizedCombos = combinations.map(normalizeCombo); - registry[id] = { combos: normalizedCombos, handler }; + registry[id] = { combos: normalizedCombos, handler, closeDialogs }; return () => this.unregister(id); }, @@ -44,10 +47,17 @@ export const shortcutStore = { */ handleEvent(event) { const normalized = normalizeEvent(event); - for (const { combos, handler } of Object.values(registry)) { + for (const { combos, handler, closeDialogs } of Object.values( + registry, + )) { if (combos.includes(normalized)) { event.preventDefault(); - handler(); + eventStack.guardAction(() => { + if (closeDialogs) { + eventStack.closeAllExcept(["classEditor"]); + } + handler(); + }); return true; } } diff --git a/frontend/src/routes/ImportDialog.svelte b/frontend/src/routes/ImportDialog.svelte index 3a330afd..350f8860 100644 --- a/frontend/src/routes/ImportDialog.svelte +++ b/frontend/src/routes/ImportDialog.svelte @@ -309,6 +309,7 @@ onPrimary={importGraphs} disablePrimary={!enableSubmit} title="Import Schemas" + size="w-1/3" >
{#if !datasetSelectionLocked} diff --git a/frontend/src/routes/layout/menu-bar/Edit.svelte b/frontend/src/routes/layout/menu-bar/Edit.svelte index 33aa3bfb..0433f1bb 100644 --- a/frontend/src/routes/layout/menu-bar/Edit.svelte +++ b/frontend/src/routes/layout/menu-bar/Edit.svelte @@ -137,27 +137,37 @@ "newClass", ["shift", "n"], () => (showNewClassDialog = true), + true, ), shortcutStore.register( "newPackage", ["alt", "n"], () => (showNewPackageDialog = true), + true, ), shortcutStore.register( "namespaces", ["ctrl", "shift", "a"], () => (showNamespaceDialog = true), + true, ), shortcutStore.register( "profileHeader", ["ctrl", "alt", "p"], () => (showEditOntologyDialog = true), + true, ), - shortcutStore.register("editPackage", ["ctrl", "shift", "k"], () => - launchPackageEditor(), + shortcutStore.register( + "editPackage", + ["ctrl", "shift", "k"], + () => launchPackageEditor(), + true, ), - shortcutStore.register("toggleEdit", ["ctrl", "alt", "r"], () => - toggleReadonly(), + shortcutStore.register( + "toggleEdit", + ["ctrl", "alt", "r"], + () => toggleReadonly(), + true, ), shortcutStore.register("copyClass", ["ctrl", "c"], () => copyClassWithShortcut(), diff --git a/frontend/src/routes/layout/menu-bar/File.svelte b/frontend/src/routes/layout/menu-bar/File.svelte index 854f9c58..b76f2628 100644 --- a/frontend/src/routes/layout/menu-bar/File.svelte +++ b/frontend/src/routes/layout/menu-bar/File.svelte @@ -69,31 +69,37 @@ "import", ["ctrl", "i"], () => (showImportDialog = true), + true, ), shortcutStore.register( "export", ["ctrl", "e"], () => (showExportDialog = true), + true, ), shortcutStore.register( "shaclImport", ["ctrl", "shift", "i"], () => (showSHACLUploadDialog = true), + true, ), shortcutStore.register( "shaclExport", ["ctrl", "shift", "e"], () => (showSHACLExportDialog = true), + true, ), shortcutStore.register( "snapshot", ["ctrl", "shift", "s"], () => (showSnapshotDialog = true), + true, ), shortcutStore.register( "settings", ["ctrl", "alt", "s"], () => (showUserSettingDialog = true), + true, ), ); }); diff --git a/frontend/src/routes/layout/menu-bar/Help.svelte b/frontend/src/routes/layout/menu-bar/Help.svelte index cf792824..7c732791 100644 --- a/frontend/src/routes/layout/menu-bar/Help.svelte +++ b/frontend/src/routes/layout/menu-bar/Help.svelte @@ -50,6 +50,7 @@ "keyboardShortcuts", ["?"], () => (showKeyboardShortcutsDialog = true), + true, ), ); }); diff --git a/frontend/src/routes/layout/menu-bar/View.svelte b/frontend/src/routes/layout/menu-bar/View.svelte index dbc265df..aa9bbee0 100644 --- a/frontend/src/routes/layout/menu-bar/View.svelte +++ b/frontend/src/routes/layout/menu-bar/View.svelte @@ -62,6 +62,7 @@ "compare", ["ctrl", "shift", "c"], () => (showCompareDialog = true), + true, ), shortcutStore.register("migrate", ["ctrl", "shift", "m"], () => goto("/migrate"), @@ -72,6 +73,7 @@ () => { if (hasGraphSelected) showSHACLFullViewDialog = true; }, + hasGraphSelected, ), ); }); diff --git a/frontend/src/routes/mainpage/classEditor/classEditor.svelte b/frontend/src/routes/mainpage/classEditor/classEditor.svelte index 6f9280fa..daa1fcf2 100644 --- a/frontend/src/routes/mainpage/classEditor/classEditor.svelte +++ b/frontend/src/routes/mainpage/classEditor/classEditor.svelte @@ -144,7 +144,7 @@ }); onMount(() => { - eventStack.addEvent(closeClassEditor); + eventStack.addEvent(closeClassEditor, "classEditor"); eventStack.registerActionGuard(withUnsavedChangesCheck); }); From e9ed19e83a614906d961467e1ce3a59478976011 Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 18 Jun 2026 13:18:08 +0200 Subject: [PATCH 2/3] increased height of the KeyboardShortcutsDialog --- frontend/src/routes/KeyboardShortcutsDialog.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/KeyboardShortcutsDialog.svelte b/frontend/src/routes/KeyboardShortcutsDialog.svelte index 86c5ab25..9cfbfbec 100644 --- a/frontend/src/routes/KeyboardShortcutsDialog.svelte +++ b/frontend/src/routes/KeyboardShortcutsDialog.svelte @@ -109,10 +109,10 @@ title="Keyboard Shortcuts" titleIcon={faKeyboard} readonly - size="w-full max-w-3xl" + size="w-full max-w-3xl h-2/3" >
{#each sections.slice(0, 2) as section} From 87ef0dc052699afbf17e4ddd8df8cdb9df61e5be Mon Sep 17 00:00:00 2001 From: Philipp Date: Thu, 18 Jun 2026 15:53:46 +0200 Subject: [PATCH 3/3] added EventType enum --- .../src/lib/eventhandling/closeEventManager.svelte.js | 9 +++++++-- frontend/src/lib/eventhandling/shortcutStore.svelte.js | 7 +++++-- .../src/routes/mainpage/classEditor/classEditor.svelte | 7 +++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/eventhandling/closeEventManager.svelte.js b/frontend/src/lib/eventhandling/closeEventManager.svelte.js index 8d010c39..997eefbe 100644 --- a/frontend/src/lib/eventhandling/closeEventManager.svelte.js +++ b/frontend/src/lib/eventhandling/closeEventManager.svelte.js @@ -17,6 +17,11 @@ export let eventStack; +export const EventType = { + DIALOG: "dialog", + CLASS_EDITOR: "classEditor", +}; + class EventStack { constructor() { this.stack = []; @@ -27,9 +32,9 @@ class EventStack { /** * @param {() => void} event - the close handler - * @param {string} [type] - optional category, e.g. "dialog" or "classEditor" + * @param {string} [type] - optional category, e.g. "DIALOG" or "CLASS_EDITOR" */ - addEvent(event, type = "dialog") { + addEvent(event, type = EventType.DIALOG) { this.stack.push({ event, type }); } diff --git a/frontend/src/lib/eventhandling/shortcutStore.svelte.js b/frontend/src/lib/eventhandling/shortcutStore.svelte.js index 82eea7b7..f4762c2e 100644 --- a/frontend/src/lib/eventhandling/shortcutStore.svelte.js +++ b/frontend/src/lib/eventhandling/shortcutStore.svelte.js @@ -15,7 +15,10 @@ * */ -import { eventStack } from "$lib/eventhandling/closeEventManager.svelte.js"; +import { + eventStack, + EventType, +} from "$lib/eventhandling/closeEventManager.svelte.js"; export const shortcutStore = { /** @@ -54,7 +57,7 @@ export const shortcutStore = { event.preventDefault(); eventStack.guardAction(() => { if (closeDialogs) { - eventStack.closeAllExcept(["classEditor"]); + eventStack.closeAllExcept([EventType.CLASS_EDITOR]); } handler(); }); diff --git a/frontend/src/routes/mainpage/classEditor/classEditor.svelte b/frontend/src/routes/mainpage/classEditor/classEditor.svelte index daa1fcf2..333d5b2e 100644 --- a/frontend/src/routes/mainpage/classEditor/classEditor.svelte +++ b/frontend/src/routes/mainpage/classEditor/classEditor.svelte @@ -23,7 +23,10 @@ import { BackendConnection } from "$lib/api/backend.js"; import LoadingSpinner from "$lib/components/LoadingSpinner.svelte"; import { PUBLIC_BACKEND_URL } from "$lib/config/runtime"; - import { eventStack } from "$lib/eventhandling/closeEventManager.svelte.js"; + import { + eventStack, + EventType, + } from "$lib/eventhandling/closeEventManager.svelte.js"; import { mapClassDtoToReactiveClass } from "$lib/models/reactive/mapper/map-dto-to-reactive-object.js"; import { adoptUnsavedClassChanges } from "$lib/models/reactive/utils/adopt-model-changes-utils.js"; import { @@ -144,7 +147,7 @@ }); onMount(() => { - eventStack.addEvent(closeClassEditor, "classEditor"); + eventStack.addEvent(closeClassEditor, EventType.CLASS_EDITOR); eventStack.registerActionGuard(withUnsavedChangesCheck); });