{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dock",
  "title": "Dock",
  "description": "A premium macOS-Style Animated Dock Menu component",
  "dependencies": [
    "framer-motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "app/registry/spectrumui/dock.tsx",
      "content": "\"use client\";\n\nimport React, { useRef, useState, useContext } from \"react\";\nimport {\n  motion,\n  MotionValue,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  AnimatePresence,\n} from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface DockProps {\n  children: React.ReactNode;\n  className?: string;\n  magnification?: number;\n  distance?: number;\n  variant?: \"normal\" | \"glass\";\n}\n\nexport interface DockIconProps {\n  children: React.ReactNode;\n  label: string;\n  onClick?: () => void;\n  className?: string;\n}\n\nconst DockContext = React.createContext<{\n  mouseX: MotionValue<number>;\n  magnification: number;\n  distance: number;\n  variant: \"normal\" | \"glass\";\n} | null>(null);\n\nconst SPRING_TACTILE = {\n  type: \"spring\",\n  stiffness: 400,\n  damping: 25,\n  mass: 0.1,\n} as const;\n\nexport function Dock({\n  children,\n  className,\n  magnification = 80,\n  distance = 140,\n  variant = \"normal\",\n}: DockProps) {\n  const mouseX = useMotionValue(Infinity);\n  const ref = useRef<HTMLDivElement>(null);\n\n  const handleMouseMove = (e: React.MouseEvent) => {\n    mouseX.set(e.clientX);\n  };\n\n  const handleMouseLeave = () => {\n    mouseX.set(Infinity);\n  };\n\n  return (\n    <DockContext.Provider value={{ mouseX, magnification, distance, variant }}>\n      <motion.div\n        ref={ref}\n        onMouseMove={handleMouseMove}\n        onMouseLeave={handleMouseLeave}\n        className={cn(\n          \"mx-auto flex h-16 items-end gap-4 rounded-3xl pb-3\",\n          variant === \"glass\"\n            ? \"bg-white/10 dark:bg-white/5 backdrop-blur-xl border border-white/20 dark:border-white/10 shadow-[0_8px_32px_0_rgba(0,0,0,0.15)] px-4\"\n            : \"bg-neutral-100/80 dark:bg-neutral-900/30 border border-neutral-200/50 dark:border-neutral-800/50 px-4\",\n          className\n        )}\n        role=\"navigation\"\n        aria-label=\"Desktop Dock\"\n      >\n        {children}\n      </motion.div>\n    </DockContext.Provider>\n  );\n}\n\nexport function DockIcon({\n  children,\n  label,\n  onClick,\n  className,\n}: DockIconProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const context = useContext(DockContext);\n  const [isBouncing, setIsBouncing] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n\n  if (!context) {\n    throw new Error(\"DockIcon must be used within a Dock component\");\n  }\n\n  const { mouseX, magnification, distance, variant } = context;\n\n  // Calculate distance from mouse pointer to center of icon\n  const distanceTransform = useTransform(mouseX, (val) => {\n    const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };\n    const centerX = bounds.x + bounds.width / 2;\n    return val - centerX;\n  });\n\n  // Calculate dynamic width based on proximity\n  const widthTransform = useTransform(distanceTransform, (d) => {\n    if (mouseX.get() === Infinity) return 40; // Base width\n    const power = Math.max(0, 1 - Math.abs(d) / distance);\n    return 40 + (magnification - 40) * power;\n  });\n\n  // Smooth width adjustments\n  const width = useSpring(widthTransform, SPRING_TACTILE);\n\n  const handleIconClick = () => {\n    if (isBouncing) return;\n    setIsBouncing(true);\n    onClick?.();\n    setTimeout(() => {\n      setIsBouncing(false);\n    }, 600);\n  };\n\n  return (\n    <div\n      ref={ref}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n      onClick={handleIconClick}\n      className=\"relative flex items-center justify-center cursor-pointer\"\n      role=\"button\"\n      tabIndex={0}\n      aria-label={label}\n      onKeyDown={(e) => {\n        if (e.key === \"Enter\" || e.key === \" \") {\n          e.preventDefault();\n          handleIconClick();\n        }\n      }}\n    >\n      <AnimatePresence>\n        {isHovered && (\n          <motion.span\n            initial={{ opacity: 0, y: 10, scale: 0.95, x: \"-50%\" }}\n            animate={{ opacity: 1, y: 0, scale: 1, x: \"-50%\" }}\n            exit={{ opacity: 0, y: 4, scale: 0.95, x: \"-50%\" }}\n            transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n            className=\"absolute bottom-full mb-3 left-1/2 -translate-x-1/2 rounded-lg bg-neutral-900 px-2.5 py-1 text-[10px] font-medium text-neutral-100 dark:bg-neutral-100 dark:text-neutral-900 shadow-md border border-neutral-800 dark:border-neutral-200 select-none pointer-events-none whitespace-nowrap\"\n            role=\"tooltip\"\n          >\n            {label}\n          </motion.span>\n        )}\n      </AnimatePresence>\n\n      <motion.div\n        style={{ width, height: width }}\n        animate={isBouncing ? { y: [0, -16, 0, -8, 0] } : { y: 0 }}\n        transition={{ duration: 0.6, ease: \"easeInOut\" }}\n        className={cn(\n          \"flex items-center justify-center rounded-2xl transition-colors\",\n          variant === \"glass\"\n            ? \"bg-white/10 dark:bg-white/5 backdrop-blur-md border border-white/20 dark:border-white/10 shadow-lg text-neutral-800 dark:text-neutral-100 hover:bg-white/20 dark:hover:bg-white/10\"\n            : \"bg-neutral-200 dark:bg-neutral-800 border border-neutral-300/50 dark:border-neutral-700/50 text-neutral-800 dark:text-neutral-200 hover:bg-neutral-300 dark:hover:bg-neutral-700\",\n          className\n        )}\n      >\n        <div className=\"flex h-3/5 w-3/5 items-center justify-center [&>svg]:h-full [&>svg]:w-full\">\n          {children}\n        </div>\n      </motion.div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/spectrumui/dock.tsx"
    }
  ],
  "type": "registry:component"
}
