This repository has no description
1<script module lang="ts">
2 import type { Component } from "svelte";
3 import type { SvelteHTMLElements } from "svelte/elements";
4
5 export interface TabDef {
6 id: string;
7 label: string;
8 href: string;
9 icon?: Component<SvelteHTMLElements["svg"]>;
10 count?: number;
11 }
12</script>
13
14<script lang="ts">
15 import { resolve } from "$app/paths";
16
17 interface Props {
18 tabs: TabDef[];
19 active: string;
20 label?: string;
21 vertical?: boolean;
22 overlapBottom?: boolean;
23 }
24
25 let { tabs, active, label, vertical = false, overlapBottom = false }: Props = $props();
26
27 const navClass = $derived(
28 vertical
29 ? "h-fit divide-y divide-border-default overflow-hidden rounded border border-border-default"
30 : "relative z-10 flex w-full overflow-x-auto overflow-y-hidden pl-4"
31 );
32
33 const itemClass = (isActive: boolean) =>
34 vertical
35 ? `flex items-center gap-3 px-3 py-2 text-sm no-underline hover:no-underline ${
36 isActive
37 ? "bg-background-default text-foreground-default dark:bg-background-inset"
38 : "bg-background-inset text-foreground-muted hover:text-foreground-default dark:bg-background-default"
39 }`
40 : `relative mr-1 flex items-center rounded-t px-4 pt-1 pb-[5px] whitespace-nowrap text-foreground-default no-underline hover:no-underline ${
41 isActive
42 ? "border border-b-0 border-border-default bg-background-default [-webkit-text-stroke:0.3px_currentColor]"
43 : "border border-b-0 border-transparent hover:bg-background-muted hover:dark:bg-background-subtle/50"
44 }`;
45</script>
46
47<nav class={[navClass, overlapBottom && "-mb-px"]} aria-label={label}>
48 {#each tabs as tab (tab.id)}
49 {@const isActive = active === tab.id}
50 {@const Glyph = tab.icon}
51 <a
52 href={tab.href.startsWith("/") ? resolve(tab.href as "/") : tab.href}
53 aria-current={isActive ? "page" : undefined}
54 class={itemClass(isActive)}
55 >
56 {#if Glyph}
57 <Glyph class={vertical ? "size-4 shrink-0" : "mr-2 size-4 "} aria-hidden="true" />
58 {/if}
59 {#if vertical}
60 {tab.label}
61 {:else}
62 <span class="flex flex-col">
63 {tab.label}
64 <span aria-hidden="true" class="invisible h-0 overflow-hidden font-medium select-none"
65 >{tab.label}</span
66 >
67 </span>
68 {/if}
69 {#if tab.count}
70 <span class="rounded-sm bg-background-inset px-1 text-sm {vertical ? 'ml-auto' : 'ml-1'}"
71 >{tab.count}</span
72 >
73 {/if}
74 </a>
75 {/each}
76</nav>