import * as React from 'react'; import * as AccordionPrimitive from '@radix-ui/react-accordion'; import { ChevronButton } from './ui/chevron-button'; import { cn } from "../utils/cn"; // --- SmoothAccordion ----------------------------------------------------------------- // Wrapper that re-exports AccordionPrimitive.Root for convenience export const SmoothAccordion = AccordionPrimitive.Root; // --- SmoothAccordionItem -------------------------------------------------------------- export const SmoothAccordionItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); SmoothAccordionItem.displayName = 'SmoothAccordionItem'; // --- SmoothAccordionTrigger ----------------------------------------------------------- interface SmoothAccordionTriggerProps extends React.ComponentPropsWithoutRef { label: React.ReactNode; className?: string; } export const SmoothAccordionTrigger = React.forwardRef< HTMLButtonElement, SmoothAccordionTriggerProps >(({ label, className, ...props }, ref) => ( {label} )); SmoothAccordionTrigger.displayName = 'SmoothAccordionTrigger'; // --- SmoothAccordionContent ----------------------------------------------------------- // Uses dynamic height measurement with ResizeObserver for ultra-smooth animation. export const SmoothAccordionContent = React.forwardRef< HTMLDivElement, React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { const [maxHeight, setMaxHeight] = React.useState(0); const contentInnerRef = React.useRef(null); const composedRef = (node: HTMLDivElement) => { // Allow Radix to receive ref as well if (typeof ref === 'function') ref(node); else if (ref) (ref as React.MutableRefObject).current = node; contentInnerRef.current = node; }; const updateHeight = React.useCallback(() => { if (contentInnerRef.current) { setMaxHeight(contentInnerRef.current.scrollHeight); } }, []); React.useEffect(() => { updateHeight(); }, [updateHeight, children]); // ResizeObserver for dynamic content React.useEffect(() => { if (!contentInnerRef.current) return; const ro = new ResizeObserver(updateHeight); ro.observe(contentInnerRef.current); return () => ro.disconnect(); }, [updateHeight]); const isOpen = (props as any)["data-state"] === "open"; return ( { // After closing, reset maxHeight to avoid lingering space if (!isOpen) { setMaxHeight(0); } }} >
{children}
); }); SmoothAccordionContent.displayName = 'SmoothAccordionContent';