{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "popover",
  "type": "registry:ui",
  "title": "Popover",
  "description": "پاپ‌اور",
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json"
  ],
  "files": [
    {
      "path": "registry/kashi/Popover.tsx",
      "type": "registry:ui",
      "target": "components/kashi/Popover.tsx",
      "content": "\"use client\";\n\nimport { cloneElement, isValidElement, useEffect, useId, useRef, useState, type ReactElement, type ReactNode } from \"react\";\nimport { cx } from \"./cx\";\n\nexport interface PopoverProps {\n  /** دکمه‌ای که پنجره را باز می‌کند */\n  trigger: ReactElement;\n  title?: ReactNode;\n  children: ReactNode;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** @default \"bottom-start\" */\n  placement?: \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n  /** نام پنجره اگر title ندارد */\n  \"aria-label\"?: string;\n  className?: string;\n}\n\nconst FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * پنجرهٔ شناور غیرمودال کنار یک دکمه؛ برای فرم کوتاه، فیلتر یا توضیح.\n * فوکوس به داخل می‌رود، Esc و کلیک بیرون می‌بندد و فوکوس به دکمه برمی‌گردد.\n */\nexport function Popover({ trigger, title, children, open, onOpenChange, placement = \"bottom-start\", \"aria-label\": ariaLabel, className }: PopoverProps) {\n  const id = useId();\n  const [inner, setInner] = useState(false);\n  const isOpen = open ?? inner;\n  const anchor = useRef<HTMLSpanElement>(null);\n  const panel = useRef<HTMLDivElement>(null);\n  const triggerRef = useRef<HTMLElement>(null);\n\n  const set = (v: boolean, focusTrigger = false) => {\n    if (open === undefined) setInner(v);\n    onOpenChange?.(v);\n    if (!v && focusTrigger) triggerRef.current?.focus();\n  };\n\n  useEffect(() => {\n    if (!isOpen) return;\n    const p = panel.current;\n    (p?.querySelector<HTMLElement>(FOCUSABLE) ?? p)?.focus();\n    const onDown = (e: PointerEvent) => {\n      if (!anchor.current?.contains(e.target as Node)) set(false);\n    };\n    document.addEventListener(\"pointerdown\", onDown);\n    return () => document.removeEventListener(\"pointerdown\", onDown);\n  }, [isOpen]);\n\n  const t = isValidElement<Record<string, unknown>>(trigger)\n    ? cloneElement(trigger, {\n        ref: triggerRef,\n        \"aria-haspopup\": \"dialog\",\n        \"aria-expanded\": isOpen,\n        \"aria-controls\": isOpen ? `${id}-panel` : undefined,\n        onClick: (e: unknown) => {\n          (trigger.props.onClick as ((e: unknown) => void) | undefined)?.(e);\n          set(!isOpen);\n        },\n      })\n    : trigger;\n\n  const [side, align] = placement.split(\"-\");\n  return (\n    <span className=\"k-popover-anchor\" ref={anchor}>\n      {t}\n      <div\n        ref={panel}\n        id={`${id}-panel`}\n        role=\"dialog\"\n        aria-labelledby={title ? `${id}-title` : undefined}\n        aria-label={title ? undefined : ariaLabel}\n        tabIndex={-1}\n        hidden={!isOpen}\n        className={cx(\"k-popover\", side === \"top\" && \"k-popover--top\", align === \"end\" && \"k-popover--end\", className)}\n        onKeyDown={(e) => {\n          if (e.key === \"Escape\") {\n            e.stopPropagation();\n            set(false, true);\n          }\n        }}\n        onBlur={(e) => {\n          const next = e.relatedTarget as Node | null;\n          if (next && !anchor.current?.contains(next)) set(false);\n        }}\n      >\n        {title && <p className=\"k-popover__title\" id={`${id}-title`}>{title}</p>}\n        <div className=\"k-popover__body\">{children}</div>\n      </div>\n    </span>\n  );\n}\n"
    }
  ]
}