everydayserieswebsite/components/ui/animated-background.tsx
2025-02-16 19:49:16 +05:30

91 lines
2.1 KiB
TypeScript

'use client';
import { cn } from '@/lib/utils';
import { AnimatePresence, Transition, motion } from 'motion/react';
import {
Children,
cloneElement,
ReactElement,
useEffect,
useState,
useId,
} from 'react';
export type AnimatedBackgroundProps = {
children:
| ReactElement<{ 'data-id': string }>[]
| ReactElement<{ 'data-id': string }>;
defaultValue?: string;
onValueChange?: (newActiveId: string | null) => void;
className?: string;
transition?: Transition;
enableHover?: boolean;
};
export function AnimatedBackground({
children,
defaultValue,
onValueChange,
className,
transition,
enableHover = false,
}: AnimatedBackgroundProps) {
const [activeId, setActiveId] = useState<string | null>(null);
const uniqueId = useId();
const handleSetActiveId = (id: string | null) => {
setActiveId(id);
if (onValueChange) {
onValueChange(id);
}
};
useEffect(() => {
if (defaultValue !== undefined) {
setActiveId(defaultValue);
}
}, [defaultValue]);
return Children.map(children, (child: any, index) => {
const id = child.props['data-id'];
const interactionProps = enableHover
? {
onMouseEnter: () => handleSetActiveId(id),
onMouseLeave: () => handleSetActiveId(null),
}
: {
onClick: () => handleSetActiveId(id),
};
return cloneElement(
child,
{
key: index,
className: cn('relative inline-flex', child.props.className),
'data-checked': activeId === id ? 'true' : 'false',
...interactionProps,
},
<>
<AnimatePresence initial={false}>
{activeId === id && (
<motion.div
layoutId={`background-${uniqueId}`}
className={cn('absolute inset-0', className)}
transition={transition}
initial={{ opacity: defaultValue ? 1 : 0 }}
animate={{
opacity: 1,
}}
exit={{
opacity: 0,
}}
/>
)}
</AnimatePresence>
<div className='z-10'>{child.props.children}</div>
</>
);
});
}