{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"bar-chart","type":"registry:ui","title":"Bar Chart","description":"Values compared across categories or periods as bars: one series, several side by side, or stacked. Plain SVG.","dependencies":[],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/lib-chart.json","https://dashral.com/r/lib-locale.json"],"files":[{"path":"registry/dashral/bar-chart.tsx","type":"registry:file","target":"components/dashral/bar-chart.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\n\nimport { barPath, type CartesianChartProps, ChartAxes, ChartLegend, ChartReading, seriesColor, useCartesianChart } from \"./lib/chart\"\nimport { useLocale } from \"./lib/locale\"\nimport { cn } from \"./lib/utils\"\n\ntype BarChartProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n  CartesianChartProps & {\n    /** Stacks the series in one bar, so its height is their total; otherwise they stand side by side. */\n    stacked?: boolean\n    /** Corner radius of a bar's end, in pixels. */\n    radius?: number\n    classNames?: { legend?: string; tooltip?: string }\n  }\n\n/**\n * Values compared across categories or periods as bars: one series, several side by side, or stacked into a total.\n * Hover, or focus it and use the arrow keys, to read a column: the other bars fade back and a tooltip lists every\n * series there.\n * The axis writes values compactly; the tooltip in full. Plain SVG, fills its container's width; the bars grow up\n * from the axis when it first shows and to their new heights when the data changes.\n */\nfunction BarChart({\n  data,\n  index,\n  labelKey,\n  series,\n  height,\n  locale,\n  formatOptions,\n  timeFormat,\n  showLegend,\n  showGrid,\n  showYAxis,\n  stacked = false,\n  radius = 4,\n  className,\n  classNames,\n  \"aria-label\": ariaLabel,\n  ...props\n}: BarChartProps) {\n  const { t } = useLocale()\n  const h = useCartesianChart({\n    data,\n    index,\n    labelKey,\n    series,\n    height,\n    locale,\n    formatOptions,\n    timeFormat,\n    showLegend,\n    showGrid,\n    showYAxis,\n    stacked,\n    band: true,\n    \"aria-label\": ariaLabel,\n    roledescription: t(\"barChart\"),\n  })\n  const n = data.length\n  // Each column takes most of its slot; side by side, its bars share it with a little gap between them.\n  const group = Math.min(h.slot * 0.7, stacked ? 48 : 36 * series.length + 4 * (series.length - 1))\n  const gap = stacked || series.length === 1 ? 0 : Math.min(4, group * 0.08)\n  const bar = stacked ? group : (group - gap * (series.length - 1)) / series.length\n  const zero = h.y(Math.max(h.ticks[0]!, Math.min(0, h.ticks.at(-1)!)))\n\n  return (\n    <div data-slot=\"bar-chart\" {...h.root} className={cn(\"grid gap-3 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring/50\", className)} {...props}>\n      {h.showLegend && <ChartLegend series={series} className={classNames?.legend} />}\n\n      <div ref={h.box} dir=\"ltr\" className=\"relative w-full touch-none select-none\" style={{ height: h.height }} {...h.plot}>\n        {h.width !== null && n > 0 && (\n          <>\n            <ChartAxes chart={h} band />\n\n            {/* The bars, growing up from the axis when the chart first shows and to new heights when the data changes. */}\n            <svg aria-hidden width={h.width} height={h.height} className=\"pointer-events-none absolute inset-0\">\n              {h.columns.map((cells, i) => {\n                const left = h.x(i) - group / 2\n                return (\n                  // The column being read keeps its color; the others fade back.\n                  <g key={i} opacity={h.active === null || h.active === i ? 1 : 0.4} className=\"transition-opacity duration-(--duration-fast)\">\n                    {series.map((s, k) => {\n                      const cell = cells[k]!\n                      const x = stacked ? left : left + k * (bar + gap)\n                      // A stacked segment starts a pixel above the one under it, so the parts read apart.\n                      const base = stacked ? h.y(cell.base) - (k > 0 ? 1 : 0) : zero\n                      const end = h.y(stacked ? cell.top : cell.value)\n                      // Only the top segment of a stack is rounded.\n                      const isTop = !stacked || cells.slice(k + 1).every((c) => c.value === 0)\n                      return (\n                        <path\n                          key={s.key}\n                          d={barPath(Math.round(x), Math.max(1, Math.round(bar)), base, h.drawn ? end : base, isTop ? radius : 0)}\n                          fill={seriesColor(s, k)}\n                          className=\"transition-[d] duration-(--duration-considered) ease-standard motion-reduce:transition-none\"\n                        />\n                      )\n                    })}\n                  </g>\n                )\n              })}\n            </svg>\n\n            <ChartReading chart={h} series={series} band className={classNames?.tooltip} />\n          </>\n        )}\n        {n === 0 && <p className=\"grid h-full place-items-center text-sm text-muted-foreground\">{t(\"noData\")}</p>}\n      </div>\n      <p aria-live=\"polite\" className=\"sr-only\">\n        {h.announce}\n      </p>\n    </div>\n  )\n}\n\nexport { BarChart, type BarChartProps }\nexport type { ChartDatum, ChartSeries } from \"./lib/chart\"\n"}],"cssVars":{"theme":{"ease-standard":"var(--ease-standard)","ease-enter":"var(--ease-enter)","ease-exit":"var(--ease-exit)","ease-spring":"var(--ease-spring)"},"light":{"duration-fast":"160ms","duration-standard":"240ms","duration-considered":"480ms","ease-standard":"cubic-bezier(0.2, 0, 0, 1)","ease-enter":"cubic-bezier(0, 0, 0, 1)","ease-exit":"cubic-bezier(0.3, 0, 1, 1)","ease-spring":"linear(0, 0.071 4%, 0.234 8%, 0.433 12%, 0.626 17%, 0.791 21%, 0.917 25%, 1.004 29%, 1.055 33%, 1.079 38%, 1.083 42%, 1.075 46%, 1.06 50%, 1.043 54%, 1.027 58%, 1.015 62%, 1.005 67%, 0.998 71%, 0.995 75%, 0.993 79%, 0.993 83%, 0.994 88%, 0.995 92%, 0.997 96%, 1)"},"dark":{}}}