{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "line-chart",
  "title": "Line chart",
  "description": "One hairline through the points, a ring on each, against one very large number that rolls to the point you're on.",
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json",
    "https://thedirectors.agency/ui/r/chart.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/line-chart.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChartFrame, type ChartGeometry, type ChartShared } from \"@/components/ui/chart\"\n\ntype Curve = \"linear\" | \"smooth\" | \"step\"\ntype Point = [number, number]\n\ntype LineChartProps = ChartShared & {\n  /** linear: straight between points. smooth: one sweep through them, never past them. step: level across each point's column. */\n  variant?: Curve\n}\n\n// The path through the points, without its opening M, so an area can run back along another.\nfunction through(p: Point[], curve: Curve): string {\n  if (p.length < 2) return \"\"\n  if (curve === \"step\")\n    return p.slice(1).map(([x, y], i) => { const mid = (p[i][0] + x) / 2; return `H${mid}V${y}H${x}` }).join(\"\")\n  if (curve === \"linear\") return p.slice(1).map(([x, y]) => `L${x},${y}`).join(\"\")\n  // Monotone cubic (Fritsch and Carlson, as d3's curveMonotoneX): smooth, and it never overshoots a point.\n  const n = p.length\n  const d = p.slice(1).map(([x, y], i) => (y - p[i][1]) / (x - p[i][0]))\n  const m = p.map((_, i) => {\n    if (i === 0) return d[0]\n    if (i === n - 1) return d[n - 2]\n    const [a, b] = [d[i - 1], d[i]]\n    return (Math.sign(a) + Math.sign(b)) * Math.min(Math.abs(a), Math.abs(b), Math.abs(a + b) / 4)\n  })\n  return p.slice(1).map(([x, y], i) => {\n    const [x0, y0] = p[i]\n    const h = (x - x0) / 3\n    return `C${x0 + h},${y0 + h * m[i]} ${x - h},${y - h * m[i + 1]} ${x},${y}`\n  }).join(\"\")\n}\n\n/**\n * The drawing under the points, in pixels so a hairline stays a hairline: one line per series\n * and, for an area, the drops under it at barcode rhythm. Laid out once the plot has a size.\n */\nfunction Draw({ spans, series, curve, area }: ChartGeometry & { curve: Curve; area?: boolean }) {\n  const ref = React.useRef<SVGSVGElement>(null)\n  const [[w, h], setSize] = React.useState([0, 0])\n  const id = React.useId()\n  React.useEffect(() => {\n    const el = ref.current\n    if (!el) return\n    const ro = new ResizeObserver(([e]) => setSize([e.contentRect.width, e.contentRect.height]))\n    ro.observe(el)\n    return () => ro.disconnect()\n  }, [])\n  const x = (i: number) => ((i + 0.5) / spans.length) * w\n  const y = (v: number) => (1 - v) * h\n  return (\n    <svg ref={ref} data-slot=\"chart-draw\" className=\"ot-line-chart-draw\" aria-hidden=\"true\">\n      {w\n        ? series.map((s, k) => {\n            const tops = spans.map((p, i): Point => [x(i), y(p[k][1])])\n            const line = `M${tops[0][0]},${tops[0][1]}${through(tops, curve)}`\n            const bases = spans.map((p, i): Point => [x(i), y(p[k][0])]).reverse()\n            return (\n              <g key={s.key} data-series={k}>\n                {area ? (\n                  <>\n                    <pattern id={`${id}-${k}`} patternUnits=\"userSpaceOnUse\" width={[3, 6, 12][Math.min(k, 2)]} height={h}>\n                      <rect width=\"1\" height={h} />\n                    </pattern>\n                    <path className=\"ot-area-chart-drops\" fill={`url(#${id}-${k})`} d={`${line}L${bases[0][0]},${bases[0][1]}${through(bases, curve)}Z`} />\n                  </>\n                ) : null}\n                <path className=\"ot-line-chart-line\" d={line} />\n              </g>\n            )\n          })\n        : null}\n    </svg>\n  )\n}\n\n/** The line and area charts' shared body: chart's frame with a drawing laid under its points. */\nfunction ChartLines({ variant = \"linear\", area, className, ...props }: LineChartProps & { area?: boolean }) {\n  return (\n    <ChartFrame\n      {...props}\n      variant={variant}\n      stacked={area}\n      className={cn(area ? \"ot-area-chart\" : \"ot-line-chart\", className)}\n      layer={(g) => <Draw {...g} curve={variant} area={area} />}\n    />\n  )\n}\n\n/**\n * One hairline through the points, a ring on each, against one very large number. Point at the\n * line, or focus it, and the ring swells, a drop runs down to the baseline and the number rolls.\n */\nfunction LineChart(props: LineChartProps) {\n  return <ChartLines {...props} />\n}\n\nexport { LineChart, ChartLines, type LineChartProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/line-chart.css",
      "content": "@layer components {\n  /* ── ot-line-chart ── one hairline through the points, a ring on each, against one very large number.\n     The line is drawn in pixels under the points (the chart's own buttons), so it stays a hairline at\n     any width; pointing drops the bar chart's stem from the ring to the baseline and rolls the number. */\n  :is(.ot-line-chart, .ot-area-chart) > .ot-chart-plot { position: relative; }\n  .ot-line-chart-draw { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; overflow: visible; pointer-events: none; }\n  .ot-line-chart-draw:is([dir=\"rtl\"] *) { scale: -1 1; }\n  .ot-line-chart-line { fill: none; stroke: var(--ot-ink); stroke-width: var(--ot-hairline); stroke-linejoin: round; }\n  .ot-line-chart-draw > [data-series=\"1\"] > .ot-line-chart-line { stroke: var(--ot-graphite); stroke-dasharray: 1 3; }\n  .ot-line-chart-draw > [data-series=\"2\"] > .ot-line-chart-line { stroke: var(--ot-pencil); stroke-dasharray: 4 3; }\n\n  /* The drop: hidden at rest, it runs down from the ring when you point (moderato, exhale). */\n  :is(.ot-line-chart, .ot-area-chart) .ot-chart-mark { left: 50%; }\n  :is(.ot-line-chart, .ot-area-chart) :is(.ot-chart-bar, .ot-chart-mark)::before {\n    background: var(--ot-ink); scale: 1 0; transform-origin: top;\n    transition: scale var(--ot-moderato) var(--ot-exhale), width var(--ot-allegro);\n  }\n  :is(.ot-line-chart, .ot-area-chart) .ot-chart-bar:is(:hover, :focus-visible)::before,\n  :is(.ot-line-chart, .ot-area-chart) .ot-chart-bar:is(:hover, :focus-visible) > .ot-chart-mark::before,\n  :is(.ot-line-chart, .ot-area-chart)[data-force~=\"hover\"] > .ot-chart-plot > .ot-chart-bar[data-shown]::before,\n  :is(.ot-line-chart, .ot-area-chart)[data-force~=\"hover\"] > .ot-chart-plot > .ot-chart-bar[data-shown] > .ot-chart-mark::before { scale: 1 1; }\n  :is(.ot-line-chart, .ot-area-chart) .ot-chart-mark:is([data-series=\"1\"], [data-series=\"2\"])::before { background: var(--c); }\n\n  @media (forced-colors: active) {\n    .ot-line-chart-line { stroke: CanvasText; }\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/line-chart.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/line-chart.css\"": {}
  },
  "meta": {
    "movement": "X",
    "underneath": "hook",
    "contract": "ot-line-chart"
  },
  "type": "registry:ui"
}
