subul / frontend /app /components /breadcrumbs.tsx
AshameTheDestroyer's picture
Breadcrumbs.
5f4ae90
import { cn } from "~/lib/utils";
import { HomeIcon } from "lucide-react";
export function Breadcrumbs() {
const pathname =
typeof window !== "undefined" ? window.location.pathname : "/";
const segments = pathname.split("/").filter(Boolean);
const crumbs = [
{ name: "/", href: "/", isEllipsis: false },
...segments.map((seg, i) => {
const href = "/" + segments.slice(0, i + 1).join("/");
const name = decodeURIComponent(seg)
.replace(/-/g, " ")
.replace(/\b\w/g, (c) => c.toUpperCase());
return { name, href, isEllipsis: false };
}),
];
const displayCrumbs =
crumbs.length > 4
? [
crumbs[0],
{ name: "...", href: null, isEllipsis: true },
crumbs[crumbs.length - 2],
crumbs[crumbs.length - 1],
]
: crumbs;
return (
<nav aria-label="Breadcrumb" className="flex items-center">
<ol className="flex items-center gap-2 text-sm">
{displayCrumbs.map((c, i, array) => (
<li
key={c.href ?? `ellipsis-${i}`}
className="flex items-center gap-2"
>
{c.isEllipsis ? (
<span className="text-muted-foreground">...</span>
) : (
<a
href={i == 0 || i == array.length -1 ? undefined : c.href!}
className={cn(
"text-ellipsis overflow-hidden whitespace-nowrap max-w-36",
i > 0 && i < array.length - 1
? "font-medium hover:underline"
: "text-muted-foreground"
)}
aria-current={
i > 0 && i < array.length - 1
? "page"
: undefined
}
>
{c.name == "/" ? <HomeIcon className="size-4" /> : c.name}
</a>
)}
{i < displayCrumbs.length - 1 && (
<span className="text-muted-foreground">/</span>
)}
</li>
))}
</ol>
</nav>
);
}