From 6ce80afcdd98932aceb6140b6e364a887eb4f9f3 Mon Sep 17 00:00:00 2001 From: Ronni Skansing Date: Thu, 30 Oct 2025 21:29:04 +0100 Subject: [PATCH] pin menu Signed-off-by: Ronni Skansing --- .../lib/components/header/DesktopMenu.svelte | 127 ++++++++++++++---- frontend/src/routes/+layout.svelte | 42 +++++- 2 files changed, 143 insertions(+), 26 deletions(-) diff --git a/frontend/src/lib/components/header/DesktopMenu.svelte b/frontend/src/lib/components/header/DesktopMenu.svelte index 8397dd1..b3c351a 100644 --- a/frontend/src/lib/components/header/DesktopMenu.svelte +++ b/frontend/src/lib/components/header/DesktopMenu.svelte @@ -7,7 +7,16 @@ import { onMount } from 'svelte'; import { beforeNavigate } from '$app/navigation'; + export let isPinned = false; + let isExpanded = false; + + // expose collapseMenu for parent to collapse menu on unpin + export function collapseMenu() { + isExpanded = false; + } + + $: isExpanded = isPinned ? true : isExpanded; let menuElement; let instantCollapse = false; let context = { @@ -16,6 +25,8 @@ }; const appState = AppStateService.instance; + import { createEventDispatcher } from 'svelte'; + const dispatch = createEventDispatcher(); onMount(() => { const unsub = appState.subscribe((s) => { @@ -27,7 +38,7 @@ // handle click outside to collapse menu const handleClickOutside = (event) => { - if (isExpanded && menuElement && !menuElement.contains(event.target)) { + if (!isPinned && isExpanded && menuElement && !menuElement.contains(event.target)) { isExpanded = false; } }; @@ -42,7 +53,7 @@ // handle navigation to collapse menu beforeNavigate(() => { - if (isExpanded) { + if (!isPinned && isExpanded) { instantCollapse = true; isExpanded = false; // reset after a brief moment @@ -156,33 +167,103 @@ class:!max-h-[calc(100vh-6rem)]={hasCompanySelected} >
-
+ {:else} + + + + + + {/if}
{#each menu as link} {#if link.type === 'submenu'} diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index fa4983b..761d8ec 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -18,7 +18,6 @@ import DesktopMenu from '$lib/components/header/DesktopMenu.svelte'; import { hideIsLoading, showIsLoading } from '$lib/store/loading'; import Header from '$lib/components/header/Header.svelte'; - import CompanyBanner from '$lib/components/header/CompanyBanner.svelte'; import { setupTheme, setupOSThemeListener } from '$lib/theme.js'; // Removed feature flags import - no longer needed @@ -42,6 +41,20 @@ let isCommandPaletteVisible = false; let isReady = false; + // pin state for menu + let isMenuPinned = false; + + // cookie helpers + function getCookie(name) { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) return parts.pop().split(';').shift(); + } + function setCookie(name, value, days = 365) { + const expires = new Date(Date.now() + days * 864e5).toUTCString(); + document.cookie = `${name}=${value}; expires=${expires}; path=/`; + } + beforeNavigate((beforeNavigate) => { if (!beforeNavigate.to) { return; @@ -58,6 +71,9 @@ setupTheme(); setupOSThemeListener(); + const pinned = getCookie('menuPinned'); + isMenuPinned = pinned === 'true'; + (async () => { // handle session // if the user already has a active session, then the session will be @@ -203,6 +219,19 @@ const toggleChangeCompanyModal = async () => { isChangeCompanyModalVisible = !isChangeCompanyModalVisible; }; + + // pin toggle handler + let desktopMenuRef; + function handlePinToggle() { + if (isMenuPinned) { + isMenuPinned = false; + setCookie('menuPinned', 'false'); + desktopMenuRef?.collapseMenu(); + } else { + isMenuPinned = true; + setCookie('menuPinned', 'true'); + } + }
@@ -220,9 +249,13 @@ {:else if loginStatus === AppStateService.LOGIN.LOGGED_IN && $page.route.id !== '/login'}
{#if installState === AppStateService.INSTALL.INSTALLED} - + {/if} -
+
{#if installState === AppStateService.INSTALL.INSTALLED}