forked from farhoodlabs/paperclip
13fd656e2b
Adds an amber "Beta" tag next to "Routines" in both the page heading and the sidebar navigation item. Extends SidebarNavItem with textBadge and textBadgeTone props for reusable text badges. Co-Authored-By: Paperclip <noreply@paperclip.ing> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
2.7 KiB
TypeScript
93 lines
2.7 KiB
TypeScript
import { NavLink } from "@/lib/router";
|
|
import { cn } from "../lib/utils";
|
|
import { useSidebar } from "../context/SidebarContext";
|
|
import type { LucideIcon } from "lucide-react";
|
|
|
|
interface SidebarNavItemProps {
|
|
to: string;
|
|
label: string;
|
|
icon: LucideIcon;
|
|
end?: boolean;
|
|
className?: string;
|
|
badge?: number;
|
|
badgeTone?: "default" | "danger";
|
|
textBadge?: string;
|
|
textBadgeTone?: "default" | "amber";
|
|
alert?: boolean;
|
|
liveCount?: number;
|
|
}
|
|
|
|
export function SidebarNavItem({
|
|
to,
|
|
label,
|
|
icon: Icon,
|
|
end,
|
|
className,
|
|
badge,
|
|
badgeTone = "default",
|
|
textBadge,
|
|
textBadgeTone = "default",
|
|
alert = false,
|
|
liveCount,
|
|
}: SidebarNavItemProps) {
|
|
const { isMobile, setSidebarOpen } = useSidebar();
|
|
|
|
return (
|
|
<NavLink
|
|
to={to}
|
|
end={end}
|
|
onClick={() => { if (isMobile) setSidebarOpen(false); }}
|
|
className={({ isActive }) =>
|
|
cn(
|
|
"flex items-center gap-2.5 px-3 py-2 text-[13px] font-medium transition-colors",
|
|
isActive
|
|
? "bg-accent text-foreground"
|
|
: "text-foreground/80 hover:bg-accent/50 hover:text-foreground",
|
|
className,
|
|
)
|
|
}
|
|
>
|
|
<span className="relative shrink-0">
|
|
<Icon className="h-4 w-4" />
|
|
{alert && (
|
|
<span className="absolute -right-0.5 -top-0.5 h-2 w-2 rounded-full bg-red-500 shadow-[0_0_0_2px_hsl(var(--background))]" />
|
|
)}
|
|
</span>
|
|
<span className="flex-1 truncate">{label}</span>
|
|
{textBadge && (
|
|
<span
|
|
className={cn(
|
|
"ml-auto rounded-full px-1.5 py-0.5 text-[10px] font-medium leading-none",
|
|
textBadgeTone === "amber"
|
|
? "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400"
|
|
: "bg-muted text-muted-foreground",
|
|
)}
|
|
>
|
|
{textBadge}
|
|
</span>
|
|
)}
|
|
{liveCount != null && liveCount > 0 && (
|
|
<span className="ml-auto flex items-center gap-1.5">
|
|
<span className="relative flex h-2 w-2">
|
|
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-blue-500" />
|
|
</span>
|
|
<span className="text-[11px] font-medium text-blue-600 dark:text-blue-400">{liveCount} live</span>
|
|
</span>
|
|
)}
|
|
{badge != null && badge > 0 && (
|
|
<span
|
|
className={cn(
|
|
"ml-auto rounded-full px-1.5 py-0.5 text-xs leading-none",
|
|
badgeTone === "danger"
|
|
? "bg-red-600/90 text-red-50"
|
|
: "bg-primary text-primary-foreground",
|
|
)}
|
|
>
|
|
{badge}
|
|
</span>
|
|
)}
|
|
</NavLink>
|
|
);
|
|
}
|