{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dock",
  "title": "Dock",
  "description": "The Liquid Glass shelf of app icons with magnification, running indicators, badges, labels and auto-hide. Windows minimize into it.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "@cupertinoui/macos-theme"
  ],
  "files": [
    {
      "path": "registry/macos/ui/dock.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Tooltip as TooltipPrimitive } from \"@base-ui/react/tooltip\"\n\nimport { cn } from \"@/lib/utils\"\n\n// The macOS Dock: a Liquid Glass shelf of app icons along the bottom of the\n// screen. `size` is the size of a tile, as set in Desktop & Dock settings;\n// the icon artwork fills 80.5% of it, like Apple's icon grid.\n// `magnification` grows tiles near the pointer, and `autoHide` slides the\n// Dock away until the pointer reaches the bottom edge of its container.\n// Position it yourself, e.g. `absolute bottom-2 left-1/2 -translate-x-1/2`.\nfunction Dock({\n  className,\n  style,\n  ref,\n  size = 51,\n  magnification,\n  autoHide = false,\n  revealed = false,\n  children,\n  onPointerMove,\n  onPointerLeave,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  size?: number\n  magnification?: number\n  autoHide?: boolean\n  revealed?: boolean\n}) {\n  const element = React.useRef<HTMLDivElement | null>(null)\n  const centers = React.useRef<number[] | null>(null)\n  const [nearEdge, setNearEdge] = React.useState(false)\n  const shown = !autoHide || revealed || nearEdge\n\n  // Auto-hide: show the Dock when the pointer touches the bottom edge of the\n  // Dock's container, hide it again when the pointer moves well above it.\n  React.useEffect(() => {\n    if (!autoHide) return\n    function onMove(event: PointerEvent) {\n      const node = element.current\n      if (!node) return\n      const area = (node.offsetParent ?? document.documentElement).getBoundingClientRect()\n      const dock = node.getBoundingClientRect()\n      const insideX = event.clientX >= area.left && event.clientX <= area.right\n      if (insideX && event.clientY >= area.bottom - 6 && event.clientY <= area.bottom) {\n        setNearEdge(true)\n      } else if (event.clientY < area.bottom - dock.height - 48 || !insideX) {\n        setNearEdge(false)\n      }\n    }\n    window.addEventListener(\"pointermove\", onMove)\n    return () => window.removeEventListener(\"pointermove\", onMove)\n  }, [autoHide])\n\n  // Magnification: icons grow with their distance to the pointer. Centers are\n  // measured once when the pointer enters, so growing icons don't feed back.\n  function items() {\n    return Array.from(\n      element.current?.querySelectorAll<HTMLElement>('[data-slot=\"dock-item\"]') ?? []\n    )\n  }\n\n  function magnify(clientX: number) {\n    const node = element.current\n    if (!node || !magnification) return\n    const scale = node.getBoundingClientRect().width / node.offsetWidth || 1\n    const list = items()\n    centers.current ??= list.map((item) => {\n      const rect = item.getBoundingClientRect()\n      return rect.left + rect.width / 2\n    })\n    // Tiles up to four places away grow.\n    const range = size * 4\n    list.forEach((item, index) => {\n      const distance = Math.abs(clientX - (centers.current?.[index] ?? 0)) / scale\n      const curve = distance >= range ? 0 : (1 + Math.cos((Math.PI * distance) / range)) / 2\n      item.style.setProperty(\"--dock-item-size\", `${size + (magnification - size) * curve}px`)\n    })\n  }\n\n  function reset() {\n    centers.current = null\n    for (const item of items()) item.style.removeProperty(\"--dock-item-size\")\n  }\n\n  return (\n    <TooltipPrimitive.Provider delay={0}>\n      <div\n        ref={(node) => {\n          element.current = node\n          if (typeof ref === \"function\") return ref(node)\n          if (ref) ref.current = node\n        }}\n        data-slot=\"dock\"\n        data-state={shown ? \"visible\" : \"hidden\"}\n        role=\"toolbar\"\n        aria-label=\"Dock\"\n        className={cn(\n          // The shelf keeps its height; magnified icons rise above it.\n          \"flex h-[calc(var(--dock-size)+18px)] w-fit items-end gap-0.5 rounded-[24px] p-[9px] text-mac-label shadow-mac-dock mac-material-dock transition-[translate,opacity] duration-300 ease-out data-[state=hidden]:pointer-events-none data-[state=hidden]:translate-y-[calc(100%+24px)] data-[state=hidden]:opacity-0\",\n          className\n        )}\n        style={{ \"--dock-size\": `${size}px`, ...style } as React.CSSProperties}\n        onPointerMove={(event) => {\n          onPointerMove?.(event)\n          magnify(event.clientX)\n        }}\n        onPointerLeave={(event) => {\n          onPointerLeave?.(event)\n          reset()\n        }}\n        {...props}\n      >\n        {children}\n      </div>\n    </TooltipPrimitive.Provider>\n  )\n}\n\n// One tile in the Dock. `label` is shown above it on hover, `running` adds\n// the dot below it and `badge` a red count. Pass a ref to use the item as\n// the target that a\n// window minimizes into. The button is laid over the icon rather than\n// wrapped around it, so an item can show live content, such as a\n// miniature of a minimized window, that has buttons of its own.\nfunction DockItem({\n  className,\n  style,\n  label,\n  running = false,\n  badge,\n  children,\n  ...props\n}: React.ComponentProps<\"button\"> & {\n  label?: React.ReactNode\n  running?: boolean\n  badge?: React.ReactNode\n}) {\n  const button = (\n    <button\n      type=\"button\"\n      aria-label={typeof label === \"string\" ? label : undefined}\n      className=\"absolute inset-0 cursor-default rounded-[22%] outline-none focus-visible:mac-focus-ring\"\n      {...props}\n    />\n  )\n\n  return (\n    <div\n      data-slot=\"dock-item\"\n      data-running={running || undefined}\n      className={cn(\n        // A size container, so the badge scales with the tile.\n        \"@container relative flex size-[var(--dock-item-size,var(--dock-size))] shrink-0 items-center justify-center transition-[width,height] duration-150 ease-out has-[>button:active]:brightness-75 [&>img]:size-[80.5%] [&>svg]:size-[80.5%]\",\n        // The running indicator below the tile.\n        \"after:absolute after:-bottom-[5px] after:left-1/2 after:size-1 after:-translate-x-1/2 after:rounded-full after:bg-black/50 after:opacity-0 data-[running]:after:opacity-100 dark:after:bg-white/55\",\n        className\n      )}\n      style={style}\n    >\n      {children}\n      {badge != null && badge !== false ? (\n        <span\n          data-slot=\"dock-badge\"\n          className=\"pointer-events-none absolute top-[2cqw] right-[1cqw] flex h-[37cqw] min-w-[37cqw] items-center justify-center rounded-full bg-[#ec3830] px-[4cqw] text-[19.6cqw] leading-none font-medium text-white tabular-nums shadow-[0_0.5px_1.5px_rgb(0_0_0/0.3)]\"\n        >\n          {badge}\n        </span>\n      ) : null}\n      {label ? (\n        <TooltipPrimitive.Root disableHoverablePopup>\n          <TooltipPrimitive.Trigger render={button} />\n          <TooltipPrimitive.Portal>\n            {/* 3px between the arrow's tip and the tile. */}\n            <TooltipPrimitive.Positioner side=\"top\" sideOffset={9} className=\"z-50\">\n              <TooltipPrimitive.Popup className=\"flex h-[26px] items-center rounded-full px-3.5 text-[14px] text-mac-label shadow-mac-dock-label mac-material-dock-label dark:text-white/90\">\n                {label}\n                <TooltipPrimitive.Arrow className=\"top-full block h-1.5 w-4 mac-material-dock-label [clip-path:polygon(0_0,100%_0,59%_100%,41%_100%)]\" />\n              </TooltipPrimitive.Popup>\n            </TooltipPrimitive.Positioner>\n          </TooltipPrimitive.Portal>\n        </TooltipPrimitive.Root>\n      ) : (\n        button\n      )}\n    </div>\n  )\n}\n\n// A squircle app icon on the icon grid. Give it a background and put a\n// symbol or image inside.\nfunction DockIcon({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"dock-icon\"\n      className={cn(\n        \"flex size-[80.5%] items-center justify-center overflow-hidden rounded-[22.5%] shadow-[inset_0_0_0_0.5px_rgb(0_0_0/0.1),0_1px_2px_rgb(0_0_0/0.2)] [&>svg]:size-[62%]\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\n// The divider between apps and minimized windows, folders and the Trash.\nfunction DockSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dock-separator\"\n      role=\"separator\"\n      aria-orientation=\"vertical\"\n      className={cn(\n        \"mx-[11px] h-(--dock-size) w-px shrink-0 self-center bg-black/15 dark:bg-white/30\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Dock, DockIcon, DockItem, DockSeparator }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}