76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { NavLink } from 'react-router-dom';
|
|
import { type LucideIcon } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
|
|
export interface NavItem {
|
|
icon: LucideIcon;
|
|
to: string;
|
|
end?: boolean;
|
|
label: string;
|
|
}
|
|
|
|
interface NavPillProps {
|
|
item: NavItem;
|
|
onNavigate?: () => void;
|
|
badge?: number;
|
|
badgeNames?: string[];
|
|
}
|
|
|
|
export const NavPill = React.memo(function NavPill({
|
|
item,
|
|
onNavigate,
|
|
badge = 0,
|
|
badgeNames = [],
|
|
}: NavPillProps) {
|
|
const Icon = useMemo(() => item.icon, [item.icon]);
|
|
const to = useMemo(() => item.to, [item.to]);
|
|
const end = useMemo(() => item.end, [item.end]);
|
|
const label = useMemo(() => item.label, [item.label]);
|
|
|
|
return (
|
|
<NavLink
|
|
to={to}
|
|
end={end}
|
|
onClick={onNavigate}
|
|
className={({ isActive }) =>
|
|
cn(
|
|
'inline-flex items-center gap-2 rounded-full border px-3 py-2 text-sm font-medium transition-all',
|
|
'focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50',
|
|
isActive
|
|
? 'border-primary/25 bg-primary text-primary-foreground shadow-sm shadow-primary/20'
|
|
: 'border-transparent text-muted-foreground hover:border-border/60 hover:bg-accent/85 hover:text-accent-foreground hover:shadow-sm',
|
|
)
|
|
}
|
|
>
|
|
<Icon className="h-4 w-4" />
|
|
<span>{label}</span>
|
|
{badge > 0 && (
|
|
<TooltipProvider delayDuration={300}>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<span className="ml-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-rose-500 px-1 text-[10px] font-bold leading-none text-white">
|
|
{badge > 99 ? '99+' : badge}
|
|
</span>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="top" className="max-w-[200px]">
|
|
<p className="font-semibold mb-1">{badge} past due</p>
|
|
{badgeNames.slice(0, 5).map((name) => (
|
|
<p key={name} className="text-xs opacity-80">
|
|
· {name}
|
|
</p>
|
|
))}
|
|
{badgeNames.length > 5 && (
|
|
<p className="text-xs opacity-60">+{badgeNames.length - 5} more</p>
|
|
)}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
)}
|
|
</NavLink>
|
|
);
|
|
});
|
|
|
|
NavPill.displayName = 'NavPill';
|