"use client"; import * as React from "react"; import { ChevronDown } from "lucide-react"; import { cn } from "../../lib/utils"; interface AccordionContextType { value: string[]; onValueChange: (value: string[]) => void; } const AccordionContext = React.createContext(undefined); // Add AccordionItemContext to pass value from AccordionItem to its children interface AccordionItemContextType { value: string; } const AccordionItemContext = React.createContext(undefined); interface AccordionProps extends React.HTMLAttributes { type?: "single" | "multiple"; value?: string | string[]; defaultValue?: string | string[]; onValueChange?: (value: string[]) => void; collapsible?: boolean; } const Accordion = React.forwardRef( ({ className, type = "multiple", value, defaultValue = [], onValueChange, children, ...props }, ref) => { // Always ensure values is an array, even if a single string is passed const [values, setValues] = React.useState( Array.isArray(value) ? value : value ? [value] : Array.isArray(defaultValue) ? defaultValue : defaultValue ? [defaultValue] : [] ); React.useEffect(() => { if (value !== undefined) { setValues(Array.isArray(value) ? value : value ? [value] : []); } }, [value]); const handleValueChange = React.useCallback((newValues: string[]) => { if (value === undefined) { setValues(newValues); } onValueChange?.(newValues); }, [onValueChange, value]); return (
{children}
); } ); Accordion.displayName = "Accordion"; interface AccordionItemProps extends React.HTMLAttributes { value: string; disabled?: boolean; } const AccordionItem = React.forwardRef( ({ className, value, disabled = false, children, ...props }, ref) => { return (
{children}
); } ); AccordionItem.displayName = "AccordionItem"; interface AccordionTriggerProps extends React.ButtonHTMLAttributes {} const AccordionTrigger = React.forwardRef( ({ className, children, ...props }, ref) => { const context = React.useContext(AccordionContext); if (!context) throw new Error("AccordionTrigger must be used within an Accordion"); const itemContext = React.useContext(AccordionItemContext); if (!itemContext) throw new Error("AccordionTrigger must be used within an AccordionItem"); const { value: values, onValueChange } = context; const { value: itemValue } = itemContext; const isOpen = values.includes(itemValue); const handleToggle = () => { const newValues = isOpen ? values.filter(v => v !== itemValue) : [...values, itemValue]; onValueChange(newValues); }; return ( ); } ); AccordionTrigger.displayName = "AccordionTrigger"; interface AccordionContentProps extends React.HTMLAttributes {} const AccordionContent = React.forwardRef( ({ className, children, ...props }, ref) => { const context = React.useContext(AccordionContext); if (!context) throw new Error("AccordionContent must be used within an Accordion"); const itemContext = React.useContext(AccordionItemContext); if (!itemContext) throw new Error("AccordionContent must be used within an AccordionItem"); const { value: values } = context; const { value: itemValue } = itemContext; const isOpen = values.includes(itemValue); return (
{children}
); } ); AccordionContent.displayName = "AccordionContent"; export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };