'use client' import * as React from 'react' import { useState } from 'react' import { ChevronDown } from 'lucide-react' import { cn } from '../../lib/utils' export interface GroupedNavLink { href: string label: string icon?: React.ReactNode /** Optional trailing slot (count pill, status dot…). */ badge?: React.ReactNode } export interface GroupedNavGroup { label: string icon?: React.ReactNode items: GroupedNavLink[] /** Start expanded (default true). Ignored while a child is active — then forced open. */ defaultOpen?: boolean /** Show a chevron toggle (default true). false = a static section heading. */ collapsible?: boolean } /** A top-level link, or a named group of links. Discriminated by `items`. */ export type GroupedNavEntry = GroupedNavLink | GroupedNavGroup export interface GroupedNavRenderLinkArgs { link: GroupedNavLink active: boolean className: string /** The pre-composed inner content (icon + label + badge). */ content: React.ReactNode } export interface GroupedNavProps { items: GroupedNavEntry[] /** The current path; drives active state. */ activeHref?: string /** Override active matching (default: exact, or `activeHref` under `href/`). */ isActive?: (href: string, activeHref?: string) => boolean /** * Render a link — pass `next/link` here in a Next app. Default renders a plain * `` so the primitive stays framework-agnostic. */ renderLink?: (args: GroupedNavRenderLinkArgs) => React.ReactNode className?: string } function isGroup(entry: GroupedNavEntry): entry is GroupedNavGroup { return Array.isArray((entry as GroupedNavGroup).items) } function defaultIsActive(href: string, activeHref?: string): boolean { if (!activeHref) return false if (href === '/') return activeHref === '/' return activeHref === href || activeHref.startsWith(href + '/') } const defaultRenderLink = ({ link, active, className, content }: GroupedNavRenderLinkArgs) => ( {content} ) function linkClasses(active: boolean, indented: boolean) { return cn( 'flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors', indented && 'pl-9', active ? 'bg-primary text-primary-foreground' : 'text-gray-700 hover:bg-gray-100 hover:text-gray-900', ) } /** * A grouped sidebar navigation list: a mix of top-level links and collapsible * groups (e.g. Dashboard + a "Content" group of the three content types). Purely * the `