mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
git-subtree-dir: apps/web git-subtree-mainline:eaa6d2276agit-subtree-split:459af9d46a
117 lines
4.6 KiB
React
117 lines
4.6 KiB
React
import { useNavigate } from "react-router-dom";
|
|
import {
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
BreadcrumbLink,
|
|
BreadcrumbList,
|
|
BreadcrumbPage,
|
|
BreadcrumbSeparator,
|
|
} from "@/components/ui/breadcrumb";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
/**
|
|
* 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
|
|
* @param {Object} [color] Independent color overrides per element
|
|
* @param {Object} [color.link] Override for non-last (clickable) items
|
|
* @param {string} [color.link.color] className applied to BreadcrumbLink
|
|
* @param {Object} [color.page] Override for the last (current page) item
|
|
* @param {string} [color.page.color] className applied to BreadcrumbPage
|
|
*
|
|
* ─── 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("/admin") },
|
|
* { label: "Settings", to: `/admin/${adminId}/settings` },
|
|
* { label: "Profile" },
|
|
* ]}
|
|
* />
|
|
*
|
|
* // With per-element color overrides
|
|
* <AppBreadcrumb
|
|
* color={{ link: { color: "text-muted-foreground" }, page: { color: "text-[#000000]" } }}
|
|
* items={items}
|
|
* />
|
|
*
|
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
*/
|
|
const AppBreadcrumb = ({ items = [], color = {} }) => {
|
|
const navigate = useNavigate();
|
|
const linkColor = color.link?.color ?? "";
|
|
const pageColor = color.page?.color ?? "";
|
|
|
|
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 ? (
|
|
<BreadcrumbPage className={cn("flex items-center gap-2 max-w-[300px] truncate", pageColor)}>
|
|
{item.icon}
|
|
<span className="hidden lg:inline truncate">{item.label}</span>
|
|
<span className="lg:hidden">...</span>
|
|
</BreadcrumbPage>
|
|
) : (
|
|
<BreadcrumbLink asChild>
|
|
<div
|
|
className={cn("flex items-center gap-2 select-none cursor-pointer max-w-[300px]", linkColor)}
|
|
onClick={(e) => {
|
|
if (item.onClick) {
|
|
item.onClick(e, navigate);
|
|
} else if (item.to) {
|
|
e.preventDefault();
|
|
navigate(item.to);
|
|
}
|
|
}}
|
|
>
|
|
{item.icon}
|
|
<span className="truncate">{item.label}</span>
|
|
</div>
|
|
</BreadcrumbLink>
|
|
)}
|
|
</BreadcrumbItem>
|
|
|
|
{!isLast && <BreadcrumbSeparator />}
|
|
</span>
|
|
);
|
|
})}
|
|
</BreadcrumbList>
|
|
</Breadcrumb>
|
|
);
|
|
};
|
|
|
|
export default AppBreadcrumb; |