{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart",
  "type": "registry:ui",
  "title": "ChartTip، BarChart، BarList، ProportionBar، PieChart، Sparkline، LineChart",
  "description": "راهنمای شناور مشترک نمودارها؛ x و y نسبت به ظرف نمودار",
  "dependencies": [
    "@kashi-ui/persian@^1.7.0"
  ],
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json"
  ],
  "files": [
    {
      "path": "registry/kashi/Chart.tsx",
      "type": "registry:ui",
      "target": "components/kashi/Chart.tsx",
      "content": "\"use client\";\n\nimport { useId, useLayoutEffect, useRef, useState, type CSSProperties, type KeyboardEvent, type ReactNode } from \"react\";\nimport { formatNumber } from \"@kashi-ui/persian\";\nimport { cx } from \"./cx\";\nimport { useKashi } from \"./KashiProvider\";\n\n/** متن‌ها و قالب عددهای نمودار به زبان و ارقام KashiProvider */\nfunction useChartText() {\n  const { messages: M, digits } = useKashi();\n  const num = (n: number) => formatNumber(n, { digits });\n  const dec = (x: number) => num(Math.round(x * 10) / 10).replace(\".\", M.common.decimal);\n  return {\n    M,\n    fmt: (n: number) => num(Math.round(n)),\n    pct: (v: number, total: number) => M.common.percent(dec((v / total) * 100)),\n    /** عدد کوتاه برای محور: ۱٬۲۰۰٬۰۰۰ ← ۱٫۲ م */\n    compact: (n: number) => {\n      const a = Math.abs(n);\n      if (a >= 1e9) return dec(n / 1e9) + M.chart.billion;\n      if (a >= 1e6) return dec(n / 1e6) + M.chart.million;\n      if (a >= 1e3) return dec(n / 1e3) + M.chart.thousand;\n      return num(n);\n    },\n  };\n}\n/** گام‌های گرد محور: ۰، ۲۵، ۵۰… */\nfunction niceTicks(max: number, count = 4): number[] {\n  if (max <= 0) return [0];\n  const raw = max / count;\n  const mag = 10 ** Math.floor(Math.log10(raw));\n  const step = [1, 2, 2.5, 5, 10].map((m) => m * mag).find((s) => s >= raw)!;\n  return Array.from({ length: Math.ceil(max / step) + 1 }, (_, i) => i * step);\n}\n\nexport interface TipRow { name?: string; value: string; color?: string }\n/** راهنمای شناور مشترک نمودارها؛ x و y نسبت به ظرف نمودار */\nexport function ChartTip({ x, y, title, rows, width }: { x: number; y: number; title?: ReactNode; rows: TipRow[]; width?: number }) {\n  // داخل ظرف بماند\n  const cx = width ? Math.min(Math.max(x, 80), width - 80) : x;\n  return (\n    <div className=\"k-chart__tip\" style={{ left: cx, top: y }} aria-hidden=\"true\">\n      {title && <span className=\"k-chart__tip-title\">{title}</span>}\n      {rows.map((r, i) => (\n        <span key={i} className=\"k-chart__tip-row\" style={r.color ? ({ \"--_c\": r.color } as CSSProperties) : undefined}>\n          {r.name !== undefined && <span>{r.color && <i />}{r.name}</span>}\n          <strong>{r.value}</strong>\n        </span>\n      ))}\n    </div>\n  );\n}\n\ntype Tip = { x: number; y: number; i: number } | null;\n/** موقعیت راهنما بالای یک عنصر، نسبت به ظرف */\nconst tipAt = (el: HTMLElement, box: HTMLElement | null, i: number, anchor?: HTMLElement | null): Tip => {\n  if (!box) return null;\n  const b = box.getBoundingClientRect();\n  const r = (anchor ?? el).getBoundingClientRect();\n  return { x: r.left + r.width / 2 - b.left, y: r.top - b.top, i };\n};\n\n/** جدول هم‌ارز برای صفحه‌خوان و چاپ */\nfunction DataTable({ caption, labels, series, format }: { caption: string; labels: string[]; series: { name: string; values: number[] }[]; format: (n: number) => string }) {\n  const { messages: M } = useKashi();\n  return (\n    <table className=\"k-sr-only\">\n      <caption>{caption}</caption>\n      <thead><tr><th scope=\"col\">{M.chart.period}</th>{series.map((s) => <th key={s.name} scope=\"col\">{s.name}</th>)}</tr></thead>\n      <tbody>{labels.map((l, i) => <tr key={l}><th scope=\"row\">{l}</th>{series.map((s) => <td key={s.name}>{format(s.values[i])}</td>)}</tr>)}</tbody>\n    </table>\n  );\n}\n\nexport interface BarChartProps {\n  /** عنوان نمودار؛ نام دسترس‌پذیر هم هست */\n  title: string;\n  /** یک سری */\n  data?: { label: string; value: number }[];\n  /** یا چند سری روی هم (ستون انباشته)، حداکثر چهار؛ با labels */\n  series?: LineSeries[];\n  labels?: string[];\n  /** قالب مقدار در راهنما و برچسب @default ارقام فارسی با جداکننده */\n  format?: (n: number) => string;\n  /** برچسب مقدار روی بلندترین ستون @default true */\n  labelMax?: boolean;\n  height?: string;\n  className?: string;\n}\n\n/** نمودار ستونی؛ تک‌سری یا انباشته تا چهار سری. با موس یا Tab روی هر ستون مقدار دقیق نمایش داده می‌شود */\nexport function BarChart({ title, data, series, labels, format, labelMax = true, height, className }: BarChartProps) {\n  const T = useChartText();\n  const M = T.M;\n  format ??= T.fmt;\n  const id = useId();\n  const stacked = series?.slice(0, 4);\n  const cats = stacked ? labels ?? [] : (data ?? []).map((d) => d.label);\n  const totals = cats.map((_, i) => (stacked ? stacked.reduce((n, s) => n + (s.values[i] ?? 0), 0) : data![i].value));\n  const max = Math.max(0, ...totals);\n  const ticks = niceTicks(max);\n  const top = ticks[ticks.length - 1] || 1;\n  const maxIndex = totals.indexOf(max);\n  const color = (i: number) => ({ \"--_c\": `var(--k-chart-${i + 1})` }) as CSSProperties;\n  const describe = (i: number) =>\n    stacked ? `${cats[i]}: ${stacked.map((s) => `${s.name} ${format!(s.values[i] ?? 0)}`).join(M.common.list)}${M.common.list}${M.common.total} ${format!(totals[i])}` : `${cats[i]}: ${format(totals[i])}`;\n  const box = useRef<HTMLDivElement>(null);\n  const [tip, setTip] = useState<Tip>(null);\n  const show = (e: { currentTarget: HTMLElement }, i: number) => setTip(tipAt(e.currentTarget, box.current, i, e.currentTarget.querySelector<HTMLElement>(\".k-bars__bar, .k-bars__stack\")));\n  return (\n    <figure className={cx(\"k-chart k-chart--bar\", className)} aria-labelledby={`${id}-t`} style={{ margin: 0, ...(height ? ({ \"--k-chart-height\": height } as CSSProperties) : {}) }}>\n      <figcaption className=\"k-chart__head\">\n        <p className=\"k-chart__title\" id={`${id}-t`}>{title}</p>\n        {stacked && stacked.length > 1 && (\n          <ul className=\"k-chart__legend\" aria-hidden=\"true\">\n            {stacked.map((s, i) => <li key={s.name} style={color(i)}><span className=\"k-chart__key k-chart__key--box\" />{s.name}</li>)}\n          </ul>\n        )}\n      </figcaption>\n      <div className=\"k-chart__plot\" ref={box}>\n        <div className=\"k-bars__grid\" aria-hidden=\"true\">{ticks.map((t) => <span key={t} data-v={T.compact(t)} />)}</div>\n        <div className=\"k-bars\" role=\"list\" aria-label={title}>\n          {cats.map((label, i) => (\n            <div\n              key={label}\n              className=\"k-bars__col\"\n              role=\"listitem\"\n              tabIndex={0}\n              aria-label={describe(i)}\n              data-active={tip?.i === i ? \"\" : undefined}\n              style={{ \"--_d\": `${i * 40}ms` } as CSSProperties}\n              onPointerEnter={(e) => show(e, i)}\n              onPointerLeave={() => setTip(null)}\n              onFocus={(e) => show(e, i)}\n              onBlur={() => setTip(null)}\n            >\n              {labelMax && i === maxIndex && <span className=\"k-bars__value\" aria-hidden=\"true\">{format(totals[i])}</span>}\n              {stacked ? (\n                <span className=\"k-bars__stack\" style={{ blockSize: `${(totals[i] / top) * 100}%` }} aria-hidden=\"true\">\n                  {stacked.map((s, si) => (\n                    <span key={s.name} className=\"k-bars__seg\" style={{ ...color(si), flexGrow: s.values[i] ?? 0 }} />\n                  ))}\n                </span>\n              ) : (\n                <span className=\"k-bars__bar\" style={{ blockSize: `${(totals[i] / top) * 100}%` }} aria-hidden=\"true\" />\n              )}\n              <span className=\"k-bars__label\" aria-hidden=\"true\">{label}</span>\n            </div>\n          ))}\n        </div>\n        {tip && (\n          <ChartTip\n            x={tip.x}\n            y={tip.y}\n            width={box.current?.clientWidth}\n            title={cats[tip.i]}\n            rows={stacked\n              ? [...stacked.map((s, si) => ({ name: s.name, value: format(s.values[tip.i] ?? 0), color: `var(--k-chart-${si + 1})` })), { name: M.common.total, value: format(totals[tip.i]) }]\n              : [{ value: format(totals[tip.i]) }]}\n          />\n        )}\n      </div>\n    </figure>\n  );\n}\n\nexport interface BarListProps {\n  title: string;\n  /** به ترتیب داده‌شده؛ معمولاً از بیشترین به کمترین */\n  data: { label: string; value: number; href?: string }[];\n  format?: (n: number) => string;\n  className?: string;\n}\n\n/** میله‌های افقی برای رتبه‌بندی با نام‌های بلند، مثل پربازدیدترین صفحه‌ها */\nexport function BarList({ title, data, format, className }: BarListProps) {\n  const T = useChartText();\n  const M = T.M;\n  format ??= T.fmt;\n  const id = useId();\n  const max = Math.max(1, ...data.map((d) => d.value));\n  const total = data.reduce((n, d) => n + d.value, 0) || 1;\n  const share = (v: number) => T.pct(v, total);\n  const box = useRef<HTMLOListElement>(null);\n  const [tip, setTip] = useState<Tip>(null);\n  return (\n    <figure className={cx(\"k-chart\", className)} aria-labelledby={`${id}-t`} style={{ margin: 0 }}>\n      <figcaption className=\"k-chart__head\"><p className=\"k-chart__title\" id={`${id}-t`}>{title}</p></figcaption>\n      <ol className=\"k-barlist\" ref={box} style={{ position: \"relative\" }}>\n        {data.map((d, i) => (\n          <li\n            key={d.label}\n            className=\"k-barlist__row\"\n            data-active={tip?.i === i ? \"\" : undefined}\n            style={{ \"--_d\": `${i * 60}ms` } as CSSProperties}\n            onPointerEnter={(e) => setTip(tipAt(e.currentTarget, box.current, i, e.currentTarget.querySelector<HTMLElement>(\".k-barlist__fill\")))}\n            onPointerLeave={() => setTip(null)}\n          >\n            <span className=\"k-barlist__track\">\n              <span className=\"k-barlist__fill\" style={{ inlineSize: `${(d.value / max) * 100}%` }} aria-hidden=\"true\" />\n              {d.href ? <a className=\"k-barlist__label\" href={d.href}>{d.label}</a> : <span className=\"k-barlist__label\">{d.label}</span>}\n            </span>\n            <span className=\"k-barlist__value\">{format(d.value)} <span className=\"k-barlist__share\">{share(d.value)}</span></span>\n          </li>\n        ))}\n        {tip && <ChartTip x={tip.x} y={tip.y} width={box.current?.clientWidth} title={data[tip.i].label} rows={[{ name: M.chart.value, value: format(data[tip.i].value) }, { name: M.chart.share, value: share(data[tip.i].value) }]} />}\n      </ol>\n    </figure>\n  );\n}\n\nexport interface ProportionBarProps {\n  title: string;\n  /** حداکثر چهار بخش؛ بیشتر را در «سایر» جمع کنید */\n  data: { label: string; value: number }[];\n  /** نمایش درصد کنار هر بخش در راهنما @default true */\n  percent?: boolean;\n  format?: (n: number) => string;\n  className?: string;\n}\n\n/** سهم هر بخش از کل در یک میلهٔ افقی، مثل منبع ورود کاربران؛ جایگزین خواناتر نمودار دایره‌ای */\nexport function ProportionBar({ title, data, percent = true, format, className }: ProportionBarProps) {\n  const T = useChartText();\n  const M = T.M;\n  format ??= T.fmt;\n  const id = useId();\n  const parts = data.slice(0, 4);\n  const total = parts.reduce((n, d) => n + d.value, 0) || 1;\n  const pct = (v: number) => T.pct(v, total);\n  const color = (i: number) => ({ \"--_c\": `var(--k-chart-${i + 1})` }) as CSSProperties;\n  const box = useRef<HTMLDivElement>(null);\n  const [tip, setTip] = useState<Tip>(null);\n  return (\n    <figure className={cx(\"k-chart\", className)} aria-labelledby={`${id}-t`} style={{ margin: 0 }}>\n      <figcaption className=\"k-chart__head\"><p className=\"k-chart__title\" id={`${id}-t`}>{title}</p></figcaption>\n      <div className=\"k-proportion__wrap\" ref={box}>\n        <div className=\"k-proportion\" aria-hidden=\"true\">\n          {parts.map((d, i) => (\n            <span\n              key={d.label}\n              className=\"k-proportion__seg\"\n              data-active={tip?.i === i ? \"\" : undefined}\n              style={{ ...color(i), flexGrow: d.value }}\n              onPointerEnter={(e) => setTip(tipAt(e.currentTarget, box.current, i))}\n              onPointerLeave={() => setTip(null)}\n            />\n          ))}\n        </div>\n        {tip && <ChartTip x={tip.x} y={tip.y} width={box.current?.clientWidth} rows={[{ name: parts[tip.i].label, value: pct(parts[tip.i].value), color: `var(--k-chart-${tip.i + 1})` }, { name: M.chart.count, value: format(parts[tip.i].value) }]} />}\n      </div>\n      <ul className=\"k-proportion__legend\">\n        {parts.map((d, i) => (\n          <li key={d.label} style={color(i)} data-active={tip?.i === i ? \"\" : undefined}>\n            <span className=\"k-chart__key k-chart__key--box\" aria-hidden=\"true\" />\n            <span>{d.label}</span>\n            <strong>{percent ? pct(d.value) : format(d.value)}</strong>\n            {percent && <span className=\"k-proportion__abs\">{format(d.value)}</span>}\n          </li>\n        ))}\n      </ul>\n    </figure>\n  );\n}\n\nexport interface PieChartProps {\n  title: string;\n  /** بیش از پنج بخش در «سایر» جمع می‌شود */\n  data: { label: string; value: number }[];\n  /** «donut» با جمع کل در وسط @default \"donut\" */\n  variant?: \"donut\" | \"pie\";\n  format?: (n: number) => string;\n  /** برچسب وسط دونات @default «جمع» */\n  totalLabel?: string;\n  /** اندازهٔ نمودار به پیکسل @default 176 */\n  size?: number;\n  className?: string;\n}\n\n/**\n * نمودار دایره‌ای یا دونات تا پنج بخش، با فاصلهٔ کوچک بین بخش‌ها، راهنمای درصد و جدول هم‌ارز.\n * برای مقایسهٔ دقیق بخش‌ها ProportionBar خواناتر است.\n */\nexport function PieChart({ title, data, variant = \"donut\", format, totalLabel, size = 176, className }: PieChartProps) {\n  const T = useChartText();\n  const M = T.M;\n  format ??= T.fmt;\n  totalLabel ??= M.common.total;\n  const id = useId();\n  const sorted = [...data].sort((a, b) => b.value - a.value);\n  const parts = sorted.length > 5 ? [...sorted.slice(0, 4), { label: M.common.other, value: sorted.slice(4).reduce((n, d) => n + d.value, 0) }] : sorted;\n  const total = parts.reduce((n, d) => n + d.value, 0) || 1;\n  const pct = (v: number) => T.pct(v, total);\n  const slot = (i: number) => (i < 4 ? `var(--k-chart-${i + 1})` : \"var(--k-text-quaternary)\");\n  const donut = variant === \"donut\";\n  // دایره با stroke-dasharray: هر بخش یک کمان؛ برای pie، شعاع نصف و ضخامت برابر قطر\n  const R = 50, r = donut ? 40 : 25, w = donut ? 16 : 50;\n  const C = 2 * Math.PI * r;\n  const gap = parts.length > 1 ? (donut ? 1.6 : 0.8) : 0;\n  const [active, setActive] = useState<number | null>(null);\n  const box = useRef<HTMLDivElement>(null);\n  let acc = 0;\n  const arcs = parts.map((d) => {\n    const len = (d.value / total) * C;\n    const a = { start: acc, len };\n    acc += len;\n    return a;\n  });\n  const mid = (i: number) => {\n    const ang = ((arcs[i].start + arcs[i].len / 2) / C) * 2 * Math.PI - Math.PI / 2;\n    const rr = donut ? r : r * 1.3;\n    return { x: (R + rr * Math.cos(ang)) / (2 * R), y: (R + rr * Math.sin(ang)) / (2 * R) };\n  };\n  return (\n    <figure className={cx(\"k-chart k-pie\", className)} aria-labelledby={`${id}-t`} style={{ margin: 0 }}>\n      <figcaption className=\"k-chart__head\"><p className=\"k-chart__title\" id={`${id}-t`}>{title}</p></figcaption>\n      <div className=\"k-pie__body\">\n        <div className=\"k-pie__plot\" ref={box} style={{ inlineSize: size, blockSize: size }} onPointerLeave={() => setActive(null)}>\n          <svg viewBox=\"0 0 100 100\" className=\"k-pie__svg\" aria-hidden=\"true\">\n            {donut && <circle cx={R} cy={R} r={r} fill=\"none\" stroke=\"var(--k-bg-tertiary)\" strokeWidth={w} />}\n            {parts.map((d, i) => (\n              <circle\n                key={d.label}\n                className=\"k-pie__slice\"\n                data-active={active === i ? \"\" : undefined}\n                cx={R}\n                cy={R}\n                r={r}\n                fill=\"none\"\n                stroke={slot(i)}\n                strokeWidth={w}\n                strokeDasharray={`${Math.max(0, arcs[i].len - gap)} ${C}`}\n                strokeDashoffset={-arcs[i].start}\n                transform={`rotate(-90 ${R} ${R})`}\n                style={{ \"--_d\": `${i * 80}ms` } as CSSProperties}\n                onPointerEnter={() => setActive(i)}\n              />\n            ))}\n          </svg>\n          {donut && (\n            <div className=\"k-pie__center\" aria-hidden=\"true\">\n              <strong>{active === null ? format(total) : pct(parts[active].value)}</strong>\n              <span>{active === null ? totalLabel : parts[active].label}</span>\n            </div>\n          )}\n          {active !== null && !donut && (\n            <ChartTip x={mid(active).x * size} y={mid(active).y * size - 8} rows={[{ name: parts[active].label, value: pct(parts[active].value), color: slot(active) }, { name: M.chart.count, value: format(parts[active].value) }]} />\n          )}\n        </div>\n        <ul className=\"k-pie__legend\">\n          {parts.map((d, i) => (\n            <li key={d.label} style={{ \"--_c\": slot(i) } as CSSProperties} data-active={active === i ? \"\" : undefined} onPointerEnter={() => setActive(i)} onPointerLeave={() => setActive(null)}>\n              <span className=\"k-chart__key k-chart__key--box\" aria-hidden=\"true\" />\n              <span className=\"k-pie__name\">{d.label}</span>\n              <strong>{pct(d.value)}</strong>\n              <span className=\"k-pie__abs\">{format(d.value)}</span>\n            </li>\n          ))}\n        </ul>\n      </div>\n    </figure>\n  );\n}\n\nexport interface SparklineProps {\n  values: number[];\n  /** نام دسترس‌پذیر، مثل «روند فروش هفت روز اخیر: افزایشی» */\n  \"aria-label\"?: string;\n  /** رنگ خط @default برند */\n  tone?: \"brand\" | \"success\" | \"error\" | \"neutral\";\n  /** سطح کم‌رنگ زیر خط @default true */\n  area?: boolean;\n  className?: string;\n}\n\n/** خط روند کوچک برای کارت آمار و جدول؛ بدون محور، فقط شکل روند */\nexport function Sparkline({ values, \"aria-label\": ariaLabel, tone = \"brand\", area = true, className }: SparklineProps) {\n  const hi = Math.max(...values), lo = Math.min(...values);\n  // حداقل دامنه ۲۵٪ مقدار تا تغییر کوچک، زیگزاگ بزرگ دیده نشود\n  const span = Math.max(hi - lo, Math.abs(hi) * 0.25) || 1;\n  const min = (hi + lo) / 2 - span / 2;\n  const pts = values.map((v, i) => [(i / Math.max(1, values.length - 1)) * 100, 28 - ((v - min) / span) * 24] as const);\n  const d = pts.map(([x, y], i) => `${i ? \"L\" : \"M\"}${x},${y}`).join(\" \");\n  const c = tone === \"neutral\" ? \"var(--k-text-tertiary)\" : tone === \"brand\" ? \"var(--k-text-brand)\" : `var(--k-text-${tone})`;\n  return (\n    <svg className={cx(\"k-sparkline\", className)} viewBox=\"0 0 100 30\" preserveAspectRatio=\"none\" role={ariaLabel ? \"img\" : undefined} aria-label={ariaLabel} aria-hidden={ariaLabel ? undefined : true} style={{ color: c }}>\n      {area && <path d={`${d} L100,30 L0,30 Z`} fill=\"currentColor\" opacity={0.1} />}\n      <path d={d} fill=\"none\" stroke=\"currentColor\" strokeWidth={2} vectorEffect=\"non-scaling-stroke\" strokeLinejoin=\"round\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\nexport interface LineSeries {\n  name: string;\n  values: number[];\n}\n\nexport interface LineChartProps {\n  title: string;\n  /** برچسب محور افقی، مثل ماه‌ها */\n  labels: string[];\n  /** حداکثر چهار سری؛ رنگ‌ها به ترتیب ثابت */\n  series: LineSeries[];\n  format?: (n: number) => string;\n  /** سطح کم‌رنگ زیر خط (فقط برای یک سری) */\n  area?: boolean;\n  height?: string;\n  className?: string;\n}\n\n/**\n * نمودار خطی تا چهار سری: خط ۲ پیکسلی، برچسب نام در انتهای هر خط،\n * راهنمای عمودی با موس و ←→ روی کیبورد، و جدول هم‌ارز برای صفحه‌خوان.\n */\nexport function LineChart({ title, labels, series, format, area, height, className }: LineChartProps) {\n  const T = useChartText();\n  const M = T.M;\n  format ??= T.fmt;\n  const id = useId();\n  const plot = useRef<HTMLDivElement>(null);\n  const [size, setSize] = useState({ w: 600, h: 220, rtl: true });\n  const [active, setActive] = useState<number | null>(null);\n  const shown = series.slice(0, 4);\n\n  useLayoutEffect(() => {\n    const el = plot.current;\n    if (!el) return;\n    const measure = () => setSize({ w: el.clientWidth, h: el.clientHeight, rtl: getComputedStyle(el).direction === \"rtl\" });\n    measure();\n    const ro = new ResizeObserver(measure);\n    ro.observe(el);\n    return () => ro.disconnect();\n  }, []);\n\n  const pad = { top: 12, bottom: 24, start: 52, end: shown.length > 1 ? 64 : 16 };\n  const max = Math.max(0, ...shown.flatMap((s) => s.values));\n  const ticks = niceTicks(max);\n  const top = ticks[ticks.length - 1] || 1;\n  const innerW = Math.max(1, size.w - pad.start - pad.end);\n  const innerH = Math.max(1, size.h - pad.top - pad.bottom);\n  // محور زمان در جهت خواندن صفحه: در RTL اولین نقطه سمت راست\n  const x = (i: number) => {\n    const t = labels.length > 1 ? i / (labels.length - 1) : 0.5;\n    return size.rtl ? size.w - pad.start - t * innerW : pad.start + t * innerW;\n  };\n  const y = (v: number) => pad.top + innerH - (v / top) * innerH;\n  // SVG با جهت ltr رسم می‌شود تا text-anchor فیزیکی باشد؛ اعداد محور بیرون از ناحیهٔ نمودار\n  const tickX = size.rtl ? size.w - pad.start + 8 : pad.start - 8;\n  const anchor = size.rtl ? \"start\" : \"end\";\n  // برچسب انتهای خط‌ها روی هم نیفتند: دست‌کم ۱۴ پیکسل فاصلهٔ عمودی\n  const last = labels.length - 1;\n  const endY = (() => {\n    const order = shown.map((s, i) => ({ i, y: y(s.values[last]) })).sort((a, b) => a.y - b.y);\n    for (let k = 1; k < order.length; k++) if (order[k].y - order[k - 1].y < 14) order[k].y = order[k - 1].y + 14;\n    return Object.fromEntries(order.map((o) => [o.i, o.y]));\n  })();\n  const endX = (i: number) => x(i) + (size.rtl ? -8 : 8);\n  const color = (i: number) => ({ \"--_c\": `var(--k-chart-${i + 1})` }) as CSSProperties;\n\n  const pick = (clientX: number) => {\n    const r = plot.current!.getBoundingClientRect();\n    const px = clientX - r.left;\n    let best = 0;\n    labels.forEach((_, i) => { if (Math.abs(x(i) - px) < Math.abs(x(best) - px)) best = i; });\n    setActive(best);\n  };\n  const onKey = (e: KeyboardEvent) => {\n    const next = { ArrowLeft: size.rtl ? 1 : -1, ArrowRight: size.rtl ? -1 : 1 }[e.key];\n    if (next) {\n      e.preventDefault();\n      setActive((a) => Math.min(labels.length - 1, Math.max(0, (a ?? (next > 0 ? -1 : labels.length)) + next)));\n    } else if (e.key === \"Home\") setActive(0);\n    else if (e.key === \"End\") setActive(labels.length - 1);\n    else if (e.key === \"Escape\") setActive(null);\n  };\n\n  const step = Math.max(1, Math.ceil(labels.length / Math.max(2, Math.floor(innerW / 64))));\n  return (\n    <figure className={cx(\"k-chart\", className)} aria-labelledby={`${id}-t`} style={{ margin: 0, ...(height ? ({ \"--k-chart-height\": height } as CSSProperties) : {}) }}>\n      <figcaption className=\"k-chart__head\">\n        <p className=\"k-chart__title\" id={`${id}-t`}>{title}</p>\n        {shown.length > 1 && (\n          <ul className=\"k-chart__legend\" aria-hidden=\"true\">\n            {shown.map((s, i) => <li key={s.name} style={color(i)}><span className=\"k-chart__key\" />{s.name}</li>)}\n          </ul>\n        )}\n      </figcaption>\n      <div\n        ref={plot}\n        className=\"k-chart__plot\"\n        tabIndex={0}\n        role=\"img\"\n        aria-label={M.chart.keyboard(title)}\n        aria-describedby={`${id}-live`}\n        onPointerMove={(e) => pick(e.clientX)}\n        onPointerLeave={() => setActive(null)}\n        onKeyDown={onKey}\n        onBlur={() => setActive(null)}\n      >\n        <svg className=\"k-chart__svg\" aria-hidden=\"true\" direction=\"ltr\">\n          <g className=\"k-chart__grid\">\n            {ticks.map((t) => <line key={t} x1={pad.start - (size.rtl ? 0 : 0)} x2={size.w - pad.end} y1={y(t)} y2={y(t)} transform={size.rtl ? `translate(${pad.end - pad.start} 0)` : undefined} />)}\n          </g>\n          {ticks.map((t) => <text key={t} className=\"k-chart__tick\" x={tickX} y={y(t) + 4} textAnchor={anchor}>{T.compact(t)}</text>)}\n          {labels.map((l, i) => (i % step === 0 || i === labels.length - 1) && <text key={l} className=\"k-chart__tick\" x={x(i)} y={size.h - 6} textAnchor=\"middle\">{l}</text>)}\n          <defs>\n            <linearGradient id={`${id}-fade`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n              <stop offset=\"0\" style={{ stopColor: \"var(--k-chart-1)\", stopOpacity: 0.28 }} />\n              <stop offset=\"1\" style={{ stopColor: \"var(--k-chart-1)\", stopOpacity: 0 }} />\n            </linearGradient>\n          </defs>\n          {(area ?? shown.length === 1) && shown.length === 1 && (\n            <path className=\"k-chart__area\" fill={`url(#${id}-fade)`} d={`M${x(0)},${y(0)} ${shown[0].values.map((v, i) => `L${x(i)},${y(v)}`).join(\" \")} L${x(labels.length - 1)},${y(0)} Z`} />\n          )}\n          {active !== null && <line className=\"k-chart__cross\" x1={x(active)} x2={x(active)} y1={pad.top} y2={pad.top + innerH} />}\n          {shown.map((s, si) => (\n            <g key={s.name} style={color(si)}>\n              <path className=\"k-chart__line\" pathLength={1} d={s.values.map((v, i) => `${i ? \"L\" : \"M\"}${x(i)},${y(v)}`).join(\" \")} />\n              <circle className=\"k-chart__dot\" cx={x(s.values.length - 1)} cy={y(s.values[s.values.length - 1])} r={4} />\n              {shown.length > 1 && <text className=\"k-chart__end\" x={endX(last)} y={endY[si] + 4} textAnchor={size.rtl ? \"end\" : \"start\"}>{s.name}</text>}\n              {active !== null && <circle className=\"k-chart__dot\" cx={x(active)} cy={y(s.values[active])} r={4.5} />}\n            </g>\n          ))}\n        </svg>\n        {active !== null && (\n          <ChartTip\n            x={x(active)}\n            y={Math.min(...shown.map((s) => y(s.values[active]))) - 6}\n            width={size.w}\n            title={labels[active]}\n            rows={shown.map((s, si) => ({ name: s.name, value: format(s.values[active]), color: `var(--k-chart-${si + 1})` }))}\n          />\n        )}\n      </div>\n      <span className=\"k-sr-only\" aria-live=\"polite\" id={`${id}-live`}>\n        {active !== null ? `${labels[active]}: ${shown.map((s) => `${s.name} ${format(s.values[active])}`).join(M.common.list)}` : \"\"}\n      </span>\n      <DataTable caption={title} labels={labels} series={shown} format={format} />\n    </figure>\n  );\n}\n"
    }
  ]
}