{ "name": "dock", "type": "registry:ui", "registryDependencies": [], "dependencies": [ "motion" ], "devDependencies": [], "tailwind": {}, "cssVars": { "light": {}, "dark": {} }, "files": [ { "path": "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 {\n Children,\n cloneElement,\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { cn } from '@/lib/utils';\n\nconst DOCK_HEIGHT = 128;\nconst DEFAULT_MAGNIFICATION = 80;\nconst DEFAULT_DISTANCE = 150;\nconst DEFAULT_PANEL_HEIGHT = 64;\n\nexport type DockProps = {\n children: React.ReactNode;\n className?: string;\n distance?: number;\n panelHeight?: number;\n magnification?: number;\n spring?: SpringOptions;\n};\n\nexport type DockItemProps = {\n className?: string;\n children: React.ReactNode;\n onClick?: () => void;\n};\n\nexport type DockLabelProps = {\n className?: string;\n children: React.ReactNode;\n};\n\nexport type DockIconProps = {\n className?: string;\n children: React.ReactNode;\n};\n\nexport type DocContextType = {\n mouseX: MotionValue;\n spring: SpringOptions;\n magnification: number;\n distance: number;\n};\n\nexport type DockProviderProps = {\n children: React.ReactNode;\n value: DocContextType;\n};\n\nconst DockContext = createContext(undefined);\n\nfunction DockProvider({ children, value }: DockProviderProps) {\n return {children};\n}\n\nfunction useDock() {\n const context = useContext(DockContext);\n if (!context) {\n throw new Error('useDock must be used within an DockProvider');\n }\n return context;\n}\n\nfunction Dock({\n children,\n className,\n spring = { mass: 0.1, stiffness: 150, damping: 12 },\n magnification = DEFAULT_MAGNIFICATION,\n distance = DEFAULT_DISTANCE,\n panelHeight = DEFAULT_PANEL_HEIGHT,\n}: DockProps) {\n const mouseX = useMotionValue(Infinity);\n const isHovered = useMotionValue(0);\n\n const maxHeight = useMemo(() => {\n return Math.max(DOCK_HEIGHT, magnification + magnification / 2 + 4);\n }, [magnification]);\n\n const heightRow = useTransform(isHovered, [0, 1], [panelHeight, maxHeight]);\n const height = useSpring(heightRow, spring);\n\n return (\n \n {\n isHovered.set(1);\n mouseX.set(pageX);\n }}\n onMouseLeave={() => {\n isHovered.set(0);\n mouseX.set(Infinity);\n }}\n className={cn(\n 'mx-auto flex w-fit gap-4 rounded-2xl bg-gray-50 px-4 dark:bg-neutral-900',\n className\n )}\n style={{ height: panelHeight }}\n role='toolbar'\n aria-label='Application dock'\n >\n \n {children}\n \n \n \n );\n}\n\nfunction DockItem({ children, className, onClick }: DockItemProps) {\n const ref = useRef(null);\n\n const { distance, magnification, mouseX, spring } = useDock();\n\n const isHovered = useMotionValue(0);\n\n const mouseDistance = useTransform(mouseX, (val) => {\n const domRect = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };\n return val - domRect.x - domRect.width / 2;\n });\n\n const widthTransform = useTransform(\n mouseDistance,\n [-distance, 0, distance],\n [40, magnification, 40]\n );\n\n const width = useSpring(widthTransform, spring);\n\n return (\n isHovered.set(1)}\n onHoverEnd={() => isHovered.set(0)}\n onFocus={() => isHovered.set(1)}\n onBlur={() => isHovered.set(0)}\n className={cn(\n 'relative inline-flex items-center justify-center',\n className\n )}\n tabIndex={0}\n role='button'\n aria-haspopup='true'\n onClick={onClick}\n >\n {Children.map(children, (child) =>\n cloneElement(child as React.ReactElement, { width, isHovered })\n )}\n \n );\n}\n\nfunction DockLabel({ children, className, ...rest }: DockLabelProps) {\n const restProps = rest as Record;\n const isHovered = restProps['isHovered'] as MotionValue;\n const [isVisible, setIsVisible] = useState(false);\n\n useEffect(() => {\n const unsubscribe = isHovered.on('change', (latest) => {\n setIsVisible(latest === 1);\n });\n\n return () => unsubscribe();\n }, [isHovered]);\n\n return (\n \n {isVisible && (\n \n {children}\n \n )}\n \n );\n}\n\nfunction DockIcon({ children, className, ...rest }: DockIconProps) {\n const restProps = rest as Record;\n const width = restProps['width'] as MotionValue;\n\n const widthTransform = useTransform(width, (val) => val / 2);\n\n return (\n \n {children}\n \n );\n}\n\nexport { Dock, DockIcon, DockItem, DockLabel };\n", "type": "registry:ui" } ] }