'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 };