refactor: Desktop Icon Strip DRY
This commit is contained in:
parent
6ec8aa9c6e
commit
c12c0b5cfe
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -18,7 +18,7 @@
|
|||
<div style="display: contents">
|
||||
<script>
|
||||
{
|
||||
__sveltekit_lswftp = {
|
||||
__sveltekit_1irt4nm = {
|
||||
base: new URL('.', location).pathname.slice(0, -1)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
import { ActionIcon } from '$lib/components/app/actions';
|
||||
import { McpLogo } from '$lib/components/app/mcp';
|
||||
import { Database, Settings, Search, SquarePen } from '@lucide/svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { onMount, type Component } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
sidebarOpen: boolean;
|
||||
|
|
@ -12,9 +14,62 @@
|
|||
|
||||
let { sidebarOpen, onSearchClick }: Props = $props();
|
||||
|
||||
const TRANSITION_DURATION = 200;
|
||||
|
||||
let isMcpActive = $derived(page.route.id === '/settings/mcp');
|
||||
let isImportExportActive = $derived(page.route.id === '/settings/import-export');
|
||||
let isSettingsActive = $derived(page.route.id === '/settings/chat');
|
||||
|
||||
interface IconItem {
|
||||
icon: Component;
|
||||
tooltip: string;
|
||||
onclick: () => void;
|
||||
activeClass?: string;
|
||||
group: 'top' | 'bottom';
|
||||
}
|
||||
|
||||
let icons = $derived<IconItem[]>([
|
||||
{
|
||||
icon: SquarePen,
|
||||
tooltip: 'New Chat',
|
||||
onclick: () => goto('?new_chat=true#/'),
|
||||
group: 'top'
|
||||
},
|
||||
{
|
||||
icon: Search,
|
||||
tooltip: 'Search',
|
||||
onclick: onSearchClick,
|
||||
group: 'top'
|
||||
},
|
||||
{
|
||||
icon: McpLogo,
|
||||
tooltip: 'MCP Servers',
|
||||
onclick: () => goto('#/settings/mcp'),
|
||||
activeClass: isMcpActive ? 'bg-accent text-accent-foreground' : '',
|
||||
group: 'bottom'
|
||||
},
|
||||
{
|
||||
icon: Database,
|
||||
tooltip: 'Import / Export',
|
||||
onclick: () => goto('#/settings/import-export'),
|
||||
activeClass: isImportExportActive ? 'bg-accent text-accent-foreground' : '',
|
||||
group: 'bottom'
|
||||
},
|
||||
{
|
||||
icon: Settings,
|
||||
tooltip: 'Settings',
|
||||
onclick: () => goto('#/settings/chat'),
|
||||
activeClass: isSettingsActive ? 'bg-accent text-accent-foreground' : '',
|
||||
group: 'bottom'
|
||||
}
|
||||
]);
|
||||
|
||||
let topIcons = $derived(icons.filter((i) => i.group === 'top'));
|
||||
let bottomIcons = $derived(icons.filter((i) => i.group === 'bottom'));
|
||||
|
||||
let mounted = $state(false);
|
||||
onMount(() => (mounted = true));
|
||||
let showIcons = $derived(mounted && !sidebarOpen);
|
||||
</script>
|
||||
|
||||
<!-- Spacer to reserve space for icon strip in the flex layout -->
|
||||
|
|
@ -30,54 +85,35 @@
|
|||
: 'opacity-100'}"
|
||||
>
|
||||
<div class="mt-12 flex flex-col items-center gap-1">
|
||||
<ActionIcon
|
||||
icon={SquarePen}
|
||||
tooltip="New Chat"
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent!"
|
||||
onclick={() => goto('?new_chat=true#/')}
|
||||
/>
|
||||
|
||||
<ActionIcon
|
||||
icon={Search}
|
||||
tooltip="Search"
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent!"
|
||||
onclick={onSearchClick}
|
||||
/>
|
||||
{#each topIcons as item (item.tooltip)}
|
||||
{#if showIcons}
|
||||
<div in:fade={{ duration: TRANSITION_DURATION, delay: TRANSITION_DURATION }}>
|
||||
<ActionIcon
|
||||
icon={item.icon}
|
||||
tooltip={item.tooltip}
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent! {item.activeClass ?? ''}"
|
||||
onclick={item.onclick}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<ActionIcon
|
||||
icon={McpLogo}
|
||||
tooltip="MCP Servers"
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent! {isMcpActive
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''}"
|
||||
onclick={() => goto('#/settings/mcp')}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={Database}
|
||||
tooltip="Import / Export"
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent! {isImportExportActive
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''}"
|
||||
onclick={() => goto('#/settings/import-export')}
|
||||
/>
|
||||
<ActionIcon
|
||||
icon={Settings}
|
||||
tooltip="Settings"
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent! {isSettingsActive
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''}"
|
||||
onclick={() => goto('#/settings/chat')}
|
||||
/>
|
||||
{#each bottomIcons as item (item.tooltip)}
|
||||
{#if showIcons}
|
||||
<div in:fade={{ duration: TRANSITION_DURATION, delay: TRANSITION_DURATION }}>
|
||||
<ActionIcon
|
||||
icon={item.icon}
|
||||
tooltip={item.tooltip}
|
||||
size="lg"
|
||||
iconSize="h-4 w-4"
|
||||
class="h-9 w-9 rounded-full hover:bg-accent! {item.activeClass ?? ''}"
|
||||
onclick={item.onclick}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
import { browser } from '$app/environment';
|
||||
import { page } from '$app/state';
|
||||
import { untrack } from 'svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import {
|
||||
ChatSidebar,
|
||||
ChatSettings,
|
||||
|
|
@ -32,6 +34,7 @@
|
|||
let isMobile = new IsMobile();
|
||||
let isDesktop = $derived(!isMobile.current);
|
||||
let sidebarOpen = $state(false);
|
||||
let mounted = $state(false);
|
||||
let innerHeight = $state<number | undefined>();
|
||||
let chatSidebar:
|
||||
| { activateSearchMode?: () => void; editActiveConversation?: () => void }
|
||||
|
|
@ -108,6 +111,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
mounted = true;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (alwaysShowSidebarOnDesktop && isDesktop) {
|
||||
sidebarOpen = true;
|
||||
|
|
@ -241,12 +248,16 @@
|
|||
</Sidebar.Root>
|
||||
|
||||
{#if !(alwaysShowSidebarOnDesktop && isDesktop) && !(isSettingsRoute && !isDesktop)}
|
||||
<Sidebar.Trigger
|
||||
class="transition-left absolute left-0 z-[900] duration-200 ease-linear {sidebarOpen
|
||||
? 'left-[calc(var(--sidebar-width)+0.75rem)] max-md:hidden'
|
||||
: 'left-0!'}"
|
||||
style="translate: 1rem 1rem;"
|
||||
/>
|
||||
{#if mounted}
|
||||
<div in:fade={{ duration: 200 }}>
|
||||
<Sidebar.Trigger
|
||||
class="transition-left absolute left-0 z-[900] duration-200 ease-linear {sidebarOpen
|
||||
? 'left-[calc(var(--sidebar-width)+0.75rem)] max-md:hidden'
|
||||
: 'left-0!'}"
|
||||
style="translate: 1rem 1rem;"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if isDesktop && !alwaysShowSidebarOnDesktop}
|
||||
|
|
|
|||
Loading…
Reference in New Issue