{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bar-chart",
  "title": "Bar chart",
  "description": "Amounts per category, grouped or stacked with separators, vertical or horizontal, theme-rounded bar ends and a selected bar.",
  "dependencies": [
    "cn",
    "@tanstack/charts"
  ],
  "registryDependencies": [
    "https://glasscn.app/r/glass-style.json",
    "https://glasscn.app/r/chart.json",
    "https://glasscn.app/r/glass-charts.json"
  ],
  "files": [
    {
      "path": "src/components/glass/bar-chart.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"cn\"\nimport { barX, barY, defineChart, group, type ChannelAccessorContext } from \"@tanstack/charts\"\nimport { scaleBand } from \"@tanstack/charts/scales/band\"\nimport { tooltip } from \"@tanstack/charts/tooltip\"\n\nimport {\n  ChartAlternative,\n  ChartContainer,\n  ChartLegend,\n  categoryAxisStyle,\n  chartColor,\n  seriesTooltip,\n  useAxisSide,\n  useChartAccessors,\n  useControlRadius,\n  valueAxis,\n  type ChartAccessibility,\n  type ChartSeries,\n  type ChartStateLabels,\n  type ChartStateValue,\n} from \"@/components/glass/chart\"\nimport { categoryKey } from \"@/lib/glass-charts\"\n\ntype XValue = string | number | Date\n\n/**\n * Amounts per category — days, habits, weeks. Several series sit side by side,\n * or pile up with `stacked` (a hairline separates the segments); `layout=\"horizontal\"`\n * turns it on its side for long labels. Bars start at zero and their ends take the\n * theme's corner radius. Each row is its own band — two rows labelled \"Mon\" stay\n * two bars — and dates are keyed by their local day.\n */\nfunction BarChart<Row>({\n  data,\n  x: xProp,\n  series: seriesProp,\n  ariaLabel,\n  height = 240,\n  stacked = false,\n  layout = \"vertical\",\n  valueFormat: valueFormatProp,\n  xFormat: xFormatProp,\n  legend = seriesProp.length > 1,\n  grid = true,\n  axis = \"trailing\",\n  selectedX,\n  onSelect,\n  state,\n  onRetry,\n  stateLabels,\n  accessibilityTable = true,\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"children\" | \"onSelect\"> & {\n  data: readonly Row[]\n  x: (d: Row) => XValue\n  series: readonly ChartSeries<Row>[]\n  ariaLabel: string\n  height?: number\n  stacked?: boolean\n  layout?: \"vertical\" | \"horizontal\"\n  valueFormat?: (value: number) => string\n  xFormat?: (value: XValue) => string\n  legend?: boolean\n  grid?: boolean\n  /** Which side the value axis sits on (vertical layout). Apple's charts put it trailing. */\n  axis?: \"leading\" | \"trailing\"\n  /** Highlights the bars at this x (the others dim) — controlled selection. */\n  selectedX?: XValue | null\n  /** A click, tap or Enter on a bar selects its row; null clears. */\n  onSelect?: (row: Row | null) => void\n  state?: ChartStateValue\n  onRetry?: () => void\n  stateLabels?: ChartStateLabels\n  /** Exact values for assistive tech: true (sr-only, default), \"visible\", or false. */\n  accessibilityTable?: ChartAccessibility\n}) {\n  const ref = React.useRef<HTMLDivElement>(null)\n  const radius = useControlRadius(ref)\n  const side = useAxisSide(ref, axis)\n  const { x, series, valueFormat, xFormat, key } = useChartAccessors({ x: xProp, series: seriesProp, valueFormat: valueFormatProp, xFormat: xFormatProp })\n  const selectedKey = selectedX == null ? null : selectedX instanceof Date ? selectedX.getTime() : selectedX\n\n  const definition = React.useMemo(() => {\n    // One mark over every series: your rows, repeated once per series, so the\n    // library can group or stack them. The datum is still your row; the\n    // position in the repeated list says which series it is.\n    const rows = series.flatMap(() => data)\n    const seriesIndex = (c: ChannelAccessorContext<Row>) => Math.floor(c.index / data.length)\n    const seriesAt = (c: ChannelAccessorContext<Row>) => series[seriesIndex(c)]\n    // the band: the row's own key (local day for dates), never its label — labels can repeat\n    const keys = data.map((d, i) => categoryKey(x(d), i))\n    const byKey = new Map(keys.map((k, i) => [k, x(data[i])]))\n    const category = (_: Row, c: ChannelAccessorContext<Row>) => keys[c.index % data.length]\n    const label = (k: string) => {\n      const v = byKey.get(k)\n      if (v === undefined) return k\n      return xFormat ? xFormat(v) : v instanceof Date ? v.toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" }) : String(v)\n    }\n    const value = (d: Row, c: ChannelAccessorContext<Row>) => seriesAt(c).value(d)\n    const id = (_: Row, c: ChannelAccessorContext<Row>) => seriesAt(c).id\n    const barKey = (_: Row, c: ChannelAccessorContext<Row>) => `${seriesAt(c).id}:${c.index % data.length}`\n    const colors = series.map((s, i) => s.color ?? chartColor(i))\n    const isSelected = (d: Row) => {\n      const v = x(d)\n      return (v instanceof Date ? v.getTime() : v) === selectedKey\n    }\n    const shared = {\n      z: id,\n      color: id,\n      key: barKey,\n      layout: stacked || series.length < 2 ? undefined : group({ padding: 0.12 }),\n      radius: { end: Math.min(radius, 8) },\n      maxThickness: 44,\n      ...(selectedKey == null\n        ? null\n        : { fill: (d: Row, c: ChannelAccessorContext<Row>) => (isSelected(d) ? colors[seriesIndex(c)] : `color-mix(in oklch, ${colors[seriesIndex(c)]} 35%, transparent)`) }),\n      // a hairline in the ground colour between stacked segments (HIG)\n      ...(stacked && series.length > 1 ? { stroke: \"var(--background)\", strokeWidth: 1 } : null),\n    }\n    const categories = { scale: () => scaleBand<string>().padding(series.length > 1 && !stacked ? 0.2 : 0.32), axis: categoryAxisStyle(label as (v: XValue) => string) }\n    const common = {\n      color: { domain: series.map((s) => s.id), range: colors },\n      tooltip: {\n        use: tooltip,\n        anchor: \"group-center\" as const,\n        sort: stacked ? undefined : (\"color-domain\" as const),\n        placement: layout === \"horizontal\" ? ([\"right\", \"left\", \"top\", \"bottom\"] as const) : ([\"top\", \"right\", \"left\", \"bottom\"] as const),\n        content: seriesTooltip(series, x, { xFormat, valueFormat, reverse: stacked }),\n      },\n    }\n    return layout === \"horizontal\"\n      ? defineChart({\n          marks: [barX(rows, { y: category, x: value, ...shared })],\n          scales: { x: valueAxis({ grid, valueFormat, side: \"bottom\" }), y: categories },\n          focus: \"group-y\",\n          ...common,\n        })\n      : defineChart({\n          marks: [barY(rows, { x: category, y: value, ...shared })],\n          scales: { x: categories, y: valueAxis({ grid, valueFormat, side }) },\n          focus: \"group-x\",\n          ...common,\n        })\n    // `key` stands for the accessors' structure; they call your latest functions\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [data, key, stacked, layout, grid, radius, side, selectedKey])\n\n  return (\n    <div ref={ref} data-slot=\"bar-chart\" data-layout={layout} className={cn(\"grid w-full gap-3\", className)} {...props}>\n      <ChartContainer\n        definition={definition}\n        ariaLabel={ariaLabel}\n        height={height}\n        state={state ?? (data.length ? \"ready\" : \"empty\")}\n        onRetry={onRetry}\n        stateLabels={stateLabels}\n        onSelect={onSelect ? (p) => onSelect(p ? (p.datum as Row) : null) : undefined}\n      />\n      {legend ? <ChartLegend items={series.map((s, i) => ({ label: s.label, color: s.color ?? chartColor(i) }))} /> : null}\n      <ChartAlternative mode={accessibilityTable} data={data} x={x} series={series} xFormat={xFormat} valueFormat={valueFormat} label={ariaLabel} />\n    </div>\n  )\n}\n\nexport { BarChart }\n",
      "type": "registry:ui",
      "target": "@components/glass/bar-chart.tsx"
    }
  ],
  "categories": [
    "glass",
    "data"
  ],
  "type": "registry:ui"
}