{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tooltip",
  "type": "registry:ui",
  "title": "Tooltip",
  "description": "تولتیپ",
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/kashi/Tooltip.tsx",
      "type": "registry:ui",
      "target": "components/kashi/Tooltip.tsx",
      "content": "\"use client\";\n\nimport { cloneElement, isValidElement, useEffect, useId, useRef, useState, type ReactElement, type ReactNode } from \"react\";\nimport { cx } from \"./cx\";\nimport { placeTip, unplaceTip, useIsomorphicLayoutEffect } from \"./utils\";\n\nexport interface TooltipProps {\n  /** متن راهنما؛ فقط توضیح تکمیلی، نه اطلاعات ضروری */\n  content: ReactNode;\n  /** عنصر فوکوس‌پذیر، معمولاً یک دکمه */\n  children: ReactElement;\n  /** «start»/«end» کنار عنصر؛ در RTL «start» سمت راست است @default \"top\" */\n  placement?: \"top\" | \"bottom\" | \"start\" | \"end\";\n  /** تأخیر نمایش با hover بر حسب میلی‌ثانیه @default 300 */\n  delay?: number;\n}\n\n/**\n * راهنمای کوتاه طبق WCAG 1.4.13: با hover و فوکوس نمایش داده می‌شود، روی خودش هم hover پذیر است\n * و با Esc بسته می‌شود. با aria-describedby به عنصر متصل است.\n * در لایهٔ بالایی مرورگر نمایش داده می‌شود، پس overflow: hidden والدها (مثل کارت) آن را نمی‌برد،\n * و اگر سمت خواسته‌شده جا نداشته باشد به سمت مقابل می‌رود.\n */\nexport function Tooltip({ content, children, placement = \"top\", delay = 300 }: TooltipProps) {\n  const id = useId();\n  const [open, setOpen] = useState(false);\n  const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  const anchor = useRef<HTMLSpanElement>(null);\n  const tip = useRef<HTMLSpanElement>(null);\n\n  const show = (wait: number) => {\n    clearTimeout(timer.current);\n    timer.current = setTimeout(() => setOpen(true), wait);\n  };\n  const hide = () => {\n    clearTimeout(timer.current);\n    timer.current = setTimeout(() => setOpen(false), 100);\n  };\n\n  useEffect(() => {\n    if (!open) return;\n    const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setOpen(false);\n    document.addEventListener(\"keydown\", onKey);\n    return () => document.removeEventListener(\"keydown\", onKey);\n  }, [open]);\n  useEffect(() => () => clearTimeout(timer.current), []);\n\n  useIsomorphicLayoutEffect(() => {\n    const a = anchor.current, t = tip.current;\n    if (!a || !t) return;\n    if (!open) return unplaceTip(t);\n    const place = () => placeTip(a, t, placement);\n    place();\n    addEventListener(\"scroll\", place, { capture: true, passive: true });\n    addEventListener(\"resize\", place);\n    return () => {\n      removeEventListener(\"scroll\", place, { capture: true });\n      removeEventListener(\"resize\", place);\n      unplaceTip(t);\n    };\n  }, [open, placement, content]);\n\n  const child = isValidElement<{ \"aria-describedby\"?: string }>(children)\n    ? cloneElement(children, {\n        \"aria-describedby\": [children.props[\"aria-describedby\"], id].filter(Boolean).join(\" \"),\n      })\n    : children;\n\n  return (\n    <span\n      ref={anchor}\n      className=\"k-tooltip-anchor\"\n      onMouseEnter={() => show(delay)}\n      onMouseLeave={hide}\n      onFocus={() => show(0)}\n      onBlur={hide}\n    >\n      {child}\n      <span\n        ref={tip}\n        // لایهٔ بالایی (Popover API)؛ والدِ دارای overflow یا transform راهنما را نمی‌برد\n        {...({ popover: \"manual\" } as Record<string, string>)}\n        role=\"tooltip\"\n        id={id} className={cx(\"k-tooltip\", placement !== \"top\" && `k-tooltip--${placement}`)} hidden={!open}>\n        {content}\n      </span>\n    </span>\n  );\n}\n"
    }
  ]
}