import * as React from 'react'; import { cn } from '../../../lib/utils'; interface CollapsibleContextValue { open: boolean; onOpenChange: (open: boolean) => void; } const CollapsibleContext = React.createContext(null); function useCollapsible() { const ctx = React.useContext(CollapsibleContext); if (!ctx) throw new Error('Collapsible components must be used within '); return ctx; } interface CollapsibleProps extends React.HTMLAttributes { defaultOpen?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; } const Collapsible = React.forwardRef( ({ defaultOpen = false, open: controlledOpen, onOpenChange: controlledOnOpenChange, className, children, ...props }, ref) => { const [internalOpen, setInternalOpen] = React.useState(defaultOpen); const isControlled = controlledOpen !== undefined; const open = isControlled ? controlledOpen : internalOpen; const onOpenChange = React.useCallback( (next: boolean) => { if (!isControlled) setInternalOpen(next); controlledOnOpenChange?.(next); }, [isControlled, controlledOnOpenChange] ); const value = React.useMemo(() => ({ open, onOpenChange }), [open, onOpenChange]); return (
{children}
); } ); Collapsible.displayName = 'Collapsible'; const CollapsibleTrigger = React.forwardRef>( ({ onClick, children, className, ...props }, ref) => { const { open, onOpenChange } = useCollapsible(); const handleClick = React.useCallback( (e: React.MouseEvent) => { onOpenChange(!open); onClick?.(e); }, [open, onOpenChange, onClick] ); return ( ); } ); CollapsibleTrigger.displayName = 'CollapsibleTrigger'; const CollapsibleContent = React.forwardRef>( ({ className, children, ...props }, ref) => { const { open } = useCollapsible(); return (
{children}
); } ); CollapsibleContent.displayName = 'CollapsibleContent'; export { Collapsible, CollapsibleTrigger, CollapsibleContent, useCollapsible };