{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mac-menu-popover",
  "title": "Mac menu popover",
  "description": "A macOS menu bar popover: a status item and a frosted panel whose arrow and panel are one shape, so one blur and one hairline follow the arrow. It grows from the arrow tip, reverses mid-flight, and drops its CSS blur for native Tauri or Electron vibrancy.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "https://eased.dev/r/eased-tokens.json",
    "https://eased.dev/r/eased-mac.json",
    "https://eased.dev/r/eased-floating.json"
  ],
  "files": [
    {
      "path": "src/registry/eased/mac-menu-popover/mac-menu-popover.tsx",
      "content": "\"use client\";\n\nimport { Popover as BasePopover } from \"@base-ui/react/popover\";\nimport { useLayoutEffect, useRef, type ComponentProps, type ReactNode } from \"react\";\nimport { floatingMotion, floatingTrigger, floatingVars, FLOATING_ROLES, type FloatingAlign, type FloatingMotion } from \"../_primitives/floating/floating\";\nimport { MAC_RADIUS, macFont, macHairlineStroke, macMaterial, macShadow, macTextSecondary, type MacMaterialMode } from \"../lib/mac\";\n\n/**\n * A macOS menu bar popover: a status item in the menu bar and a frosted panel that hangs from it\n * on an arrow. The panel and arrow are one shape (one clip-path), so a single backdrop blur covers\n * both with no seam, and the hairline is one stroke that follows the arrow. Behaviour is Base UI\n * Popover; motion is Eased's floating layer, so the panel grows from the arrow tip and a close\n * during the open turns around mid-flight.\n */\n\n/** Groups the parts and owns the open state (`open`, `defaultOpen`, `onOpenChange`). Non-modal. */\nexport const MacMenuPopover = BasePopover.Root;\n\n/** Closes the popover. `<MacMenuPopoverClose render={<MacMenuItem … />} />` makes a row that closes it. */\nexport const MacMenuPopoverClose = BasePopover.Close;\n\n/** Arrow geometry, px: 24 × 12 (45° sides), a softened tip and a small fillet where it meets the panel. */\nconst ARROW = { width: 24, height: 12, tip: 2.5, fillet: 3 } as const;\n/** Keeps the arrow clear of the panel's rounded corners when a collision shifts the panel. */\nconst ARROW_PADDING = MAC_RADIUS.popover + ARROW.fillet + 1;\n\n/** The spec's motion: a little further and a little smaller than a web popover, on the same transitions. */\nconst MOTION: FloatingMotion = { ...FLOATING_ROLES.popover, distance: 6, scale: 0.95 };\nconst MOTION_VARS = floatingVars(MOTION);\n\n/*\n * Fills for hover, press, separators and tracks on the material, as variables so the parts below\n * stay single-class. Same values as macOS's quaternary/tertiary system fills, light and dark.\n */\nconst FILLS =\n  \"[--mac-hover:rgb(0_0_0/0.06)] [--mac-press:rgb(0_0_0/0.1)] [--mac-separator:rgb(0_0_0/0.1)] [--mac-track:rgb(0_0_0/0.08)] \" +\n  \"in-[.dark]:[--mac-hover:rgb(255_255_255/0.08)] in-[.dark]:[--mac-press:rgb(255_255_255/0.14)] in-[.dark]:[--mac-separator:rgb(255_255_255/0.1)] in-[.dark]:[--mac-track:rgb(255_255_255/0.1)] \" +\n  \"in-data-[theme=dark]:[--mac-hover:rgb(255_255_255/0.08)] in-data-[theme=dark]:[--mac-press:rgb(255_255_255/0.14)] in-data-[theme=dark]:[--mac-separator:rgb(255_255_255/0.1)] in-data-[theme=dark]:[--mac-track:rgb(255_255_255/0.1)]\";\n\n/* ---------- Status item ---------- */\n\nexport type MacStatusItemProps = Omit<ComponentProps<typeof BasePopover.Trigger>, \"className\"> & {\n  /** The accessible name. Status items are usually an icon or a number, so this is required. */\n  label: string;\n  className?: string;\n};\n\n/**\n * The menu bar button that opens the popover: an icon, optionally with a short value (\"62%\").\n * Like a macOS status item it presses and stays highlighted (`data-popup-open`) by colour only,\n * never by scale, so the arrow's anchor stays put. Keyboard focus draws Eased's ring.\n */\nexport function MacStatusItem({ label, className = \"\", ...props }: MacStatusItemProps) {\n  return (\n    <BasePopover.Trigger\n      aria-label={label}\n      {...props}\n      className={`${floatingTrigger} ${FILLS} inline-flex h-[22px] shrink-0 items-center gap-[5px] rounded-[5px] px-[7px] font-medium tabular-nums active:bg-(--mac-press) data-popup-open:bg-(--mac-press) cursor-default! ${className}`}\n    />\n  );\n}\n\n/* ---------- The panel ---------- */\n\nexport type MacMenuPopoverContentProps = {\n  /** Panel width, px. Never wider than the space available. Default 320. */\n  width?: number;\n  /** Draw the arrow. Without it the panel is a plain rounded rectangle. Default true. */\n  arrow?: boolean;\n  /** `\"css\"` frosts the panel in a web page; `\"native\"` leaves it to a Tauri/Electron window's vibrancy. Default \"css\". */\n  material?: MacMaterialMode;\n  /** Below the menu bar, or above an anchor near the bottom of the screen. Default \"bottom\". */\n  side?: \"bottom\" | \"top\";\n  /** Default \"center\": the tip sits under the status item; collisions shift the panel and the tip stays put. */\n  align?: FloatingAlign;\n  /** Gap from the status item to the arrow tip, px. Default 6 (5px below a 24px menu bar). */\n  sideOffset?: number;\n  alignOffset?: number;\n  /** Space kept from the boundary, px. Default 8. */\n  collisionPadding?: number;\n  /** The area the panel must stay inside. Default: the viewport (the tray window, in Tauri). */\n  collisionBoundary?: ComponentProps<typeof BasePopover.Positioner>[\"collisionBoundary\"];\n  /** Where focus goes on open. Default: the first control for a keyboard open, the panel for a pointer open. */\n  initialFocus?: ComponentProps<typeof BasePopover.Popup>[\"initialFocus\"];\n  /** Where focus returns on close. Default: the status item. */\n  finalFocus?: ComponentProps<typeof BasePopover.Popup>[\"finalFocus\"];\n  /** Portal target. Default `document.body`. */\n  container?: HTMLElement | null;\n  /** Stacking order. Default 50. */\n  zIndex?: number;\n  /** Appended to the content area's classes (12px padding, a flex column). */\n  className?: string;\n  children?: ReactNode;\n};\n\n/**\n * The frosted panel. 11px corners, 12px padding, 13px SF type. It exposes `--pad` and\n * `--radius-outer` so nested corners stay concentric (rows use 7px).\n */\nexport function MacMenuPopoverContent({\n  width = 320,\n  arrow = true,\n  material = \"css\",\n  side = \"bottom\",\n  align = \"center\",\n  sideOffset = 6,\n  alignOffset = 0,\n  collisionPadding = 8,\n  collisionBoundary,\n  initialFocus,\n  finalFocus,\n  container,\n  zIndex = 50,\n  className = \"\",\n  children,\n}: MacMenuPopoverContentProps) {\n  return (\n    <BasePopover.Portal container={container}>\n      <BasePopover.Positioner\n        side={side}\n        align={align}\n        sideOffset={sideOffset}\n        alignOffset={alignOffset}\n        collisionPadding={collisionPadding}\n        collisionBoundary={collisionBoundary}\n        arrowPadding={ARROW_PADDING}\n        style={{ zIndex }}\n      >\n        <Panel width={width} arrow={arrow} material={material} side={side} initialFocus={initialFocus} finalFocus={finalFocus} className={className}>\n          {children}\n        </Panel>\n      </BasePopover.Positioner>\n    </BasePopover.Portal>\n  );\n}\n\ntype PanelProps = Required<Pick<MacMenuPopoverContentProps, \"width\" | \"arrow\" | \"material\" | \"side\" | \"className\">> &\n  Pick<MacMenuPopoverContentProps, \"initialFocus\" | \"finalFocus\" | \"children\">;\n\n/** Attributes the floating motion reads; the shadow layer copies them from the panel to move in step. */\nconst MIRRORED = [\"data-starting-style\", \"data-ending-style\", \"data-side\"];\n\n/**\n * Mounted only while open (inside the Positioner), so its refs are live in its layout effect.\n *\n * Layers, back to front: the shadow (a sibling, because a filter on an ancestor would stop the\n * panel's backdrop blur from seeing the page), then the panel, which carries the blur, the\n * clip-path and the floating motion on one element, so the blur also survives the fade.\n */\nfunction Panel({ width, arrow, material, side, initialFocus, finalFocus, className, children }: PanelProps) {\n  const popupRef = useRef<HTMLDivElement>(null);\n  const arrowRef = useRef<HTMLDivElement>(null);\n  const shadowRef = useRef<HTMLDivElement>(null);\n  const shadowPathRef = useRef<SVGPathElement>(null);\n  const hairlineRef = useRef<SVGPathElement>(null);\n\n  useLayoutEffect(() => {\n    const popup = popupRef.current;\n    const shadow = shadowRef.current;\n    if (!popup || !shadow) return;\n    const arrowEl = arrowRef.current;\n\n    // Size from offset* (layout px), so the scale animation never distorts the shape.\n    const draw = () => {\n      const w = popup.offsetWidth;\n      const h = popup.offsetHeight;\n      const tip = arrowEl ? arrowEl.offsetLeft + arrowEl.offsetWidth / 2 : w / 2;\n      const d = shapePath(w, h, tip, arrowEl ? (popup.dataset.side === \"top\" ? \"top\" : \"bottom\") : null);\n      popup.style.clipPath = `path(\"${d}\")`;\n      // The shadow layer keeps only what falls outside the shape, so it never darkens the glass.\n      shadow.style.clipPath = `path(evenodd, \"M-80 -80H${w + 80}V${h + 80}H-80Z ${d}\")`;\n      shadowPathRef.current?.setAttribute(\"d\", d);\n      hairlineRef.current?.setAttribute(\"d\", d);\n    };\n    const mirror = () => {\n      for (const name of MIRRORED) {\n        const value = popup.getAttribute(name);\n        if (value === null) shadow.removeAttribute(name);\n        else shadow.setAttribute(name, value);\n      }\n    };\n\n    mirror();\n    draw();\n    // Observer callbacks run before the next paint, so the shape and shadow never lag a frame.\n    const resize = new ResizeObserver(draw);\n    resize.observe(popup);\n    const mutations = new MutationObserver(() => {\n      mirror();\n      draw();\n    });\n    mutations.observe(popup, { attributes: true, attributeFilter: MIRRORED });\n    // Base UI moves the arrow (inline `left`) when a collision shifts the panel.\n    if (arrowEl) mutations.observe(arrowEl, { attributes: true, attributeFilter: [\"style\"] });\n    return () => {\n      resize.disconnect();\n      mutations.disconnect();\n    };\n  }, [arrow]);\n\n  return (\n    <>\n      <div\n        ref={shadowRef}\n        aria-hidden\n        // Mounts with the panel in its starting state, before the observer takes over.\n        data-starting-style=\"\"\n        data-side={side}\n        className={`${floatingMotion()} ${macShadow} pointer-events-none absolute inset-0`}\n        style={MOTION_VARS}\n      >\n        <svg className=\"absolute inset-0 size-full overflow-visible\">\n          <path ref={shadowPathRef} />\n        </svg>\n      </div>\n\n      <BasePopover.Popup\n        ref={popupRef}\n        // A pointer open focuses the panel, so no row lights up; a keyboard open focuses the first control.\n        initialFocus={initialFocus ?? ((type) => (type === \"keyboard\" ? true : popupRef.current))}\n        finalFocus={finalFocus}\n        className={`${floatingMotion()} ${macMaterial(\"popover\", material, { hairline: false })} ${macFont} ${FILLS} relative flex max-h-(--available-height) max-w-(--available-width) flex-col outline-none [--pad:12px] [--radius-outer:11px] ${\n          arrow ? \"data-[side=bottom]:pt-3 data-[side=top]:pb-3\" : \"\"\n        }`}\n        style={{ ...MOTION_VARS, width }}\n      >\n        {/* Invisible: Base UI positions it under the status item; the shape reads its offset. */}\n        {arrow && <BasePopover.Arrow ref={arrowRef} className=\"pointer-events-none h-3 w-6 data-[side=bottom]:top-0 data-[side=top]:bottom-0\" />}\n        <div className={`flex min-h-0 flex-col overflow-y-auto p-(--pad) ${className}`}>{children}</div>\n        {/* A 1px stroke on the clip edge: the clip keeps its inner half, a 0.5px hairline. */}\n        <svg aria-hidden className=\"pointer-events-none absolute inset-0 size-full\">\n          <path ref={hairlineRef} fill=\"none\" strokeWidth={1} className={macHairlineStroke} />\n        </svg>\n      </BasePopover.Popup>\n    </>\n  );\n}\n\nconst n = (v: number) => Math.round(v * 100) / 100;\n\n/**\n * The panel outline as one SVG path: a rounded rectangle with the arrow (when `arrowSide` is set)\n * on the edge facing the anchor, its tip at `tipX`. Built for the bottom side (arrow on top) and\n * mirrored vertically for the top side.\n */\nfunction shapePath(w: number, h: number, tipX: number, arrowSide: \"top\" | \"bottom\" | null) {\n  const a = arrowSide ? ARROW.height : 0;\n  const r = Math.max(0, Math.min(MAC_RADIUS.popover, (h - a) / 2, w / 2));\n  const flip = arrowSide === \"top\";\n  const sweep = flip ? 0 : 1;\n  const p = (x: number, y: number) => `${n(x)} ${n(flip ? h - y : y)}`;\n  const arc = (x: number, y: number) => `A${n(r)} ${n(r)} 0 0 ${sweep} ${p(x, y)}`;\n\n  let top = \"\";\n  if (arrowSide) {\n    const hw = ARROW.width / 2;\n    const k = Math.SQRT1_2; // the arrow's sides run at 45°\n    const f = ARROW.fillet;\n    const t = ARROW.tip;\n    const x = Math.min(Math.max(tipX, r + hw + f), w - r - hw - f);\n    top =\n      `L${p(x - hw - f, a)} Q${p(x - hw, a)} ${p(x - hw + f * k, a - f * k)} ` +\n      `L${p(x - t * k, t * k)} Q${p(x, 0)} ${p(x + t * k, t * k)} ` +\n      `L${p(x + hw - f * k, a - f * k)} Q${p(x + hw, a)} ${p(x + hw + f, a)} `;\n  }\n\n  return (\n    `M${p(r, a)} ${top}L${p(w - r, a)} ${arc(w, a + r)} L${p(w, h - r)} ${arc(w - r, h)} ` +\n    `L${p(r, h)} ${arc(0, h - r)} L${p(0, a + r)} ${arc(r, a)} Z`\n  );\n}\n\n/**\n * The panel without the popover, for a Tauri/Electron tray window, where the native tray icon is\n * the status item and the window is the panel. Same type, fills and 12px padding; it fills the\n * window, so the window rounds the corners (Tauri `windowEffects.radius`; macOS rounds a frameless\n * Electron window). Usually `material=\"native\"`: a CSS blur can't see the desktop behind a window.\n */\nexport function MacMenuPanel({ material = \"native\", className = \"\", children }: { material?: MacMaterialMode; className?: string; children?: ReactNode }) {\n  return (\n    <div className={`${macMaterial(\"popover\", material)} ${macFont} ${FILLS} flex min-h-full flex-col p-(--pad) [--pad:12px] [--radius-outer:11px] ${className}`}>\n      {children}\n    </div>\n  );\n}\n\n/* ---------- Rows ---------- */\n\nexport type MacMenuItemProps = ComponentProps<\"button\"> & {\n  /** A 16px icon on the content line. */\n  icon?: ReactNode;\n  /** A keyboard shortcut shown on the right, e.g. \"⌘D\". Display only: wire the key yourself. */\n  shortcut?: string;\n};\n\n/**\n * A menu-style action row. Its highlight bleeds 8px into the panel padding so the icon and label\n * sit on the content line; the 7px corner is concentric with the panel. Colour only on hover and\n * press; keyboard focus shows the same highlight, a shade stronger.\n */\nexport function MacMenuItem({ icon, shortcut, className = \"\", children, type = \"button\", ...props }: MacMenuItemProps) {\n  return (\n    <button\n      type={type}\n      {...props}\n      className={`-mx-2 grid h-6 items-center gap-2 rounded-[7px] px-2 text-left outline-none [transition:background-color_150ms_var(--eased-ease-standard)] select-none hover-fine:bg-(--mac-hover) focus-visible:bg-(--mac-press) active:bg-(--mac-press) disabled:opacity-40 ${\n        icon ? \"grid-cols-[16px_1fr_auto]\" : \"grid-cols-[1fr_auto]\"\n      } ${className}`}\n    >\n      {icon && <span className=\"flex size-4 items-center justify-center [&_svg]:size-4\">{icon}</span>}\n      <span className=\"truncate\">{children}</span>\n      {shortcut && <Shortcut keys={shortcut} />}\n    </button>\n  );\n}\n\n/** Modifiers right-aligned, then the key centred in its own cell, so keys line up in a column as in a macOS menu. */\nfunction Shortcut({ keys }: { keys: string }) {\n  const chars = Array.from(keys);\n  return (\n    <span className={`${macTextSecondary} flex justify-end`}>\n      <span className=\"tracking-wide\">{chars.slice(0, -1).join(\"\")}</span>\n      <span className=\"w-[1.2em] text-center\">{chars[chars.length - 1]}</span>\n    </span>\n  );\n}\n\n/** A divider between groups, on the content lines. */\nexport function MacMenuSeparator({ className = \"\" }: { className?: string }) {\n  return <div role=\"separator\" className={`my-[5px] h-px shrink-0 bg-(--mac-separator) ${className}`} />;\n}\n",
      "type": "registry:ui",
      "target": "@components/eased/mac-menu-popover/mac-menu-popover.tsx"
    }
  ],
  "docs": "Import MacMenuPopover, MacStatusItem, MacMenuPopoverContent, MacMenuItem and MacMenuSeparator from components/eased/mac-menu-popover/mac-menu-popover. The Eased tokens were merged into your global CSS; nothing to import. Put MacStatusItem (with a label) and MacMenuPopoverContent inside MacMenuPopover, and wrap rows that should close the panel in MacMenuPopoverClose. Dark mode follows .dark or data-theme=\"dark\". In a Tauri or Electron tray window, render MacMenuPanel instead; it defaults to material=\"native\" (a CSS blur cannot see the desktop there). Keep html and body transparent and call useSystemAppearance() from components/eased/lib/mac so dark follows the system.",
  "type": "registry:ui"
}