'use client'; import { forwardRef, HTMLAttributes, ReactNode, Children, cloneElement, isValidElement, useMemo } from 'react'; export type AnimationType = 'fade' | 'slide' | 'scale' | 'blur'; export type Direction = 'up' | 'down' | 'left' | 'right'; export interface AnimatedListProps extends Omit, 'animation'> { /** List items (should be valid React elements) */ children: ReactNode; /** Animation type */ animation?: AnimationType; /** Delay between items in ms */ staggerDelay?: number; /** Animation duration in ms */ duration?: number; /** Direction for slide animations */ direction?: Direction; /** Element type to render */ as?: 'ul' | 'ol' | 'div'; } const animationStyles = { fade: 'animate-fadeIn', slideUp: 'animate-slideUp', slideDown: 'animate-slideDown', slideLeft: 'animate-slideLeft', slideRight: 'animate-slideRight', scale: 'animate-scaleIn', blur: 'animate-blurIn', }; export const AnimatedList = forwardRef( ( { children, animation = 'fade', staggerDelay = 100, duration = 400, direction = 'up', as = 'ul', className = '', style, ...props }, ref ) => { const childrenArray = Children.toArray(children); const itemAnimation = useMemo(() => { if (animation === 'slide') { const directionMap = { up: animationStyles.slideUp, down: animationStyles.slideDown, left: animationStyles.slideLeft, right: animationStyles.slideRight, } as const; return directionMap[direction]; } return animationStyles[`${animation}Cap` as keyof typeof animationStyles] ?? animationStyles.fade; }, [animation, direction]); const Element = as; return ( <> {childrenArray.map((child, index) => { const itemStyle = { animation: `${itemAnimation} ${duration}ms cubic-bezier(0.16, 1, 0.3, 1) forwards ${index * staggerDelay}ms`, opacity: 0, ...((isValidElement(child) && (child.props as any).style) || {}), }; if (isValidElement(child)) { return cloneElement(child, { key: child.key ?? index, className: child.props.className || '', style: itemStyle, }); } return (
  • {child}
  • ); })}
    ); } ); AnimatedList.displayName = 'AnimatedList'; export default AnimatedList;