Skip to content

Commit b9af90b

Browse files
committed
feat: add breadcrumbs navigation
1 parent f9f8208 commit b9af90b

File tree

6 files changed

+47
-2
lines changed

6 files changed

+47
-2
lines changed

.changeset/fifty-seas-thank.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@quassel/frontend": patch
3+
"@quassel/ui": patch
4+
---
5+
6+
Add export ui

.changeset/many-snails-try.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@quassel/frontend": patch
3+
"@quassel/ui": patch
4+
---
5+
6+
Add content shell
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { isMatch, Link, useLocation, useMatches } from "@tanstack/react-router";
2+
import { Anchor, Breadcrumbs } from "@quassel/ui";
3+
4+
// Inspired by https://github.com/TanStack/router/blob/main/examples/react/kitchen-sink-file-based/src/components/Breadcrumbs.tsx
5+
export function BreadcrumbsNavigation() {
6+
const matches = useMatches();
7+
const location = useLocation();
8+
9+
if (matches.some((match) => match.status === "pending")) return null;
10+
11+
const matchesWithTitle = matches.filter((match) => isMatch(match, "context.title"));
12+
const entries = matchesWithTitle.map((match) => ({ label: match.context.title, to: match.fullPath }));
13+
const uniqueEntries = entries.filter((entry, index) => entries.findIndex((e) => e.label === entry.label) === index);
14+
15+
// Remove the last entry if it's the current page
16+
if (uniqueEntries.length > 0 && uniqueEntries[uniqueEntries.length - 1].to.startsWith(location.pathname)) {
17+
uniqueEntries.pop();
18+
}
19+
20+
return (
21+
<Breadcrumbs>
22+
{uniqueEntries.map((e) => (
23+
<Anchor key={e.label} renderRoot={(props) => <Link to={e.to} {...props} />}>
24+
{e.label}
25+
</Anchor>
26+
))}
27+
</Breadcrumbs>
28+
);
29+
}

apps/frontend/src/routes/_auth/administration.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useEffect } from "react";
33
import { $layout } from "../../stores/layout";
44
import { i18n } from "../../stores/i18n";
55
import { ContentShell } from "@quassel/ui";
6+
import { BreadcrumbsNavigation } from "../../components/Breadcrumbs";
67

78
const messages = i18n("AdministrationRoute", {
89
title: "Administration",
@@ -19,7 +20,7 @@ function AdministrationLayout() {
1920
const actions = matches[matches.length - 1]?.context.actions;
2021

2122
return (
22-
<ContentShell title={title || messages.get().title} actions={actions}>
23+
<ContentShell title={title || messages.get().title} breadcrumbs={<BreadcrumbsNavigation />} actions={actions}>
2324
<Outlet />
2425
</ContentShell>
2526
);

libs/ui/src/components/ContentShell.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { PropsWithChildren, ReactElement } from "react";
44
type Props = PropsWithChildren<{
55
title: string;
66
actions?: ReactElement[];
7+
breadcrumbs?: ReactElement;
78
}>;
89

9-
export function ContentShell({ title, actions, children }: Props) {
10+
export function ContentShell({ title, breadcrumbs, actions, children }: Props) {
1011
return (
1112
<>
13+
{breadcrumbs}
1214
<Flex justify="space-between">
1315
<Title>{title}</Title>
1416
<div>{actions}</div>

libs/ui/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export {
5252
ActionIcon,
5353
Anchor,
5454
AppShell,
55+
Breadcrumbs,
5556
Button,
5657
Checkbox,
5758
Combobox,

0 commit comments

Comments
 (0)