{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dropdown-menu",
  "type": "registry:ui",
  "title": "DropdownMenu، MenuItem، MenuSeparator، MenuLabel",
  "description": "دراپ‌داون",
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json"
  ],
  "files": [
    {
      "path": "registry/kashi/DropdownMenu.tsx",
      "type": "registry:ui",
      "target": "components/kashi/DropdownMenu.tsx",
      "content": "\"use client\";\n\nimport {\n  Children, cloneElement, createContext, isValidElement, useContext, useEffect, useId, useRef, useState,\n  type KeyboardEvent, type ReactElement, type ReactNode,\n} from \"react\";\nimport { cx } from \"./cx\";\n\nexport const MenuCtx = createContext<{ close: (focusTrigger?: boolean) => void } | null>(null);\n\nexport interface DropdownMenuProps {\n  /** دکمه‌ای که منو را باز می‌کند */\n  trigger: ReactElement;\n  /** نام منو برای صفحه‌خوان؛ پیش‌فرض متن دکمه */\n  label?: string;\n  /** هم‌ترازی با لبهٔ ابتدا یا انتهای دکمه @default \"start\" */\n  align?: \"start\" | \"end\";\n  children: ReactNode;\n}\n\nexport const items = (menu: HTMLElement | null) =>\n  [...(menu?.querySelectorAll<HTMLElement>('[role^=\"menuitem\"]:not([aria-disabled=\"true\"])') ?? [])];\n\n/**\n * منوی کشویی با الگوی Menu Button در WAI-ARIA:\n * Enter، Space یا ↓ باز می‌کند و اولین گزینه را فوکوس می‌کند، ↑ آخرین را؛\n * ↑↓ و Home/End حرکت، حرف اول برای پرش، Esc بستن و برگشت به دکمه، Tab بستن.\n */\nexport function DropdownMenu({ trigger, label, align = \"start\", children }: DropdownMenuProps) {\n  const id = useId();\n  const [open, setOpen] = useState(false);\n  const focusOnOpen = useRef<\"first\" | \"last\">(\"first\");\n  const triggerRef = useRef<HTMLElement>(null);\n  const menuRef = useRef<HTMLUListElement>(null);\n  const anchorRef = useRef<HTMLSpanElement>(null);\n\n  const close = (focusTrigger = true) => {\n    setOpen(false);\n    if (focusTrigger) triggerRef.current?.focus();\n  };\n\n  useEffect(() => {\n    if (!open) return;\n    const list = items(menuRef.current);\n    (focusOnOpen.current === \"last\" ? list[list.length - 1] : list[0])?.focus();\n    const onDown = (e: PointerEvent) => {\n      if (!anchorRef.current?.contains(e.target as Node)) setOpen(false);\n    };\n    document.addEventListener(\"pointerdown\", onDown);\n    return () => document.removeEventListener(\"pointerdown\", onDown);\n  }, [open]);\n\n  const openWith = (where: \"first\" | \"last\") => {\n    focusOnOpen.current = where;\n    setOpen(true);\n  };\n\n  const onMenuKey = (e: KeyboardEvent<HTMLUListElement>) => menuKeyDown(e, menuRef.current, close, () => setOpen(false));\n\n  const t = isValidElement<Record<string, unknown>>(trigger)\n    ? cloneElement(trigger, {\n        ref: triggerRef,\n        id: `${id}-trigger`,\n        \"aria-haspopup\": \"menu\",\n        \"aria-expanded\": open,\n        \"aria-controls\": open ? `${id}-menu` : undefined,\n        onClick: () => (open ? close(false) : openWith(\"first\")),\n        onKeyDown: (e: KeyboardEvent) => {\n          if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n            e.preventDefault();\n            openWith(e.key === \"ArrowUp\" ? \"last\" : \"first\");\n          }\n        },\n      })\n    : trigger;\n\n  return (\n    <span className=\"k-menu-anchor\" ref={anchorRef}>\n      {t}\n      <MenuCtx.Provider value={{ close }}>\n        <ul\n          ref={menuRef}\n          id={`${id}-menu`}\n          role=\"menu\"\n          aria-label={label}\n          aria-labelledby={label ? undefined : `${id}-trigger`}\n          className={cx(\"k-menu\", align === \"end\" && \"k-menu--end\")}\n          hidden={!open}\n          onKeyDown={onMenuKey}\n        >\n          {Children.map(children, (c) => c)}\n        </ul>\n      </MenuCtx.Provider>\n    </span>\n  );\n}\n\n/** پیمایش کیبورد مشترک منوها: ↑↓، Home/End، حرف اول، Esc و Tab */\nexport function menuKeyDown(e: KeyboardEvent<HTMLElement>, menu: HTMLElement | null, close: () => void, dismiss: () => void) {\n  const list = items(menu);\n  const i = list.indexOf(document.activeElement as HTMLElement);\n  const go = (j: number) => list[(j + list.length) % list.length]?.focus();\n  if (e.key === \"ArrowDown\") go(i + 1);\n  else if (e.key === \"ArrowUp\") go(i - 1);\n  else if (e.key === \"Home\") go(0);\n  else if (e.key === \"End\") go(list.length - 1);\n  else if (e.key === \"Escape\") close();\n  else if (e.key === \"Tab\") return dismiss();\n  else if (e.key.length === 1 && /\\S/.test(e.key)) {\n    // پرش با حرف اول\n    const k = e.key.toLowerCase();\n    const start = i + 1;\n    const hit = [...list.slice(start), ...list.slice(0, start)].find((el) => el.textContent?.trim().toLowerCase().startsWith(k));\n    hit?.focus();\n  } else return;\n  e.preventDefault();\n}\n\nexport interface MenuItemProps {\n  onSelect?: () => void;\n  icon?: ReactNode;\n  /** میان‌بر نمایشی، مثل \"Ctrl+D\" */\n  shortcut?: string;\n  /** «danger» برای عملیات مخرب */\n  tone?: \"default\" | \"danger\";\n  disabled?: boolean;\n  /** لینک به‌جای عمل */\n  href?: string;\n  /** گزینهٔ تیک‌دار (menuitemcheckbox)، مثل «نمایش ستون تاریخ»؛ منو پس از انتخاب باز می‌ماند */\n  checked?: boolean;\n  children: ReactNode;\n}\n\nexport function MenuItem({ onSelect, icon, shortcut, tone = \"default\", disabled, href, checked, children }: MenuItemProps) {\n  const checkable = checked !== undefined;\n  const ctx = useContext(MenuCtx);\n  const cls = cx(\"k-menu__item\", tone === \"danger\" && \"k-menu__item--danger\");\n  const content = (\n    <>\n      {checkable && (\n        <span className=\"k-menu__check\" aria-hidden=\"true\">\n          {checked && <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"m5 10.5 3.2 3L15 7\" /></svg>}\n        </span>\n      )}\n      {icon}\n      <span>{children}</span>\n      {shortcut && <span className=\"k-menu__shortcut\" aria-hidden=\"true\">{shortcut}</span>}\n    </>\n  );\n  const select = () => {\n    if (disabled) return;\n    onSelect?.();\n    if (!checkable) ctx?.close();\n  };\n  return (\n    <li role=\"none\">\n      {href && !disabled ? (\n        <a role=\"menuitem\" tabIndex={-1} className={cls} href={href} onClick={() => ctx?.close(false)}>{content}</a>\n      ) : (\n        <button type=\"button\" role={checkable ? \"menuitemcheckbox\" : \"menuitem\"} aria-checked={checkable ? checked : undefined} tabIndex={-1} className={cls} aria-disabled={disabled || undefined} onClick={select}>{content}</button>\n      )}\n    </li>\n  );\n}\n\nexport const MenuSeparator = () => <li role=\"separator\" className=\"k-menu__separator\" />;\nexport const MenuLabel = ({ children }: { children: ReactNode }) => <li role=\"presentation\" className=\"k-menu__label\">{children}</li>;\n"
    }
  ]
}