veza/apps/web/src/components/navigation/Breadcrumbs.tsx
2025-12-12 21:34:34 -05:00

87 lines
2.6 KiB
TypeScript

import { Link } from 'react-router-dom';
import { ChevronRight, Home } from 'lucide-react';
import { cn } from '@/lib/utils';
export interface BreadcrumbItem {
label: string;
href?: string;
icon?: React.ReactNode;
}
export interface BreadcrumbsProps {
items: BreadcrumbItem[];
showHome?: boolean;
homeHref?: string;
separator?: React.ReactNode;
className?: string;
}
/**
* Composant Breadcrumbs pour navigation hiérarchique.
*/
export function Breadcrumbs({
items,
showHome = true,
homeHref = '/',
separator,
className,
}: BreadcrumbsProps) {
const allItems = showHome
? [
{ label: 'Home', href: homeHref, icon: <Home className="h-4 w-4" /> },
...items,
]
: items;
const defaultSeparator = separator || (
<ChevronRight className="h-4 w-4 text-muted-foreground" />
);
return (
<nav aria-label="Breadcrumb" className={cn('flex items-center', className)}>
<ol className="flex flex-wrap items-center gap-1 sm:gap-2">
{allItems.map((item, index) => {
const isLast = index === allItems.length - 1;
const isClickable = !isLast && item.href;
return (
<li key={index} className="flex items-center gap-1 sm:gap-2">
{isClickable ? (
<Link
to={item.href!}
className={cn(
'flex items-center gap-1 text-sm font-medium text-muted-foreground',
'transition-colors hover:text-foreground',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-sm',
)}
>
{item.icon && <span className="shrink-0">{item.icon}</span>}
<span className="truncate">{item.label}</span>
</Link>
) : (
<span
className={cn(
'flex items-center gap-1 text-sm font-medium',
isLast ? 'text-foreground' : 'text-muted-foreground',
)}
aria-current={isLast ? 'page' : undefined}
>
{item.icon && <span className="shrink-0">{item.icon}</span>}
<span className="truncate">{item.label}</span>
</span>
)}
{!isLast && (
<span
className="mx-1 sm:mx-2 text-muted-foreground shrink-0"
aria-hidden="true"
>
{defaultSeparator}
</span>
)}
</li>
);
})}
</ol>
</nav>
);
}