{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "interactive-area-chart",
  "type": "registry:ui",
  "title": "Interactive area chart",
  "description": "A React SVG area chart with a crosshair that follows the pointer or arrow keys, showing the value and percent change against a dashed comparison line.",
  "dependencies": [],
  "registryDependencies": [
    "https://ui.minidev.pro/r/area-chart.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/interactive-area-chart.tsx",
      "type": "registry:ui",
      "target": "components/ui/interactive-area-chart.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\nimport { smoothPath } from \"@/components/ui/area-chart\"\n\ntype Point = { label: string; value: number; compare?: number }\n\nconst SAMPLE: Point[] = [\n  \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\",\n].map((label, i) => ({ label, value: Math.round(42 + i * 3.4 + Math.sin(i * 1.3) * 5), compare: Math.round(38 + i * 2.2 + Math.cos(i) * 3) }))\n\n/**\n * InteractiveAreaChart — hover or arrow-key across the series; a hairline\n * crosshair pins the value and the delta against the comparison line.\n */\nfunction InteractiveAreaChart({\n  data = SAMPLE,\n  format = (n: number) => `$${n.toLocaleString()}k`,\n  label = \"Revenue\",\n  compareLabel = \"Last year\",\n  className,\n}: {\n  data?: Point[]\n  format?: (n: number) => string\n  label?: string\n  compareLabel?: string\n  className?: string\n}) {\n  const id = React.useId().replace(/:/g, \"\")\n  const [i, setI] = React.useState<number | null>(null)\n  const w = 600\n  const h = 200\n  const pad = 10\n  const all = data.flatMap((d) => [d.value, d.compare ?? d.value])\n  const max = Math.max(...all)\n  const min = Math.min(...all) * 0.9\n  const x = (k: number) => (k / Math.max(data.length - 1, 1)) * w\n  const y = (v: number) => h - pad - ((v - min) / (max - min || 1)) * (h - pad * 2)\n  const pts = data.map((d, k) => [x(k), y(d.value)] as const)\n  const cmp = data.every((d) => d.compare != null) ? data.map((d, k) => [x(k), y(d.compare!)] as const) : null\n  const line = smoothPath(pts)\n  const active = i ?? data.length - 1\n  const cur = data[active]\n  const delta = cur.compare != null ? ((cur.value - cur.compare) / cur.compare) * 100 : null\n\n  const onMove = (e: React.PointerEvent<HTMLDivElement>) => {\n    const r = e.currentTarget.getBoundingClientRect()\n    const k = Math.round(((e.clientX - r.left) / r.width) * (data.length - 1))\n    setI(Math.max(0, Math.min(data.length - 1, k)))\n  }\n\n  return (\n    <div data-slot=\"interactive-area-chart\" className={cn(\"w-full max-w-2xl rounded-xl border border-border bg-surface p-5 shadow-raised\", className)}>\n      <div className=\"flex flex-wrap items-end justify-between gap-3\">\n        <div>\n          <p className=\"text-[0.8125rem] text-fg-muted\">{label} · {cur.label}</p>\n          <div className=\"mt-1 flex items-baseline gap-2\">\n            <p className=\"text-3xl font-medium tracking-[-0.03em] tabular-nums text-fg\">{format(cur.value)}</p>\n            {delta != null ? (\n              <span className={cn(\"rounded-md px-1.5 py-0.5 text-[11px] font-medium tabular-nums\", delta >= 0 ? \"bg-success/10 text-success\" : \"bg-danger/10 text-danger\")}>\n                {delta >= 0 ? \"+\" : \"−\"}{Math.abs(delta).toFixed(1)}% vs {compareLabel.toLowerCase()}\n              </span>\n            ) : null}\n          </div>\n        </div>\n        <div className=\"flex items-center gap-4 text-[11px] text-fg-muted\">\n          <span className=\"inline-flex items-center gap-1.5\"><span className=\"h-0.5 w-3 rounded-full bg-accent\" />{label}</span>\n          {cmp ? <span className=\"inline-flex items-center gap-1.5\"><span className=\"w-3 border-t border-dashed border-fg-subtle\" />{compareLabel}</span> : null}\n        </div>\n      </div>\n      <div\n        role=\"slider\"\n        tabIndex={0}\n        aria-label={`${label} by ${data.length} periods`}\n        aria-valuemin={0}\n        aria-valuemax={data.length - 1}\n        aria-valuenow={active}\n        aria-valuetext={`${cur.label}: ${format(cur.value)}`}\n        onPointerMove={onMove}\n        onPointerLeave={() => setI(null)}\n        onKeyDown={(e) => {\n          if (e.key === \"ArrowLeft\") { e.preventDefault(); setI(Math.max(0, active - 1)) }\n          if (e.key === \"ArrowRight\") { e.preventDefault(); setI(Math.min(data.length - 1, active + 1)) }\n        }}\n        className=\"relative mt-5 h-52 cursor-crosshair rounded-md outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-4 focus-visible:ring-offset-surface\"\n      >\n        <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio=\"none\" className=\"absolute inset-0 size-full overflow-visible text-accent\" aria-hidden>\n          <defs>\n            <linearGradient id={`iac-${id}`} x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n              <stop offset=\"0%\" stopColor=\"currentColor\" stopOpacity=\"0.24\" />\n              <stop offset=\"100%\" stopColor=\"currentColor\" stopOpacity=\"0\" />\n            </linearGradient>\n            <clipPath id={`iacc-${id}`}>\n              <rect x=\"0\" y=\"0\" width={x(active)} height={h} />\n            </clipPath>\n          </defs>\n          {[0.25, 0.5, 0.75].map((f) => (\n            <line key={f} x1=\"0\" x2={w} y1={h * f} y2={h * f} className=\"stroke-border\" strokeDasharray=\"2 4\" vectorEffect=\"non-scaling-stroke\" />\n          ))}\n          <line x1=\"0\" x2={w} y1={h} y2={h} className=\"stroke-border\" vectorEffect=\"non-scaling-stroke\" />\n          {cmp ? <path d={smoothPath(cmp)} fill=\"none\" className=\"stroke-fg-subtle\" strokeWidth=\"1.25\" strokeDasharray=\"4 4\" vectorEffect=\"non-scaling-stroke\" /> : null}\n          <path d={`${line} L${w},${h} L0,${h} Z`} fill={`url(#iac-${id})`} opacity=\"0.45\" />\n          <path d={`${line} L${w},${h} L0,${h} Z`} fill={`url(#iac-${id})`} clipPath={`url(#iacc-${id})`} />\n          <path d={line} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.75\" strokeLinecap=\"round\" vectorEffect=\"non-scaling-stroke\" />\n          <line x1={x(active)} x2={x(active)} y1=\"0\" y2={h} stroke=\"currentColor\" strokeOpacity=\"0.4\" vectorEffect=\"non-scaling-stroke\" />\n        </svg>\n        <span\n          aria-hidden\n          className=\"pointer-events-none absolute size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-surface bg-accent shadow-[0_0_0_5px_var(--accent-soft)] transition-[left,top] duration-100\"\n          style={{ left: `${(x(active) / w) * 100}%`, top: `${(y(cur.value) / h) * 100}%` }}\n        />\n      </div>\n      <div className=\"mt-2 flex justify-between font-mono text-[10px] text-fg-subtle\">\n        {data.map((d, k) => (\n          <span key={d.label} className={cn(\"transition-colors\", k === active && \"text-fg\")}>{d.label}</span>\n        ))}\n      </div>\n    </div>\n  )\n}\nexport { InteractiveAreaChart }\n"
    }
  ]
}
