{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "context-menu",
  "type": "registry:ui",
  "title": "ContextMenu",
  "description": "منوی راست‌کلیک",
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/dropdown-menu.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json"
  ],
  "files": [
    {
      "path": "registry/kashi/ContextMenu.tsx",
      "type": "registry:ui",
      "target": "components/kashi/ContextMenu.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { MenuCtx, items, menuKeyDown } from \"./DropdownMenu\";\nimport { cx } from \"./cx\";\nimport { useKashi } from \"./KashiProvider\";\n\nexport interface ContextMenuProps {\n  /** ناحیه‌ای که راست‌کلیک روی آن منو را باز می‌کند */\n  children: ReactNode;\n  /** گزینه‌ها: MenuItem، MenuSeparator و MenuLabel */\n  menu: ReactNode;\n  /** نام منو برای صفحه‌خوان @default «گزینه‌ها» */\n  label?: string;\n  className?: string;\n}\n\n/**\n * منوی راست‌کلیک؛ با کیبورد هم: کلید ContextMenu یا Shift+F10 روی ناحیهٔ فوکوس‌شده.\n * همان گزینه‌ها و پیمایش منوی کشویی را دارد.\n */\nexport function ContextMenu({ children, menu, label, className }: ContextMenuProps) {\n  const { messages: M } = useKashi();\n  label ??= M.contextMenu.label;\n  const id = useId();\n  const [pos, setPos] = useState<{ x: number; y: number } | null>(null);\n  const area = useRef<HTMLDivElement>(null);\n  const list = useRef<HTMLUListElement>(null);\n  const returnTo = useRef<HTMLElement | null>(null);\n  const placed = useRef(false);\n  // نگه‌داشتن انگشت روی صفحهٔ لمسی؛ Safari در iOS رویداد contextmenu نمی‌فرستد\n  const press = useRef<{ t: ReturnType<typeof setTimeout>; x: number; y: number } | null>(null);\n  const cancelPress = () => {\n    if (press.current) clearTimeout(press.current.t);\n    press.current = null;\n  };\n\n  const close = (focusBack = true) => {\n    placed.current = false;\n    setPos(null);\n    if (focusBack) returnTo.current?.focus();\n  };\n  useEffect(() => cancelPress, []);\n  const openAt = (x: number, y: number) => {\n    returnTo.current = (document.activeElement as HTMLElement) ?? area.current;\n    setPos({ x, y });\n  };\n\n  useEffect(() => {\n    const el = list.current!;\n    const pop = el as HTMLElement & { showPopover?: () => void; hidePopover?: () => void };\n    if (!pos) {\n      if (el.matches?.(\":popover-open\")) pop.hidePopover?.();\n      return;\n    }\n    if (pop.showPopover && !el.matches(\":popover-open\")) pop.showPopover();\n    // یک بار جای‌گذاری: در RTL منو سمت چپ نشانگر باز می‌شود (مثل منوی بومی) و از صفحه بیرون نمی‌رود\n    if (!placed.current) {\n      placed.current = true;\n      const r = el.getBoundingClientRect();\n      const rtl = getComputedStyle(area.current!).direction === \"rtl\";\n      let nx = rtl ? pos.x - r.width : pos.x;\n      if (nx < 8) nx = rtl ? pos.x : 8;\n      nx = Math.max(8, Math.min(nx, innerWidth - r.width - 8));\n      const ny = Math.max(8, pos.y + r.height > innerHeight - 8 ? pos.y - r.height : pos.y);\n      if (nx !== pos.x || ny !== pos.y) return setPos({ x: nx, y: Math.min(ny, innerHeight - r.height - 8) });\n    }\n    items(el)[0]?.focus({ preventScroll: true });\n    const onDown = (e: PointerEvent) => !el.contains(e.target as Node) && close(false);\n    const onScroll = () => close(false);\n    document.addEventListener(\"pointerdown\", onDown);\n    // اسکرولی که همین حالا در جریان است منو را نبندد\n    const t = setTimeout(() => addEventListener(\"scroll\", onScroll, { passive: true, once: true }), 150);\n    return () => {\n      clearTimeout(t);\n      document.removeEventListener(\"pointerdown\", onDown);\n      removeEventListener(\"scroll\", onScroll);\n    };\n  }, [pos?.x, pos?.y]);\n\n  return (\n    <div\n      ref={area}\n      className={cx(\"k-context-area\", className)}\n      aria-haspopup=\"menu\"\n      aria-describedby={`${id}-hint`}\n      onContextMenu={(e) => {\n        e.preventDefault();\n        openAt(e.clientX, e.clientY);\n      }}\n      onPointerDown={(e) => {\n        if (e.pointerType !== \"touch\") return;\n        cancelPress();\n        const { clientX: x, clientY: y } = e;\n        press.current = { x, y, t: setTimeout(() => { press.current = null; openAt(x, y); }, 500) };\n      }}\n      onPointerMove={(e) => {\n        if (press.current && Math.hypot(e.clientX - press.current.x, e.clientY - press.current.y) > 10) cancelPress();\n      }}\n      onPointerUp={cancelPress}\n      onPointerCancel={cancelPress}\n      onKeyDown={(e) => {\n        if (e.key === \"ContextMenu\" || (e.shiftKey && e.key === \"F10\")) {\n          e.preventDefault();\n          const r = (document.activeElement as HTMLElement).getBoundingClientRect();\n          openAt(r.left + r.width / 2, r.top + r.height / 2);\n        }\n      }}\n    >\n      {children}\n      <span id={`${id}-hint`} className=\"k-sr-only\">{M.contextMenu.hint}</span>\n      <MenuCtx.Provider value={{ close }}>\n        <ul\n          ref={list}\n          // لایهٔ بالایی مرورگر (Popover API): والدِ دارای transform جای position: fixed را عوض نمی‌کند\n          {...({ popover: \"manual\" } as Record<string, string>)}\n          role=\"menu\"\n          aria-label={label}\n          className=\"k-menu k-menu--context\"\n          hidden={!pos}\n          style={pos ? ({ \"--_x\": `${pos.x}px`, \"--_y\": `${pos.y}px` } as React.CSSProperties) : undefined}\n          onKeyDown={(e) => menuKeyDown(e, list.current, () => close(), () => close(false))}\n        >\n          {menu}\n        </ul>\n      </MenuCtx.Provider>\n    </div>\n  );\n}\n"
    }
  ]
}