{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Dock-TS-TW",
	"title": "Dock",
	"description": "macOS style magnifying dock with proximity scaling of icons.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "Dock/Dock.tsx",
			"content": "'use client';\n\nimport {\n  motion,\n  MotionValue,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  type SpringOptions,\n  AnimatePresence\n} from 'motion/react';\nimport React, { Children, cloneElement, useEffect, useMemo, useRef, useState } from 'react';\n\nexport type DockItemData = {\n  icon: React.ReactNode;\n  label: React.ReactNode;\n  onClick: () => void;\n  className?: string;\n};\n\nexport type DockProps = {\n  items: DockItemData[];\n  className?: string;\n  distance?: number;\n  panelHeight?: number;\n  baseItemSize?: number;\n  dockHeight?: number;\n  magnification?: number;\n  spring?: SpringOptions;\n};\n\ntype DockItemProps = {\n  className?: string;\n  children: React.ReactNode;\n  onClick?: () => void;\n  mouseX: MotionValue<number>;\n  spring: SpringOptions;\n  distance: number;\n  baseItemSize: number;\n  magnification: number;\n  label?: React.ReactNode;\n};\n\nfunction DockItem({\n  children,\n  className = '',\n  onClick,\n  mouseX,\n  spring,\n  distance,\n  magnification,\n  baseItemSize,\n  label\n}: DockItemProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const isHovered = useMotionValue(0);\n\n  const mouseDistance = useTransform(mouseX, val => {\n    const rect = ref.current?.getBoundingClientRect() ?? {\n      x: 0,\n      width: baseItemSize\n    };\n    return val - rect.x - baseItemSize / 2;\n  });\n\n  const targetSize = useTransform(mouseDistance, [-distance, 0, distance], [baseItemSize, magnification, baseItemSize]);\n  const size = useSpring(targetSize, spring);\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n    if (e.key === 'Enter' || e.key === ' ') {\n      e.preventDefault();\n      onClick?.();\n    }\n  };\n\n  return (\n    <motion.div\n      ref={ref}\n      style={{\n        width: size,\n        height: size\n      }}\n      onHoverStart={() => isHovered.set(1)}\n      onHoverEnd={() => isHovered.set(0)}\n      onFocus={() => isHovered.set(1)}\n      onBlur={() => isHovered.set(0)}\n      onClick={onClick}\n      onKeyDown={handleKeyDown}\n      className={`relative inline-flex items-center justify-center rounded-full bg-[#120F17] border-neutral-700 border-2 shadow-md ${className}`}\n      tabIndex={0}\n      role=\"button\"\n      aria-haspopup=\"true\"\n      aria-label={typeof label === 'string' ? label : undefined}\n    >\n      {Children.map(children, child =>\n        React.isValidElement(child)\n          ? cloneElement(child as React.ReactElement<{ isHovered?: MotionValue<number> }>, { isHovered })\n          : child\n      )}\n    </motion.div>\n  );\n}\n\ntype DockLabelProps = {\n  className?: string;\n  children: React.ReactNode;\n  isHovered?: MotionValue<number>;\n};\n\nfunction DockLabel({ children, className = '', isHovered }: DockLabelProps) {\n  const [isVisible, setIsVisible] = useState(false);\n\n  useEffect(() => {\n    if (!isHovered) return;\n    const unsubscribe = isHovered.on('change', latest => {\n      setIsVisible(latest === 1);\n    });\n    return () => unsubscribe();\n  }, [isHovered]);\n\n  return (\n    <AnimatePresence>\n      {isVisible && (\n        <motion.div\n          initial={{ opacity: 0, y: 0 }}\n          animate={{ opacity: 1, y: -10 }}\n          exit={{ opacity: 0, y: 0 }}\n          transition={{ duration: 0.2 }}\n          className={`${className} absolute -top-6 left-1/2 w-fit whitespace-pre rounded-md border border-neutral-700 bg-[#120F17] px-2 py-0.5 text-xs text-white`}\n          role=\"tooltip\"\n          style={{ x: '-50%' }}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  );\n}\n\ntype DockIconProps = {\n  className?: string;\n  children: React.ReactNode;\n  isHovered?: MotionValue<number>;\n};\n\nfunction DockIcon({ children, className = '' }: DockIconProps) {\n  return <div className={`flex items-center justify-center ${className}`}>{children}</div>;\n}\n\nexport default function Dock({\n  items,\n  className = '',\n  spring = { mass: 0.1, stiffness: 150, damping: 12 },\n  magnification = 70,\n  distance = 200,\n  panelHeight = 64,\n  dockHeight = 256,\n  baseItemSize = 50\n}: DockProps) {\n  const mouseX = useMotionValue(Infinity);\n  const isHovered = useMotionValue(0);\n\n  const maxHeight = useMemo(() => Math.max(dockHeight, magnification + magnification / 2 + 4), [magnification]);\n  const heightRow = useTransform(isHovered, [0, 1], [panelHeight, maxHeight]);\n  const height = useSpring(heightRow, spring);\n\n  return (\n    <motion.div style={{ height, scrollbarWidth: 'none' }} className=\"mx-2 flex max-w-full items-center\">\n      <motion.div\n        onMouseMove={({ pageX }) => {\n          isHovered.set(1);\n          mouseX.set(pageX);\n        }}\n        onMouseLeave={() => {\n          isHovered.set(0);\n          mouseX.set(Infinity);\n        }}\n        className={`${className} absolute bottom-2 left-1/2 transform -translate-x-1/2 flex items-end w-fit gap-4 rounded-2xl border-neutral-700 border-2 pb-2 px-4`}\n        style={{ height: panelHeight }}\n        role=\"toolbar\"\n        aria-label=\"Application dock\"\n      >\n        {items.map((item, index) => (\n          <DockItem\n            key={index}\n            onClick={item.onClick}\n            className={item.className}\n            mouseX={mouseX}\n            spring={spring}\n            distance={distance}\n            magnification={magnification}\n            baseItemSize={baseItemSize}\n            label={item.label}\n          >\n            <DockIcon>{item.icon}</DockIcon>\n            <DockLabel>{item.label}</DockLabel>\n          </DockItem>\n        ))}\n      </motion.div>\n    </motion.div>\n  );\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"motion@^12.23.12"
	]
}