Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
Ronni Skansing
2025-10-30 21:45:28 +01:00
parent 870e8761b5
commit 6ce80afcdd
2 changed files with 143 additions and 26 deletions
@@ -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}
>
<div
class="sticky top-0 bg-highlight-blue/20 dark:bg-gray-800/70 border-b w-full border-blue-700/30 dark:border-highlight-blue/40 transform-none transition-colors duration-200"
class={isPinned
? 'absolute right-2 top-2 flex items-center justify-between px-0'
: 'sticky top-0 bg-highlight-blue/20 dark:bg-gray-800/70 border-b w-full border-blue-700/30 dark:border-highlight-blue/40 transform-none transition-colors duration-200 flex items-center justify-between px-1'}
>
<button
class="w-full flex items-center justify-center rounded-md hover:bg-blue-600/30 dark:hover:bg-highlight-blue/20 transition-colors group px-3 py-2"
on:click={() => (isExpanded = !isExpanded)}
>
<svg
class="text-blue-100 dark:text-highlight-blue duration-200 w-6 transition-colors"
class:rotate-180={!isExpanded}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
{#if !isPinned}
<div class="flex items-center w-full">
<button
class="flex items-center justify-center rounded-md hover:bg-blue-600/30 dark:hover:bg-highlight-blue/20 transition-colors group px-3 py-2"
on:click={() => (isExpanded = !isExpanded)}
type="button"
>
<svg
class="text-blue-100 dark:text-highlight-blue duration-200 w-4 h-4 transition-colors"
class:rotate-180={!isExpanded}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"
/>
</svg>
</button>
{#if isExpanded}
<button
class="flex items-center justify-center rounded-md hover:bg-blue-600/30 dark:hover:bg-highlight-blue/20 transition-colors group px-3 py-2"
title={isPinned ? 'Unpin menu' : 'Pin menu'}
on:click={() => dispatch('pinToggle')}
type="button"
>
{#if isPinned}
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
class="w-4 h-4 text-blue-100 dark:text-highlight-blue"
>
<path
d="M16 9V5a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v4l-4 4v2h6v5a1 1 0 0 0 2 0v-5h6v-2l-4-4z"
/>
</svg>
{:else}
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="w-4 h-4 text-blue-100 dark:text-highlight-blue"
>
<path
d="M16 9V5a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v4l-4 4v2h6v5a1 1 0 0 0 2 0v-5h6v-2l-4-4z"
stroke="currentColor"
stroke-width="2"
/>
</svg>
{/if}
</button>
{/if}
</div>
{:else}
<button
class="flex items-center justify-center w-4 h-4 text-blue-100 dark:text-highlight-blue"
title="Unpin menu"
on:click={() => dispatch('pinToggle')}
type="button"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5"
/>
</svg>
</button>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="w-4 h-4 text-blue-100 dark:text-highlight-blue"
>
<path
d="M16 9V5a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v4l-4 4v2h6v5a1 1 0 0 0 2 0v-5h6v-2l-4-4z"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
{/if}
</div>
<!-- Navigation Items -->
<div
class="flex flex-col py-4 flex-1 overflow-y-auto {scrollBarClassesVertical} [&::-webkit-scrollbar-track]:bg-cta-blue dark:[&::-webkit-scrollbar-track]:bg-gray-800"
class="flex flex-col flex-1 overflow-y-auto {scrollBarClassesVertical} [&::-webkit-scrollbar-track]:bg-cta-blue dark:[&::-webkit-scrollbar-track]:bg-gray-800"
class:py-4={!isPinned}
>
{#each menu as link}
{#if link.type === 'submenu'}
+39 -3
View File
@@ -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');
}
}
</script>
<div class="flex flex-col min-w-[768px]">
@@ -220,9 +249,13 @@
{:else if loginStatus === AppStateService.LOGIN.LOGGED_IN && $page.route.id !== '/login'}
<Header bind:isProfileMenuVisible bind:isMobileMenuVisible {toggleChangeCompanyModal} />
{#if installState === AppStateService.INSTALL.INSTALLED}
<DesktopMenu />
<DesktopMenu
bind:this={desktopMenuRef}
isPinned={isMenuPinned}
on:pinToggle={handlePinToggle}
/>
{/if}
<div class="grid grid-cols-12 lg:ml-24 ml-8 mr-10 mt-4">
<div class={`grid grid-cols-12 mr-10 mt-4 ${isMenuPinned ? 'menu-pinned ml-52' : 'ml-24'}`}>
{#if installState === AppStateService.INSTALL.INSTALLED}
<MobileMenu
bind:visible={isMobileMenuVisible}
@@ -250,4 +283,7 @@
:global(table) {
user-select: text;
}
.menu-pinned {
grid-template-columns: 10rem 1fr !important;
}
</style>