Skip to content

Commit 927e1af

Browse files
committed
Start working on breadcrumbs feature
1 parent 55af2ee commit 927e1af

10 files changed

Lines changed: 230 additions & 0 deletions

src/lib/components/layout/Header.svelte

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
<script lang="ts">
22
import { PUBLIC_DISCORD_INVITE_URL, PUBLIC_GITHUB_PROJECT_URL } from '$env/static/public';
33
import LightSwitch from '$lib/components/LightSwitch.svelte';
4+
import {
5+
Breadcrumb,
6+
BreadcrumbList,
7+
BreadcrumbItem,
8+
BreadcrumbSeparator,
9+
BreadcrumbLink,
10+
BreadcrumbPage,
11+
} from '$lib/components/ui/breadcrumb';
412
import { Button } from '$lib/components/ui/button';
513
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
614
import { useSidebar } from '$lib/components/ui/sidebar';
15+
import { BreadCrumbStore } from '$lib/stores/BreadCrumbStore';
716
import { UserStore } from '$lib/stores/UserStore';
817
918
import PanelLeft from '@lucide/svelte/icons/panel-left';
@@ -27,6 +36,26 @@
2736
>
2837
<PanelLeft size={24} class="m-0 text-gray-600 dark:text-gray-300" />
2938
</Button>
39+
{#if $BreadCrumbStore.length > 0}
40+
<Breadcrumb>
41+
<BreadcrumbList>
42+
{#each $BreadCrumbStore as crumb, index}
43+
<BreadcrumbItem>
44+
{#if index < $BreadCrumbStore.length - 1}
45+
<BreadcrumbLink href={crumb.href} class="text-gray-600 dark:text-gray-300">
46+
{crumb.text}
47+
</BreadcrumbLink>
48+
<BreadcrumbSeparator class="text-gray-600 dark:text-gray-300" />
49+
{:else}
50+
<BreadcrumbPage class="text-gray-900 dark:text-gray-100">
51+
{crumb.text}
52+
</BreadcrumbPage>
53+
{/if}
54+
</BreadcrumbItem>
55+
{/each}
56+
</BreadcrumbList>
57+
</Breadcrumb>
58+
{/if}
3059
<div
3160
class={`flex flex-1 flex-row items-center justify-between space-x-2 py-2 ${$UserStore.self ? 'pr-2' : 'px-2'}`}
3261
>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script lang="ts">
2+
import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
3+
import type { HTMLAttributes } from "svelte/elements";
4+
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js";
5+
6+
let {
7+
ref = $bindable(null),
8+
class: className,
9+
...restProps
10+
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLSpanElement>>> = $props();
11+
</script>
12+
13+
<span
14+
bind:this={ref}
15+
data-slot="breadcrumb-ellipsis"
16+
role="presentation"
17+
aria-hidden="true"
18+
class={cn("flex size-9 items-center justify-center", className)}
19+
{...restProps}
20+
>
21+
<EllipsisIcon class="size-4" />
22+
<span class="sr-only">More</span>
23+
</span>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script lang="ts">
2+
import type { HTMLLiAttributes } from "svelte/elements";
3+
import { cn, type WithElementRef } from "$lib/utils.js";
4+
5+
let {
6+
ref = $bindable(null),
7+
class: className,
8+
children,
9+
...restProps
10+
}: WithElementRef<HTMLLiAttributes> = $props();
11+
</script>
12+
13+
<li
14+
bind:this={ref}
15+
data-slot="breadcrumb-item"
16+
class={cn("inline-flex items-center gap-1.5", className)}
17+
{...restProps}
18+
>
19+
{@render children?.()}
20+
</li>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script lang="ts">
2+
import type { HTMLAnchorAttributes } from "svelte/elements";
3+
import type { Snippet } from "svelte";
4+
import { cn, type WithElementRef } from "$lib/utils.js";
5+
6+
let {
7+
ref = $bindable(null),
8+
class: className,
9+
href = undefined,
10+
child,
11+
children,
12+
...restProps
13+
}: WithElementRef<HTMLAnchorAttributes> & {
14+
child?: Snippet<[{ props: HTMLAnchorAttributes }]>;
15+
} = $props();
16+
17+
const attrs = $derived({
18+
"data-slot": "breadcrumb-link",
19+
class: cn("hover:text-foreground transition-colors", className),
20+
href,
21+
...restProps,
22+
});
23+
</script>
24+
25+
{#if child}
26+
{@render child({ props: attrs })}
27+
{:else}
28+
<a bind:this={ref} {...attrs}>
29+
{@render children?.()}
30+
</a>
31+
{/if}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script lang="ts">
2+
import type { HTMLOlAttributes } from "svelte/elements";
3+
import { cn, type WithElementRef } from "$lib/utils.js";
4+
5+
let {
6+
ref = $bindable(null),
7+
class: className,
8+
children,
9+
...restProps
10+
}: WithElementRef<HTMLOlAttributes> = $props();
11+
</script>
12+
13+
<ol
14+
bind:this={ref}
15+
data-slot="breadcrumb-list"
16+
class={cn(
17+
"text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5",
18+
className
19+
)}
20+
{...restProps}
21+
>
22+
{@render children?.()}
23+
</ol>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script lang="ts">
2+
import type { HTMLAttributes } from "svelte/elements";
3+
import { cn, type WithElementRef } from "$lib/utils.js";
4+
5+
let {
6+
ref = $bindable(null),
7+
class: className,
8+
children,
9+
...restProps
10+
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
11+
</script>
12+
13+
<span
14+
bind:this={ref}
15+
data-slot="breadcrumb-page"
16+
role="link"
17+
aria-disabled="true"
18+
aria-current="page"
19+
class={cn("text-foreground font-normal", className)}
20+
{...restProps}
21+
>
22+
{@render children?.()}
23+
</span>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script lang="ts">
2+
import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
3+
import { cn, type WithElementRef } from "$lib/utils.js";
4+
import type { HTMLLiAttributes } from "svelte/elements";
5+
6+
let {
7+
ref = $bindable(null),
8+
class: className,
9+
children,
10+
...restProps
11+
}: WithElementRef<HTMLLiAttributes> = $props();
12+
</script>
13+
14+
<li
15+
bind:this={ref}
16+
data-slot="breadcrumb-separator"
17+
role="presentation"
18+
aria-hidden="true"
19+
class={cn("[&>svg]:size-3.5", className)}
20+
{...restProps}
21+
>
22+
{#if children}
23+
{@render children?.()}
24+
{:else}
25+
<ChevronRightIcon />
26+
{/if}
27+
</li>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts">
2+
import type { WithElementRef } from "$lib/utils.js";
3+
import type { HTMLAttributes } from "svelte/elements";
4+
5+
let {
6+
ref = $bindable(null),
7+
class: className,
8+
children,
9+
...restProps
10+
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
11+
</script>
12+
13+
<nav
14+
bind:this={ref}
15+
data-slot="breadcrumb"
16+
class={className}
17+
aria-label="breadcrumb"
18+
{...restProps}
19+
>
20+
{@render children?.()}
21+
</nav>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Root from "./breadcrumb.svelte";
2+
import Ellipsis from "./breadcrumb-ellipsis.svelte";
3+
import Item from "./breadcrumb-item.svelte";
4+
import Separator from "./breadcrumb-separator.svelte";
5+
import Link from "./breadcrumb-link.svelte";
6+
import List from "./breadcrumb-list.svelte";
7+
import Page from "./breadcrumb-page.svelte";
8+
9+
export {
10+
Root,
11+
Ellipsis,
12+
Item,
13+
Separator,
14+
Link,
15+
List,
16+
Page,
17+
//
18+
Root as Breadcrumb,
19+
Ellipsis as BreadcrumbEllipsis,
20+
Item as BreadcrumbItem,
21+
Separator as BreadcrumbSeparator,
22+
Link as BreadcrumbLink,
23+
List as BreadcrumbList,
24+
Page as BreadcrumbPage,
25+
};

src/lib/stores/BreadCrumbStore.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { writable } from 'svelte/store';
2+
3+
export interface BreadCrumbEntry {
4+
text: string;
5+
href?: string;
6+
};
7+
8+
export const BreadCrumbStore = writable<BreadCrumbEntry[]>([]);

0 commit comments

Comments
 (0)