91 lines
2.1 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
});
|
|
}
|