import React, { createContext, useContext, useState, useCallback } from 'react'; import { cn } from '@/lib/utils'; interface AccordionContextType { value: string | string[]; onValueChange: (value: string) => void; type: 'single' | 'multiple'; } const AccordionContext = createContext( undefined ); interface AccordionItemContextType { value: string; } const AccordionItemContext = createContext< AccordionItemContextType | undefined >(undefined); function useAccordion() { const context = useContext(AccordionContext); if (!context) { throw new Error('Accordion components must be used within an Accordion'); } return context; } function useAccordionItem() { const context = useContext(AccordionItemContext); if (!context) { throw new Error( 'AccordionTrigger and AccordionContent must be used within an AccordionItem' ); } return context; } interface AccordionProps { type?: 'single' | 'multiple'; value?: string | string[]; onValueChange?: (value: string | string[]) => void; children: React.ReactNode; } function Accordion({ type = 'single', value: controlledValue, onValueChange, children, }: AccordionProps) { const [internalValue, setInternalValue] = useState( type === 'single' ? '' : [] ); const isControlled = controlledValue !== undefined; const value = isControlled ? controlledValue : internalValue; const handleValueChange = useCallback( (itemValue: string) => { if (type === 'single') { const newValue = value === itemValue ? '' : itemValue; if (!isControlled) { setInternalValue(newValue); } onValueChange?.(newValue); } else { const valueArray = Array.isArray(value) ? value : []; const newValue = valueArray.includes(itemValue) ? valueArray.filter((v) => v !== itemValue) : [...valueArray, itemValue]; if (!isControlled) { setInternalValue(newValue); } onValueChange?.(newValue); } }, [value, type, isControlled, onValueChange] ); return (
{children}
); } interface AccordionItemProps { value: string; children: React.ReactNode; className?: string; } function AccordionItem({ value, children, className }: AccordionItemProps) { return (
{children}
); } interface AccordionTriggerProps extends React.ButtonHTMLAttributes { children: React.ReactNode; } function AccordionTrigger({ className, children, ...props }: AccordionTriggerProps) { const accordion = useAccordion(); const item = useAccordionItem(); const handleClick = () => { accordion.onValueChange(item.value); }; const isOpen = accordion.type === 'single' ? accordion.value === item.value : Array.isArray(accordion.value) && accordion.value.includes(item.value); return (
); } interface AccordionContentProps extends React.HTMLAttributes { children: React.ReactNode; } function AccordionContent({ className, children, ...props }: AccordionContentProps) { const accordion = useAccordion(); const item = useAccordionItem(); const isOpen = accordion.type === 'single' ? accordion.value === item.value : Array.isArray(accordion.value) && accordion.value.includes(item.value); return (
{children}
); } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };