import * as React from 'react'; import { cn } from '@/lib/utils'; interface TabOption { value: T; label: React.ReactNode; } interface AnimatedTabsProps { options: TabOption[]; value: T; onValueChange: ( value: T ) => void; className?: string; } function AnimatedTabs( { options, value, onValueChange, className, }: AnimatedTabsProps ) { const containerRef = React.useRef( null ); const [ indicatorStyle, setIndicatorStyle ] = React.useState( { width: 0, left: 0, } ); // Update indicator position when value changes React.useEffect( () => { if ( ! containerRef.current ) { return; } const activeIndex = options.findIndex( opt => opt.value === value ); if ( activeIndex === -1 ) { return; } const buttons = containerRef.current.querySelectorAll( 'button' ); const activeButton = buttons[ activeIndex ] as HTMLButtonElement; if ( activeButton ) { setIndicatorStyle( { width: activeButton.offsetWidth, left: activeButton.offsetLeft, } ); } }, [ value, options ] ); // Also update on mount and resize React.useEffect( () => { const updateIndicator = () => { if ( ! containerRef.current ) { return; } const activeIndex = options.findIndex( opt => opt.value === value ); if ( activeIndex === -1 ) { return; } const buttons = containerRef.current.querySelectorAll( 'button' ); const activeButton = buttons[ activeIndex ] as HTMLButtonElement; if ( activeButton ) { setIndicatorStyle( { width: activeButton.offsetWidth, left: activeButton.offsetLeft, } ); } }; // Initial update after render const timer = setTimeout( updateIndicator, 0 ); window.addEventListener( 'resize', updateIndicator ); return () => { clearTimeout( timer ); window.removeEventListener( 'resize', updateIndicator ); }; }, [ options, value ] ); return (
{ /* Sliding indicator */ }
{ /* Tab buttons */ } { options.map( option => ( ) ) }
); } export { AnimatedTabs }; export type { TabOption };