{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bar-chart",
  "type": "registry:ui",
  "title": "Bar Chart",
  "description": "Grouped or stacked vertical bar chart on a zero-based y-axis: bars at most 24px thick with 4px rounded data-ends, 2px surface gaps between bars and stacked segments, grow-from-baseline motion, a slot highlight with per-bar tooltip emphasis, and the same keyboard readout, legend filtering and table view as LineChart.",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://components.dethink.co.uk/r/dethink-base.json",
    "https://components.dethink.co.uk/r/chart-core.json",
    "https://components.dethink.co.uk/r/chart.json"
  ],
  "files": [
    {
      "path": "packages/components/src/components/bar-chart/bar-chart.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/bar-chart/bar-chart.tsx",
      "content": "import { forwardRef } from \"react\";\nimport { ChartBars } from \"../chart/chart\";\nimport {\n  CartesianChart,\n  valueAt,\n  type CartesianChartProps,\n  type CartesianLayout,\n  type ChartDatum,\n} from \"../chart/cartesian-chart\";\nimport { isFiniteValue, stackSeries } from \"../chart/chart-core\";\n\nexport type { ChartDatum };\n\nexport interface BarChartProps extends Omit<\n  CartesianChartProps,\n  \"curve\" | \"includeZero\"\n> {\n  /**\n   * Stack series in one bar per index so its end reads as the total.\n   * Positive values stack up and negative values down.\n   */\n  stacked?: boolean;\n  /** Largest bar (or group) thickness in pixels. At most 24. */\n  maxBarWidth?: number;\n}\n\n/** Surface gap between adjacent bars and between stacked segments. */\nconst GAP = 2;\nconst MAX_BAR_WIDTH = 24;\n// Share of each index's slot that bars may fill; the rest separates groups.\nconst SLOT_FILL = 0.72;\n\nexport const BarChart = forwardRef<HTMLDivElement, BarChartProps>(\n  (\n    {\n      animate = true,\n      data,\n      maxBarWidth = MAX_BAR_WIDTH,\n      stacked = false,\n      ...props\n    },\n    ref,\n  ) => (\n    <CartesianChart\n      {...props}\n      ref={ref}\n      data={data}\n      kind=\"bar\"\n      xLayout=\"band\"\n      // Bar length encodes the value, so the axis always starts at zero.\n      includeZero\n      data-stacked={stacked ? \"\" : undefined}\n      layout={(visible): CartesianLayout => {\n        const keys = visible.map((item) => item.key);\n        const bands = stacked\n          ? stackSeries(data, keys, valueAt).map((band) => band.values)\n          : undefined;\n        const raw = (s: number, i: number) => valueAt(data[i], keys[s]!);\n        // The segment end away from zero, where its data-end sits.\n        const edge = (s: number, i: number) => {\n          const band = bands![s]![i]!;\n          return (raw(s, i) ?? 0) < 0 ? band[0] : band[1];\n        };\n        const base = (s: number, i: number) => {\n          const band = bands![s]![i]!;\n          return (raw(s, i) ?? 0) < 0 ? band[1] : band[0];\n        };\n        // A stacked segment is outermost when no later series extends its side.\n        const outermost = (s: number, i: number) => {\n          const sign = Math.sign(raw(s, i) ?? 0);\n          return !keys.some(\n            (_, later) => later > s && Math.sign(raw(later, i) ?? 0) === sign,\n          );\n        };\n\n        // Bars and gaps always fit inside the slot: dense charts first lose\n        // the gaps between grouped bars, then bars go below 1px, rather than\n        // spilling into the neighbouring category.\n        const geometry = (step: number) => {\n          const slot = step * SLOT_FILL;\n          const count = bands ? 1 : Math.max(1, keys.length);\n          const gap =\n            count > 1 ? Math.min(GAP, (slot * 0.25) / (count - 1)) : 0;\n          const barWidth = Math.min(\n            Math.min(maxBarWidth, MAX_BAR_WIDTH),\n            (slot - gap * (count - 1)) / count,\n          );\n          return {\n            gap,\n            barWidth,\n            groupWidth: barWidth * count + gap * (count - 1),\n          };\n        };\n\n        return {\n          markHalfWidth: (step) => geometry(step).groupWidth / 2,\n          domainValues: bands\n            ? bands.flatMap((band) => band.flat())\n            : data.flatMap((datum) => keys.map((key) => valueAt(datum, key))),\n          pointValue: (key, i) => {\n            const s = keys.indexOf(key);\n            if (!isFiniteValue(raw(s, i))) return null;\n            return bands ? edge(s, i) : raw(s, i);\n          },\n          render: ({ x, step, y, highlighted, hovered }) => {\n            const { gap, barWidth, groupWidth } = geometry(step);\n            const dimmed = (key: string) => {\n              const focus = highlighted ?? hovered;\n              return focus !== undefined && focus !== key;\n            };\n\n            return visible.map((item, s) => (\n              <ChartBars\n                key={item.key}\n                data-series={item.key}\n                values={data.map((_, i) => (bands ? edge(s, i) : raw(s, i)))}\n                baseline={bands ? data.map((_, i) => base(s, i)) : undefined}\n                inset={bands ? (i) => (base(s, i) !== 0 ? GAP : 0) : undefined}\n                rounded={bands ? (i) => outermost(s, i) : true}\n                x={(i) =>\n                  x(i) - groupWidth / 2 + (bands ? 0 : s * (barWidth + gap))\n                }\n                width={barWidth}\n                y={y}\n                color={item.colorVar}\n                animate={animate}\n                dimmed={dimmed(item.key)}\n              />\n            ));\n          },\n        };\n      }}\n    />\n  ),\n);\n\nBarChart.displayName = \"BarChart\";\n"
    },
    {
      "path": "packages/components/src/components/bar-chart/index.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/bar-chart/index.ts",
      "content": "export { BarChart, type BarChartProps } from \"./bar-chart\";\n"
    }
  ]
}
