'use client'; import { ReactNode } from 'react'; import { motion, Variants } from 'motion/react'; import React from 'react'; export type PresetType = | 'fade' | 'slide' | 'scale' | 'blur' | 'blur-slide' | 'zoom' | 'flip' | 'bounce' | 'rotate' | 'swing'; export type AnimatedGroupProps = { children: ReactNode; className?: string; variants?: { container?: Variants; item?: Variants; }; preset?: PresetType; as?: React.ElementType; asChild?: React.ElementType; }; const defaultContainerVariants: Variants = { visible: { transition: { staggerChildren: 0.1, }, }, }; const defaultItemVariants: Variants = { hidden: { opacity: 0 }, visible: { opacity: 1 }, }; const presetVariants: Record<PresetType, Variants> = { fade: {}, slide: { hidden: { y: 20 }, visible: { y: 0 }, }, scale: { hidden: { scale: 0.8 }, visible: { scale: 1 }, }, blur: { hidden: { filter: 'blur(4px)' }, visible: { filter: 'blur(0px)' }, }, 'blur-slide': { hidden: { filter: 'blur(4px)', y: 20 }, visible: { filter: 'blur(0px)', y: 0 }, }, zoom: { hidden: { scale: 0.5 }, visible: { scale: 1, transition: { type: 'spring', stiffness: 300, damping: 20 }, }, }, flip: { hidden: { rotateX: -90 }, visible: { rotateX: 0, transition: { type: 'spring', stiffness: 300, damping: 20 }, }, }, bounce: { hidden: { y: -50 }, visible: { y: 0, transition: { type: 'spring', stiffness: 400, damping: 10 }, }, }, rotate: { hidden: { rotate: -180 }, visible: { rotate: 0, transition: { type: 'spring', stiffness: 200, damping: 15 }, }, }, swing: { hidden: { rotate: -10 }, visible: { rotate: 0, transition: { type: 'spring', stiffness: 300, damping: 8 }, }, }, }; const addDefaultVariants = (variants: Variants) => ({ hidden: { ...defaultItemVariants.hidden, ...variants.hidden }, visible: { ...defaultItemVariants.visible, ...variants.visible }, }); function AnimatedGroup({ children, className, variants, preset, as = 'div', asChild = 'div', }: AnimatedGroupProps) { const selectedVariants = { item: addDefaultVariants(preset ? presetVariants[preset] : {}), container: addDefaultVariants(defaultContainerVariants), }; const containerVariants = variants?.container || selectedVariants.container; const itemVariants = variants?.item || selectedVariants.item; const MotionComponent = React.useMemo( () => motion.create(as as keyof JSX.IntrinsicElements), [as] ); const MotionChild = React.useMemo( () => motion.create(asChild as keyof JSX.IntrinsicElements), [asChild] ); return ( <MotionComponent initial='hidden' animate='visible' variants={containerVariants} className={className} > {React.Children.map(children, (child, index) => ( <MotionChild key={index} variants={itemVariants}> {child} </MotionChild> ))} </MotionComponent> ); } export { AnimatedGroup };