{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "mag-dock", "title": "MagDock", "description": "A spring-animated dock with icons, tooltips, and active states.", "dependencies": [ "motion" ], "files": [ { "path": "registry/new-york/components/mag-dock/mag-dock.tsx", "type": "registry:component", "content": "\"use client\";\n\nimport React, { useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n AnimatePresence,\n motion,\n useMotionValue,\n useSpring,\n useTransform,\n type MotionValue,\n} from \"motion/react\";\n\nexport interface MagDockItem {\n id: number;\n icon: React.ElementType;\n name: string;\n active?: boolean;\n onClick?: () => void;\n}\n\ninterface MagDockProps {\n items: MagDockItem[];\n className?: string;\n}\n\nfunction MagDockIcon({\n mouseX,\n icon: Icon,\n name,\n onItemClick,\n}: {\n mouseX: MotionValue;\n icon: React.ElementType;\n name: string;\n onItemClick?: () => void;\n}) {\n const ref = useRef(null);\n const [hovered, setHovered] = useState(false);\n\n const distance = useTransform(mouseX, (val) => {\n const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };\n return val - bounds.x - bounds.width / 2;\n });\n\n const sizeSync = useTransform(\n distance,\n [-150, -75, 0, 75, 150],\n [40, 52, 72, 52, 40],\n );\n const size = useSpring(sizeSync, { mass: 0.1, stiffness: 200, damping: 14 });\n\n const liftSync = useTransform(size, [40, 72], [0, -14]);\n const lift = useSpring(liftSync, { mass: 0.1, stiffness: 200, damping: 14 });\n\n return (\n
\n \n {hovered && (\n \n {name}\n \n )}\n \n\n setHovered(true)}\n onHoverEnd={() => setHovered(false)}\n onClick={() => onItemClick?.()}\n whileTap={{ scale: 0.88 }}\n className={cn(\n \"flex cursor-pointer items-center justify-center rounded-2xl\",\n \"bg-linear-to-b from-background/30 to-background/10 text-foreground\",\n \"shadow-[0_2px_8px_rgba(0,0,0,0.15),inset_0_1px_0_rgba(255,255,255,0.2)]\",\n \"dark:shadow-[0_2px_8px_rgba(0,0,0,0.4),inset_0_1px_0_rgba(255,255,255,0.1)]\",\n \"transition-colors hover:from-background/40 hover:to-background/15\",\n )}\n >\n \n \n
\n );\n}\n\nexport default function MagDock({ items, className }: MagDockProps) {\n const mouseX = useMotionValue(Infinity);\n\n return (\n \n mouseX.set(e.pageX)}\n onMouseLeave={() => mouseX.set(Infinity)}\n className={cn(\n \"mx-auto flex h-16 items-end gap-2 rounded-2xl px-3 pb-2\",\n \"border border-border/40\",\n \"bg-background/70 backdrop-blur-2xl\",\n \"shadow-[0_8px_32px_rgba(0,0,0,0.12)]\",\n \"dark:shadow-[0_8px_32px_rgba(0,0,0,0.45)]\",\n )}\n >\n {items.map((item) => (\n \n ))}\n \n \n );\n}\n" } ], "type": "registry:component" }