{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tabs",
  "type": "registry:ui",
  "title": "Tabs، TabList، Tab، TabPanel",
  "description": "زبانه‌ها با الگوی Tabs در WAI-ARIA؛ کلیدهای جهت‌دار در راست‌به‌چپ برعکس عمل می‌کنند",
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/kashi/Tabs.tsx",
      "type": "registry:ui",
      "target": "components/kashi/Tabs.tsx",
      "content": "\"use client\";\n\nimport { createContext, useContext, useId, useRef, useState, type HTMLAttributes, type KeyboardEvent, type ReactNode } from \"react\";\nimport { cx } from \"./cx\";\nimport { useIsomorphicLayoutEffect } from \"./utils\";\n\ninterface Ctx { id: string; selected: string; select: (v: string) => void; orientation: \"horizontal\" | \"vertical\" }\nconst TabsCtx = createContext<Ctx | null>(null);\nconst useTabs = () => {\n  const ctx = useContext(TabsCtx);\n  if (!ctx) throw new Error(\"[kashi] Tab و TabPanel باید داخل <Tabs> باشند.\");\n  return ctx;\n};\n\nexport interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\" | \"defaultValue\"> {\n  value?: string;\n  defaultValue?: string;\n  onChange?: (value: string) => void;\n  /** @default \"line\" */\n  variant?: \"line\" | \"pill\" | \"segmented\";\n  /** زبانه‌ها تمام عرض را پر کنند */\n  block?: boolean;\n  /** «vertical»: زبانه‌ها در ستون کناری، مثل صفحهٔ تنظیمات @default \"horizontal\" */\n  orientation?: \"horizontal\" | \"vertical\";\n  /** @default \"md\" */\n  size?: \"sm\" | \"md\";\n}\n\n/** زبانه‌ها با الگوی Tabs در WAI-ARIA؛ کلیدهای جهت‌دار در راست‌به‌چپ برعکس عمل می‌کنند */\nexport function Tabs({ value, defaultValue = \"\", onChange, variant = \"line\", block, orientation = \"horizontal\", size = \"md\", className, children, ...rest }: TabsProps) {\n  const id = useId();\n  const [inner, setInner] = useState(defaultValue);\n  const selected = value !== undefined ? value : inner;\n  return (\n    <TabsCtx.Provider\n      value={{\n        id,\n        selected,\n        orientation,\n        select: (v) => {\n          if (value === undefined) setInner(v);\n          onChange?.(v);\n        },\n      }}\n    >\n      <div\n        className={cx(\"k-tabs\", `k-tabs--${variant}`, block && \"k-tabs--block\", orientation === \"vertical\" && \"k-tabs--vertical\", size === \"sm\" && \"k-tabs--sm\", className)}\n        data-orientation={orientation}\n        {...rest}\n      >\n        {children}\n      </div>\n    </TabsCtx.Provider>\n  );\n}\n\nexport interface TabListProps extends HTMLAttributes<HTMLDivElement> {\n  /** نام فهرست برای صفحه‌خوان، مثل «تنظیمات حساب» */\n  \"aria-label\": string;\n}\n\nexport function TabList({ className, children, ...rest }: TabListProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const { orientation, selected } = useTabs();\n  // نشانگر زبانهٔ فعال که با تغییر زبانه می‌لغزد؛ تا اندازه‌گیری نشده، سبک ایستای CSS نمایش داده می‌شود\n  const [box, setBox] = useState<{ x: number; y: number; w: number; h: number } | null>(null);\n  useIsomorphicLayoutEffect(() => {\n    const list = ref.current;\n    if (!list) return;\n    const measure = () => {\n      const tab = list.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]');\n      if (!tab) return setBox(null);\n      setBox({ x: tab.offsetLeft, y: tab.offsetTop, w: tab.offsetWidth, h: tab.offsetHeight });\n    };\n    measure();\n    const ro = new ResizeObserver(measure);\n    ro.observe(list);\n    return () => ro.disconnect();\n  }, [selected]);\n  const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n    const tabs = [...(ref.current?.querySelectorAll<HTMLButtonElement>('[role=\"tab\"]:not(:disabled)') ?? [])];\n    const i = tabs.indexOf(document.activeElement as HTMLButtonElement);\n    if (i < 0) return;\n    const rtl = getComputedStyle(ref.current!).direction === \"rtl\";\n    const moves: Record<string, number> =\n      orientation === \"vertical\"\n        ? { ArrowDown: i + 1, ArrowUp: i - 1, Home: 0, End: tabs.length - 1 }\n        : { ArrowLeft: rtl ? i + 1 : i - 1, ArrowRight: rtl ? i - 1 : i + 1, Home: 0, End: tabs.length - 1 };\n    if (!(e.key in moves)) return;\n    e.preventDefault();\n    const next = tabs[(moves[e.key] + tabs.length) % tabs.length];\n    next.focus();\n    next.click(); // فعال‌سازی خودکار با فوکوس\n  };\n  return (\n    <div\n      ref={ref}\n      role=\"tablist\"\n      aria-orientation={orientation}\n      className={cx(\"k-tabs__list\", className)}\n      data-indicator={box ? \"\" : undefined}\n      style={box ? ({ \"--_ix\": `${box.x}px`, \"--_iy\": `${box.y}px`, \"--_iw\": `${box.w}px`, \"--_ih\": `${box.h}px` } as React.CSSProperties) : undefined}\n      onKeyDown={onKeyDown}\n      {...rest}\n    >\n      {children}\n      {box && <span className=\"k-tabs__indicator\" aria-hidden=\"true\" />}\n    </div>\n  );\n}\n\nexport interface TabProps extends Omit<HTMLAttributes<HTMLButtonElement>, \"value\"> {\n  value: string;\n  disabled?: boolean;\n}\n\nexport function Tab({ value, className, children, disabled, ...rest }: TabProps) {\n  const { id, selected, select } = useTabs();\n  const on = selected === value;\n  return (\n    <button\n      type=\"button\"\n      role=\"tab\"\n      id={`${id}-tab-${value}`}\n      aria-selected={on}\n      aria-controls={`${id}-panel-${value}`}\n      tabIndex={on ? 0 : -1}\n      disabled={disabled}\n      className={cx(\"k-tabs__tab\", className)}\n      onClick={() => select(value)}\n      {...rest}\n    >\n      {children}\n    </button>\n  );\n}\n\nexport interface TabPanelProps extends HTMLAttributes<HTMLDivElement> {\n  value: string;\n  children: ReactNode;\n}\n\nexport function TabPanel({ value, className, children, ...rest }: TabPanelProps) {\n  const { id, selected } = useTabs();\n  return (\n    <div\n      role=\"tabpanel\"\n      id={`${id}-panel-${value}`}\n      aria-labelledby={`${id}-tab-${value}`}\n      hidden={selected !== value}\n      tabIndex={0}\n      className={cx(\"k-tabs__panel\", className)}\n      {...rest}\n    >\n      {children}\n    </div>\n  );\n}\n"
    }
  ]
}