"use client"; import * as React from "react"; import { ChevronDown } from "lucide-react"; import { cn } from "@/lib/utils"; interface AccordionContextValue { value: string[]; onValueChange: (value: string[]) => void; } const AccordionContext = React.createContext( undefined ); interface AccordionProps { type?: "single" | "multiple"; defaultValue?: string | string[]; value?: string | string[]; onValueChange?: (value: string | string[]) => void; children: React.ReactNode; className?: string; } const Accordion = React.forwardRef( ({ type = "single", defaultValue, value, onValueChange, children, className }, ref) => { const [internalValue, setInternalValue] = React.useState( defaultValue ? Array.isArray(defaultValue) ? defaultValue : [defaultValue] : [] ); const controlledValue = value ? Array.isArray(value) ? value : [value] : undefined; const currentValue = controlledValue ?? internalValue; const handleValueChange = React.useCallback( (newValue: string[]) => { if (!controlledValue) { setInternalValue(newValue); } if (onValueChange) { onValueChange(type === "single" ? newValue[0] || "" : newValue); } }, [controlledValue, onValueChange, type] ); const contextValue = React.useMemo( () => ({ value: currentValue, onValueChange: handleValueChange, }), [currentValue, handleValueChange] ); return (
{children}
); } ); Accordion.displayName = "Accordion"; interface AccordionItemProps { value: string; children: React.ReactNode; className?: string; } const AccordionItem = React.forwardRef( ({ value, children, className }, ref) => { return (
{children}
); } ); AccordionItem.displayName = "AccordionItem"; interface AccordionTriggerProps { children: React.ReactNode; className?: string; } const AccordionTrigger = React.forwardRef< HTMLButtonElement, AccordionTriggerProps >(({ children, className }, ref) => { const context = React.useContext(AccordionContext); if (!context) { throw new Error("AccordionTrigger must be used within Accordion"); } const item = React.useContext(ItemContext); if (!item) { throw new Error("AccordionTrigger must be used within AccordionItem"); } const isOpen = context.value.includes(item.value); const handleClick = () => { const newValue = isOpen ? context.value.filter((v) => v !== item.value) : [...context.value, item.value]; context.onValueChange(newValue); }; return ( ); }); AccordionTrigger.displayName = "AccordionTrigger"; interface AccordionContentProps { children: React.ReactNode; className?: string; } const ItemContext = React.createContext<{ value: string } | undefined>( undefined ); const AccordionContent = React.forwardRef< HTMLDivElement, AccordionContentProps >(({ children, className }, ref) => { const context = React.useContext(AccordionContext); if (!context) { throw new Error("AccordionContent must be used within Accordion"); } const item = React.useContext(ItemContext); if (!item) { throw new Error("AccordionContent must be used within AccordionItem"); } const isOpen = context.value.includes(item.value); return (
{children}
); }); AccordionContent.displayName = "AccordionContent"; const AccordionItemWithContext = React.forwardRef< HTMLDivElement, AccordionItemProps >(({ value, children, ...props }, ref) => { return ( {children} ); }); AccordionItemWithContext.displayName = "AccordionItem"; export { Accordion, AccordionItemWithContext as AccordionItem, AccordionTrigger, AccordionContent, };