{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dock",
  "title": "Dock",
  "description": "macOS-style dock with magnifying icons and labels.",
  "dependencies": [
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/dock.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"radix-ui\";\n\nimport { gsap, motionScale } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\ntype DockContext = { size: number; magnification: number; distance: number };\nconst DockContext = React.createContext<DockContext>({ size: 40, magnification: 1.6, distance: 120 });\n\ntype DockProps = React.ComponentProps<\"nav\"> & {\n  /** Resting item size in px. */\n  size?: number;\n  /** Size multiplier for the item right under the pointer. */\n  magnification?: number;\n  /** How far (px) from the pointer items start to grow. */\n  distance?: number;\n};\n\n/**\n * A macOS-style dock: items swell as the pointer nears them and neighbours make room, eased with GSAP.\n * Keyboard focus lifts an item the same way; with motion off it is a plain toolbar. On narrow screens the items\n * shrink (staying square) so the dock always fits its container.\n */\nfunction Dock({ className, size = 40, magnification = 1.6, distance = 120, children, ...props }: DockProps) {\n  const ref = React.useRef<HTMLElement>(null);\n\n  const update = (clientX: number | null) => {\n    const dock = ref.current;\n    if (!dock) return;\n    const scale = motionScale(dock);\n    dock.querySelectorAll<HTMLElement>('[data-slot=\"dock-item\"]').forEach((item) => {\n      let target = size;\n      if (clientX !== null && scale) {\n        const rect = item.getBoundingClientRect();\n        const d = Math.abs(clientX - (rect.left + rect.width / 2));\n        const t = Math.max(0, 1 - d / distance);\n        target = size + size * (magnification - 1) * Math.cos(((1 - t) * Math.PI) / 2) ** 2 * Math.min(scale, 1.5);\n      }\n      gsap.to(item, { width: target, duration: 0.3, ease: \"cue\", overwrite: \"auto\" });\n    });\n  };\n\n  return (\n    <DockContext.Provider value={{ size, magnification, distance }}>\n      <nav\n        ref={ref}\n        data-slot=\"dock\"\n        className={cn(\"mx-auto flex w-fit max-w-full items-end gap-1.5 rounded-2xl border border-border bg-card/80 p-2 shadow-raised backdrop-blur-md sm:gap-2\", className)}\n        style={{ height: size + 16 }}\n        onPointerMove={(e) => update(e.clientX)}\n        onPointerLeave={() => update(null)}\n        {...props}\n      >\n        {children}\n      </nav>\n    </DockContext.Provider>\n  );\n}\n\ntype DockItemProps = React.ComponentProps<\"button\"> & {\n  /** Shown above the item on hover and focus, and used as its accessible name. */\n  label: string;\n  /** Marks the item as active (an indicator dot appears under it). */\n  active?: boolean;\n  asChild?: boolean;\n};\n\nfunction DockItem({ className, label, active, asChild, children, onFocus, onBlur, ...props }: DockItemProps) {\n  const { size, magnification } = React.useContext(DockContext);\n  const Comp = asChild ? Slot.Root : \"button\";\n  return (\n    <Comp\n      data-slot=\"dock-item\"\n      data-active={active || undefined}\n      aria-label={label}\n      className={cn(\n        \"group/dock relative flex aspect-square min-w-0 shrink items-center justify-center rounded-xl border border-border bg-background text-foreground shadow-control outline-none focus-visible:ring-3 focus-visible:ring-ring/50 [&_svg]:pointer-events-none [&_svg]:size-[45%]\",\n        className,\n      )}\n      style={{ width: size }}\n      onFocus={(e: React.FocusEvent<HTMLButtonElement>) => {\n        onFocus?.(e);\n        if (motionScale(e.currentTarget)) gsap.to(e.currentTarget, { width: size * Math.min(magnification, 1.3), duration: 0.3, ease: \"cue\" });\n      }}\n      onBlur={(e: React.FocusEvent<HTMLButtonElement>) => {\n        onBlur?.(e);\n        gsap.to(e.currentTarget, { width: size, duration: 0.3, ease: \"cue\" });\n      }}\n      {...props}\n    >\n      {children}\n      <span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute -top-8 left-1/2 -translate-x-1/2 translate-y-1 rounded-md bg-foreground px-2 py-0.5 text-xs whitespace-nowrap text-background opacity-0 shadow-overlay transition-[opacity,translate] duration-150 ease-cue group-hover/dock:translate-y-0 group-hover/dock:opacity-100 group-focus-visible/dock:translate-y-0 group-focus-visible/dock:opacity-100\"\n      >\n        {label}\n      </span>\n      {active && <span aria-hidden=\"true\" className=\"absolute -bottom-1.5 left-1/2 size-1 -translate-x-1/2 rounded-full bg-foreground/70\" />}\n    </Comp>\n  );\n}\n\nfunction DockSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n  return <div data-slot=\"dock-separator\" role=\"separator\" aria-orientation=\"vertical\" className={cn(\"mx-1 h-[70%] w-px self-center bg-border\", className)} {...props} />;\n}\n\nexport { Dock, DockItem, DockSeparator };\nexport type { DockProps, DockItemProps };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/dock",
  "categories": [
    "navigation"
  ],
  "type": "registry:ui"
}