diff --git a/CHANGELOG.md b/CHANGELOG.md index 59de18d..8726e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,10 @@ The format is inspired by Keep a Changelog and SemVer. when not current). Centralized in a new `format_time.js` helper used by both the Load dialog rows and the status tooltip so future surfaces stay aligned. +- **Sidebar context menus now enforce a single open menu.** Opening a + workflow, directory, archive, or Workflows-header right-click menu now closes + any previous sidebar context menu first, so repeated right-clicks cannot + stack multiple floating menus with stale actions. - **"Create directory" moved off the Workflows toolbar onto right-click of the section header.** The 📂 (`pi-folder-open`) button used to live in the Workflows action bar between the section label and the Save buttons. Tree- diff --git a/web/sidebar/modals.js b/web/sidebar/modals.js index e4d6ed4..9ac1872 100644 --- a/web/sidebar/modals.js +++ b/web/sidebar/modals.js @@ -2149,12 +2149,34 @@ export function showSnapshotSettingsDialog({ // ============================================================================= // Context menu helper // ============================================================================= +let activeContextMenuCleanup = null; + +function closeActiveContextMenu() { + if (activeContextMenuCleanup) { + activeContextMenuCleanup(); + activeContextMenuCleanup = null; + } +} + export function showContextMenu(event, items) { event.preventDefault(); event.stopPropagation(); + closeActiveContextMenu(); const menu = document.createElement("div"); menu.className = "koolook-context-menu"; + let closeOnClick = null; + + const cleanup = () => { + menu.remove(); + if (closeOnClick) { + document.removeEventListener("click", closeOnClick); + document.removeEventListener("contextmenu", closeOnClick); + closeOnClick = null; + } + if (activeContextMenuCleanup === cleanup) activeContextMenuCleanup = null; + }; + activeContextMenuCleanup = cleanup; for (const item of items) { if (!item) { @@ -2172,7 +2194,7 @@ export function showContextMenu(event, items) { m.style.cursor = "not-allowed"; } else { m.addEventListener("click", () => { - menu.remove(); + cleanup(); item.action(); }); } @@ -2188,11 +2210,9 @@ export function showContextMenu(event, items) { if (rect.bottom > window.innerHeight) menu.style.top = `${event.clientY - rect.height}px`; setTimeout(() => { - const closeOnClick = (ev) => { + closeOnClick = (ev) => { if (!menu.contains(ev.target)) { - menu.remove(); - document.removeEventListener("click", closeOnClick); - document.removeEventListener("contextmenu", closeOnClick); + cleanup(); } }; document.addEventListener("click", closeOnClick);