{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "context-menu",
  "type": "registry:ui",
  "title": "Context Menu",
  "description": "Opens at the pointer on right-click, long-press or Shift+F10.",
  "categories": [
    "menus"
  ],
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "https://www.stealth.pm/r/tokens.json",
    "https://www.stealth.pm/r/cn.json",
    "https://www.stealth.pm/r/dropdown-menu.json"
  ],
  "files": [
    {
      "path": "components/ui/context-menu.tsx",
      "content": "\"use client\";\nimport { ContextMenu as Base } from \"@base-ui/react/context-menu\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { DropdownMenuPopup, type DropdownMenuPopupProps } from \"@/components/ui/dropdown-menu\";\n\n// Rows, groups, submenus and shortcuts are the dropdown menu's own parts: a context\n// menu is the same list, opened from a different place.\nexport {\n  DropdownMenuItem as ContextMenuItem,\n  DropdownMenuLinkItem as ContextMenuLinkItem,\n  DropdownMenuCheckboxItem as ContextMenuCheckboxItem,\n  DropdownMenuRadioGroup as ContextMenuRadioGroup,\n  DropdownMenuRadioItem as ContextMenuRadioItem,\n  DropdownMenuGroup as ContextMenuGroup,\n  DropdownMenuLabel as ContextMenuLabel,\n  DropdownMenuSeparator as ContextMenuSeparator,\n  DropdownMenuSub as ContextMenuSub,\n  DropdownMenuSubTrigger as ContextMenuSubTrigger,\n  DropdownMenuSubContent as ContextMenuSubContent,\n  DropdownMenuShortcut as ContextMenuShortcut,\n} from \"@/components/ui/dropdown-menu\";\nexport type {\n  DropdownMenuItemProps as ContextMenuItemProps,\n  DropdownMenuCheckboxItemProps as ContextMenuCheckboxItemProps,\n  DropdownMenuRadioItemProps as ContextMenuRadioItemProps,\n  DropdownMenuSubTriggerProps as ContextMenuSubTriggerProps,\n} from \"@/components/ui/dropdown-menu\";\n\nexport type ContextMenuProps = Base.Root.Props;\n\n/** Groups a region and the menu it opens on right-click or long-press. */\nexport function ContextMenu(props: ContextMenuProps) {\n  return <Base.Root {...props} />;\n}\n\nexport type ContextMenuTriggerProps = Base.Trigger.Props & {\n  /** Open from the keyboard with Shift+F10 or the Menu key while the region has focus. */\n  keyboard?: boolean;\n};\n\n/**\n * The region that owns the menu. On touch it sinks slightly while a long-press\n * is registering, so the half-second hold never feels like nothing is happening.\n */\nexport function ContextMenuTrigger({ keyboard = true, className, onKeyDown, onTouchStart, onTouchMove, onTouchEnd, onTouchCancel, ...rest }: ContextMenuTriggerProps) {\n  const [pressing, setPressing] = useState<{ x: number; y: number } | null>(null);\n\n  return (\n    <Base.Trigger\n      data-pressing={pressing ? \"\" : undefined}\n      onKeyDown={(e) => {\n        onKeyDown?.(e);\n        if (!keyboard || e.defaultPrevented || e.target !== e.currentTarget) return;\n        if (e.key === \"ContextMenu\" || (e.shiftKey && e.key === \"F10\")) {\n          e.preventDefault();\n          // Open where a pointer would have: near the start of the region, just under it.\n          const r = e.currentTarget.getBoundingClientRect();\n          e.currentTarget.dispatchEvent(\n            new MouseEvent(\"contextmenu\", { bubbles: true, cancelable: true, clientX: r.left + Math.min(24, r.width / 2), clientY: r.bottom - 4 }),\n          );\n        }\n      }}\n      onTouchStart={(e) => {\n        onTouchStart?.(e);\n        const t = e.touches[0];\n        setPressing(e.touches.length === 1 && t ? { x: t.clientX, y: t.clientY } : null);\n      }}\n      onTouchMove={(e) => {\n        onTouchMove?.(e);\n        const t = e.touches[0];\n        // Same 10px tolerance Base UI uses before it gives up on the long-press.\n        if (pressing && t && Math.hypot(t.clientX - pressing.x, t.clientY - pressing.y) > 10) setPressing(null);\n      }}\n      onTouchEnd={(e) => {\n        onTouchEnd?.(e);\n        setPressing(null);\n      }}\n      onTouchCancel={(e) => {\n        onTouchCancel?.(e);\n        setPressing(null);\n      }}\n      className={(state) =>\n        cn(\n          \"outline-none focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-fg-3\",\n          \"pointer-coarse:select-none\",\n          \"transition-[scale] duration-200 ease-out-expo\",\n          \"motion-safe:data-pressing:not-data-popup-open:scale-[0.985] data-pressing:not-data-popup-open:duration-500 data-pressing:not-data-popup-open:ease-out\",\n          typeof className === \"function\" ? className(state) : className,\n        )\n      }\n      {...rest}\n    />\n  );\n}\n\nexport type ContextMenuContentProps = DropdownMenuPopupProps & {\n  collisionPadding?: Base.Positioner.Props[\"collisionPadding\"];\n  /** Where the menu is portaled. Defaults to the body. */\n  container?: Base.Portal.Props[\"container\"];\n};\n\n/**\n * The menu, opened with its corner tucked under the pointer. It grows out of that\n * exact point, and flips to stay on screen near the edges.\n */\nexport function ContextMenuContent({ collisionPadding = 8, container, ...rest }: ContextMenuContentProps) {\n  return (\n    <Base.Portal container={container}>\n      <Base.Positioner collisionPadding={collisionPadding} className=\"z-(--z-dropdown) outline-none\">\n        <DropdownMenuPopup {...rest} />\n      </Base.Positioner>\n    </Base.Portal>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "https://www.stealth.pm/ui/context-menu"
}
