diff --git a/frontend/src/lib/components/sidebar/OperatorMenu.svelte b/frontend/src/lib/components/sidebar/OperatorMenu.svelte index 63f7773d8a..8f2688898d 100644 --- a/frontend/src/lib/components/sidebar/OperatorMenu.svelte +++ b/frontend/src/lib/components/sidebar/OperatorMenu.svelte @@ -34,7 +34,7 @@ import { Menu, Menubar, MenuItem } from '$lib/components/meltComponents' import MenuButton, { sidebarClasses } from './MenuButton.svelte' import MenuLink from './MenuLink.svelte' - import { onDestroy } from 'svelte' + import ResizeTransitionWrapper from '../common/ResizeTransitionWrapper.svelte' let darkMode: boolean = $state(false) interface Props { @@ -78,7 +78,13 @@ ) ) - let secondMenuLinks = $derived( + type SecondMenuLink = { label: string; id: string; href: string } + function filterLink(link: SecondMenuLink) { + if (!$userWorkspaces || !$workspaceStore) return false + let userWorkspace = $userWorkspaces.find((_) => _.id === $workspaceStore) + return userWorkspace?.operator_settings?.[link.id] === true + } + let secondMenuLinks: SecondMenuLink[] = $derived( [ { label: 'Resources', @@ -95,6 +101,25 @@ id: 'assets', href: `${base}/assets` }, + { + label: 'Groups', + id: 'groups', + href: `${base}/groups` + }, + { + label: 'Folders', + id: 'folders', + href: `${base}/folders` + }, + { + label: 'Workers', + id: 'workers', + href: `${base}/workers` + } + ].filter(filterLink) + ) + let secondMenuTriggerLinks = $derived( + [ { label: 'Custom HTTP routes', id: 'triggers', @@ -144,52 +169,15 @@ label: 'Audit logs', id: 'audit_logs', href: `${base}/audit_logs` - }, - { - label: 'Groups', - id: 'groups', - href: `${base}/groups` - }, - { - label: 'Folders', - id: 'folders', - href: `${base}/folders` - }, - { - label: 'Workers', - id: 'workers', - href: `${base}/workers` } - ].filter((link) => { - if (!$userWorkspaces || !$workspaceStore) return false - return ( - $userWorkspaces.find((_) => _.id === $workspaceStore)?.operator_settings?.[link.id] === true - ) - }) + ].filter(filterLink) ) - - let moreOpen = $state(false) - let moreOpenTimeout: number | undefined = $state() - - function debouncedSetMoreOpen(value: boolean) { - if (moreOpenTimeout) { - clearTimeout(moreOpenTimeout) - } - moreOpenTimeout = setTimeout(() => { - moreOpen = value - }, 150) // 150ms debounce - } - - onDestroy(() => { - if (moreOpenTimeout) { - clearTimeout(moreOpenTimeout) - } - }) + let showMore = $state(false) {#snippet children({ createMenu })} - + (showMore = false)}> {#snippet triggr({ trigger })} logout()} class={twMerge( 'flex flex-row gap-3.5 items-center px-2 py-2 w-full', - 'text-secondary text-xs', - 'hover:bg-surface-hover hover:text-primary cursor-pointer', + 'text-primary text-xs', + 'hover:bg-surface-hover cursor-pointer', 'data-[highlighted]:bg-surface-hover data-[highlighted]:text-primary' )} {item} @@ -315,57 +303,42 @@ Sign out -
debouncedSetMoreOpen(true)} - onmouseleave={() => debouncedSetMoreOpen(false)} - role="none" - > - debouncedSetMoreOpen(true)} - onFocusOut={() => debouncedSetMoreOpen(false)} - {item} - > - {#if !moreOpen || secondMenuLinks.length === 0} -
More...
- {/if} -
- {#if moreOpen && secondMenuLinks.length > 0} - {#each secondMenuLinks as menuLink (menuLink.href ?? menuLink.label)} -
- debouncedSetMoreOpen(true)} - onFocusOut={() => debouncedSetMoreOpen(false)} - > - {menuLink.label} - -
- {/each} +
(showMore = false)} role="none"> + {#if secondMenuLinks.length} + + {#if !showMore} +
(showMore = true)} role="none"> + +
More...
+
+
+ {:else} + {#snippet renderSecondMenuLinks(menuLinks: SecondMenuLink[])} + {#each menuLinks as menuLink (menuLink.href ?? menuLink.label)} + + {menuLink.label} + + {/each} + {/snippet} +
+
{@render renderSecondMenuLinks(secondMenuLinks)}
+
{@render renderSecondMenuLinks(secondMenuTriggerLinks)}
+
+ {/if} +
+ {/if} + {#if $enterpriseLicense} + {/if}
- {#if $enterpriseLicense} -
{ - if (moreOpenTimeout) { - setTimeout(() => { - clearTimeout(moreOpenTimeout) - }, 15) - } - }} - onmouseleave={() => { - debouncedSetMoreOpen(false) - }} - role="none" - > - -
- {/if} {/snippet}
{/snippet}