'use client'; import { cva, type VariantProps } from 'class-variance-authority'; import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'; import React, { PropsWithChildren, useRef } from 'react'; import { cn } from '../../lib/utils'; export interface DockProps extends VariantProps { className?: string; magnification?: number; distance?: number; direction?: 'top' | 'middle' | 'bottom'; children: React.ReactNode; } const DEFAULT_MAGNIFICATION = 60; const DEFAULT_DISTANCE = 140; const dockVariants = cva( 'supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max gap-2 rounded-2xl border p-2 backdrop-blur-md', ); const Dock = React.forwardRef( ( { className, children, magnification = DEFAULT_MAGNIFICATION, distance = DEFAULT_DISTANCE, direction = 'bottom', ...props }, ref, ) => { const mouseX = useMotionValue(Infinity); const renderChildren = () => { return React.Children.map(children, (child) => { if (React.isValidElement(child) && child.type === DockIcon) { return React.cloneElement(child, { ...child.props, mouseX: mouseX, magnification: magnification, distance: distance, }); } return child; }); }; return ( mouseX.set(e.pageX)} onMouseLeave={() => mouseX.set(Infinity)} {...props} className={cn(dockVariants({ className }), { 'items-start': direction === 'top', 'items-center': direction === 'middle', 'items-end': direction === 'bottom', })} > {renderChildren()} ); }, ); Dock.displayName = 'Dock'; export interface DockIconProps { size?: number; magnification?: number; distance?: number; mouseX?: any; className?: string; children?: React.ReactNode; props?: PropsWithChildren; } const DockIcon = ({ size, magnification = DEFAULT_MAGNIFICATION, distance = DEFAULT_DISTANCE, mouseX, className, children, ...props }: DockIconProps) => { const ref = useRef(null); const distanceCalc = useTransform(mouseX, (val: number) => { const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }; return val - bounds.x - bounds.width / 2; }); const widthSync = useTransform(distanceCalc, [-distance, 0, distance], [40, magnification, 40]); const width = useSpring(widthSync, { mass: 0.1, stiffness: 150, damping: 12, }); return ( {children} ); }; DockIcon.displayName = 'DockIcon'; export { Dock, DockIcon, dockVariants };