Files
starr-philproperties/src/components/generic/Breadcrumb/AppBreadcrumb.jsx
T
2026-05-06 14:14:54 +08:00

104 lines
3.8 KiB
React

import { useNavigate } from "react-router-dom";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
/**
* AppBreadcrumb
*
* Generic breadcrumb driven entirely by the `items` prop.
* The last item is always rendered as the current page (non-clickable).
* All preceding items are rendered as clickable links.
*
* ─── Item definition shape ────────────────────────────────────────────────────
*
* @field {string} label Display text
* @field {ReactNode} [icon] Optional icon rendered before the label
* @field {string} [to] navigate() path — if omitted, item is non-navigable
* @field {Function} [onClick] Custom click handler (e, navigate) => void
* Overrides `to` when provided.
*
* ─────────────────────────────────────────────────────────────────────────────
*
* @param {Array} items Ordered list of breadcrumb item definitions
*
* ─── Usage ───────────────────────────────────────────────────────────────────
*
* import { House, Users } from "lucide-react";
* import AppBreadcrumb from "@/components/generic/AppBreadcrumb";
*
* // Basic — just paths
* <AppBreadcrumb
* items={[
* { label: "Home", icon: <House className="size-4" />, to: `/admin/${adminId}/users` },
* { label: "Users" },
* ]}
* />
*
* // With custom click handler
* <AppBreadcrumb
* items={[
* { label: "Home", icon: <House className="size-4" />, onClick: (e, navigate) => navigate(-1) },
* { label: "Settings", to: `/admin/${adminId}/settings` },
* { label: "Profile" },
* ]}
* />
*
* ─────────────────────────────────────────────────────────────────────────────
*/
const AppBreadcrumb = ({ items = [] }) => {
const navigate = useNavigate();
if (!items.length) return null;
return (
<Breadcrumb>
<BreadcrumbList>
{items.map((item, index) => {
const isLast = index === items.length - 1;
return (
<span key={index} className="flex items-center gap-1.5">
<BreadcrumbItem>
{isLast ? (
// Current page — no interaction
<BreadcrumbPage className="flex items-center gap-2">
{item.icon}
{item.label}
</BreadcrumbPage>
) : (
// Clickable link
<BreadcrumbLink asChild>
<div
className="flex items-center gap-2 select-none cursor-pointer"
onClick={(e) => {
if (item.onClick) {
item.onClick(e, navigate);
} else if (item.to) {
e.preventDefault();
navigate(item.to);
}
}}
>
{item.icon}
<span>{item.label}</span>
</div>
</BreadcrumbLink>
)}
</BreadcrumbItem>
{!isLast && <BreadcrumbSeparator />}
</span>
);
})}
</BreadcrumbList>
</Breadcrumb>
);
};
export default AppBreadcrumb;