diff --git a/frontend/src/lib/components/common/contextmenu/ContextMenu.svelte b/frontend/src/lib/components/common/contextmenu/ContextMenu.svelte index 7302c4948a..b46f1ed7e4 100644 --- a/frontend/src/lib/components/common/contextmenu/ContextMenu.svelte +++ b/frontend/src/lib/components/common/contextmenu/ContextMenu.svelte @@ -3,6 +3,14 @@ import { fly } from 'svelte/transition' import { twMerge } from 'tailwind-merge' import type { Snippet } from 'svelte' + import { + getContextMenuContainerClass, + CONTEXT_MENU_ITEM_BASE_CLASS, + CONTEXT_MENU_ITEM_HOVER_MELT_CLASS, + CONTEXT_MENU_ITEM_DISABLED_CLASS, + CONTEXT_MENU_DIVIDER_CLASS, + CONTEXT_MENU_ANIMATION_CLASSES + } from './contextMenuStyles' export interface ContextMenuItem { id: string @@ -61,20 +69,20 @@ {#if $open}
{#each items as menuItem (menuItem.id)} {#if menuItem.divider} -
+
{:else}
handleItemClick(menuItem)} diff --git a/frontend/src/lib/components/common/contextmenu/contextMenuStyles.ts b/frontend/src/lib/components/common/contextmenu/contextMenuStyles.ts new file mode 100644 index 0000000000..ecbe6c6f87 --- /dev/null +++ b/frontend/src/lib/components/common/contextmenu/contextMenuStyles.ts @@ -0,0 +1,45 @@ +/** + * Shared styles for context menu components + * Ensures visual consistency across all context menu implementations + */ + +/** + * Base container styles for context menu + * @param zIndex - Optional z-index override (default: 'z-50') + */ +export function getContextMenuContainerClass(zIndex: string = 'z-50'): string { + return `${zIndex} flex flex-col gap-1 min-w-[12rem] overflow-hidden rounded-md border bg-surface p-1 shadow-md` +} + +/** + * Base styles for context menu items + */ +export const CONTEXT_MENU_ITEM_BASE_CLASS = + 'relative flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-xs outline-none transition-colors' + +/** + * Hover state styles for context menu items (standard CSS hover) + */ +export const CONTEXT_MENU_ITEM_HOVER_CLASS = 'hover:bg-surface-hover' + +/** + * Hover state styles for context menu items (Melt UI data attribute) + */ +export const CONTEXT_MENU_ITEM_HOVER_MELT_CLASS = 'data-[highlighted]:bg-surface-hover' + +/** + * Disabled state styles for context menu items + */ +export const CONTEXT_MENU_ITEM_DISABLED_CLASS = 'pointer-events-none opacity-50' + +/** + * Divider styles for context menu + */ +export const CONTEXT_MENU_DIVIDER_CLASS = 'my-1 h-px bg-border-light' + +/** + * Melt UI animation classes for context menu + */ +export const CONTEXT_MENU_ANIMATION_CLASSES = + 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2' + diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 316bb7a7a8..700925cc2c 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -64,6 +64,7 @@ import SelectionBoundingBox from './SelectionBoundingBox.svelte' import SelectionTool from './SelectionTool.svelte' import NodeContextMenu from './NodeContextMenu.svelte' + import PaneContextMenu from './PaneContextMenu.svelte' import { SelectionManager } from './selectionUtils.svelte' import { ChangeTracker } from '$lib/svelte5Utils.svelte' import { NoteManager } from './noteManager.svelte' @@ -235,6 +236,9 @@ // Runtime text height tracking for notes (not stored in FlowNote) let noteTextHeights = $state>({}) + // Reference to pane context menu component + let paneContextMenu: PaneContextMenu | undefined = $state(undefined) + // Selection manager - create one if not provided let selectionManager = selectionManagerProp || new SelectionManager() const selectedId = $derived(selectionManager.getSelectedId()) @@ -753,6 +757,7 @@ /> {/if} + { document.dispatchEvent(new Event('focus')) @@ -761,6 +766,9 @@ } noteManager.clearNoteSelection() }} + onpanecontextmenu={({ event }) => { + paneContextMenu?.onPaneContextMenu(event) + }} onnodedragstop={(event) => { const node = event.targetNode if (node && node.type === 'note') { diff --git a/frontend/src/lib/components/graph/PaneContextMenu.svelte b/frontend/src/lib/components/graph/PaneContextMenu.svelte new file mode 100644 index 0000000000..2bf0765134 --- /dev/null +++ b/frontend/src/lib/components/graph/PaneContextMenu.svelte @@ -0,0 +1,109 @@ + + +{#if contextMenuVisible} + + + + + +{/if}