import { useState, type ComponentType, type ReactNode } from "react"; import { Link } from "@/lib/router"; import { ChevronRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useSidebar } from "../context/SidebarContext"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { cn } from "@/lib/utils"; type SidebarSectionIcon = ComponentType<{ className?: string }>; export type SidebarSectionMenuAction = | { type: "item"; label: string; icon?: SidebarSectionIcon; href?: string; onSelect?: () => void; } | { type: "separator" }; export type SidebarSectionRadioChoice = { label: string; value: string; }; type SidebarSectionMenu = { actions?: SidebarSectionMenuAction[]; ariaLabel?: string; radioChoices?: SidebarSectionRadioChoice[]; radioLabel?: string; radioValue?: string; onRadioValueChange?: (value: string) => void; }; type SidebarSectionHeaderAction = { ariaLabel: string; icon: SidebarSectionIcon; onClick: () => void; }; interface SidebarSectionProps { label: string; children: ReactNode; collapsible?: { open: boolean; onOpenChange: (open: boolean) => void; }; menu?: SidebarSectionMenu; headerAction?: SidebarSectionHeaderAction; } function SidebarSectionHeader({ collapsible, headerAction, label, menu, }: Pick) { const { isMobile } = useSidebar(); const [menuOpen, setMenuOpen] = useState(false); const hasMenu = Boolean( menu && ((menu.actions?.length ?? 0) > 0 || (menu.radioChoices?.length ?? 0) > 0), ); const labelClassName = "text-[10px] font-medium uppercase tracking-widest font-mono text-muted-foreground/60"; const headerControlVisibilityClassName = isMobile ? "opacity-100" : "opacity-0 group-hover/sidebar-section:opacity-100 group-focus-within/sidebar-section:opacity-100"; const caretClassName = cn( "h-3 w-3 shrink-0 text-muted-foreground/60 transition-all", headerControlVisibilityClassName, collapsible?.open && "rotate-90", menuOpen && "opacity-100", ); const actionClassName = cn( "h-5 w-5 shrink-0 text-muted-foreground/60 transition-opacity hover:text-foreground data-[state=open]:opacity-100", headerControlVisibilityClassName, ); const headerContent = {label}; const HeaderActionIcon = headerAction?.icon; const headingControl = hasMenu ? ( {menu?.actions?.map((action, index) => { if (action.type === "separator") { return ; } const Icon = action.icon; const content = ( <> {Icon ? : null} {action.label} ); if (action.href) { return ( {content} ); } return ( {content} ); })} {menu?.radioChoices && menu.radioChoices.length > 0 ? ( {menu.radioChoices.map((choice) => ( {choice.label} ))} ) : null} ) : (
{headerContent}
); return (
{collapsible ? ( ) : null} {headingControl} {headerAction && HeaderActionIcon ? ( ) : null}
); } export function SidebarSection({ label, children, collapsible, menu, headerAction, }: SidebarSectionProps) { const content =
{children}
; if (collapsible) { return ( {content} ); } return (
{content}
); }