diff --git a/web/src/lib/components/Shell.svelte b/web/src/lib/components/Shell.svelte index e32f8530..6d505911 100644 --- a/web/src/lib/components/Shell.svelte +++ b/web/src/lib/components/Shell.svelte @@ -9,6 +9,24 @@ let { children } = $props<{ children: import('svelte').Snippet }>(); let menuOpen = $state(false); + let menuRef: HTMLElement | undefined = $state(); + let menuButtonRef: HTMLElement | undefined = $state(); + + // Close the dropdown when the click was outside both the button and the menu. + // The previous implementation used onclick={(e) => e.stopPropagation()} on the + // menu container, which kept the window-level close-on-outside-click handler + // from firing — but it also blocked SvelteKit's document-level link-click + // interceptor, so clicking Admin/Settings inside the dropdown fell through + // to the browser's native navigation (a full-page reload). On a hard reload, + // /admin/+layout.ts's load function ran before bootstrap() settled the user + // store, so the admin guard saw user.value === null and redirected to /. + function handleWindowClick(e: MouseEvent) { + if (!menuOpen) return; + const target = e.target as Node | null; + if (!target) return; + if (menuRef?.contains(target) || menuButtonRef?.contains(target)) return; + menuOpen = false; + } async function handleLogout() { menuOpen = false; @@ -34,7 +52,7 @@ ]; - (menuOpen = false)} onkeydown={(e) => e.key === 'Escape' && (menuOpen = false)} /> + e.key === 'Escape' && (menuOpen = false)} />
@@ -44,17 +62,17 @@
{#if menuOpen}
e.stopPropagation()} - onkeydown={(e) => e.stopPropagation()} role="menu" tabindex="-1" >