Type-checks every link against the real route tree. Caught a dead one: the
book page linked tags to /tag/{id}, a route that has never existed.
62 lines
2.3 KiB
Svelte
62 lines
2.3 KiB
Svelte
<script lang="ts">
|
|
import { resolve } from '$app/paths';
|
|
import { Separator } from '$lib/components/ui/separator/index.js';
|
|
|
|
import ChitaiMark from '$lib/components/icons/chitai-mark.svelte';
|
|
|
|
import NavMain from './nav-main.svelte';
|
|
import NavUser from './nav-user.svelte';
|
|
import LibrarySwitcher from './library-switcher.svelte';
|
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
|
import type { ComponentProps } from 'svelte';
|
|
import { getLibraryState } from '$lib/state/library.svelte';
|
|
|
|
let {
|
|
ref = $bindable(null),
|
|
collapsible = 'icon',
|
|
user,
|
|
...restProps
|
|
}: ComponentProps<typeof Sidebar.Root> & { user: { email: string } } = $props();
|
|
|
|
const libraryState = getLibraryState();
|
|
|
|
// Keep components out of these objects. `<header.icon />` is a *dynamic*
|
|
// component: every time the $derived object is recomputed — on hydration
|
|
// when LibraryState picks up previousLibraryId, and on every navigation for
|
|
// the footer — Svelte re-evaluates the expression and can remount the SVG,
|
|
// which reads as a flash and shifts layout. Referencing the component
|
|
// directly in the markup keeps it static.
|
|
const header = $derived({
|
|
title: 'chitai',
|
|
url: resolve('/(root)/(library)/library/[libraryId]', { libraryId: String(libraryState.activeLibrary!.id) })
|
|
});
|
|
</script>
|
|
|
|
<Sidebar.Root variant="floating" {collapsible} {...restProps} class="ml-1 py-3">
|
|
<Sidebar.Header class="h-(--header-height)">
|
|
<!--
|
|
The mark is 28px where the nav icons are 16px, and both start at the same
|
|
8px padding — so its centre sits 6px right of theirs. -ml-1.5 is that 6px,
|
|
which lines the logo up with the nav in both expanded and collapsed states.
|
|
-->
|
|
<Sidebar.MenuButton class="mt-1 -ml-1.5">
|
|
{#snippet child({ props })}
|
|
<!-- header.url is built with resolve(); the rule cannot trace the variable. -->
|
|
<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
|
|
<a href={header.url} {...props}>
|
|
<ChitaiMark class="mr-3 size-7!" />
|
|
<span class="font-serif text-xl tracking-tight">{header.title}</span>
|
|
</a>
|
|
{/snippet}
|
|
</Sidebar.MenuButton>
|
|
</Sidebar.Header>
|
|
<Separator />
|
|
<Sidebar.Content class="mt-4 mr-2 overflow-x-hidden">
|
|
<LibrarySwitcher />
|
|
<NavMain />
|
|
</Sidebar.Content>
|
|
<Sidebar.Footer>
|
|
<NavUser {user} />
|
|
</Sidebar.Footer>
|
|
</Sidebar.Root>
|