{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bar-chart",
  "title": "Bar Chart",
  "description": "A single-series bar chart with gradient or striped fills, highlighted bars, and a hover marker.",
  "dependencies": [
    "recharts"
  ],
  "registryDependencies": [
    "chart"
  ],
  "files": [
    {
      "path": "registry/dashboardcn/ui/bar-chart.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  Bar,\n  BarChart as RechartsBarChart,\n  CartesianGrid,\n  Cell,\n  Rectangle,\n  ReferenceLine,\n  XAxis,\n  YAxis,\n} from \"recharts\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChartContainer, ChartTooltip } from \"@/components/ui/chart\"\n\nexport type BarRow = Record<string, unknown>\n\nexport interface BarReferenceLine {\n  /** Horizontal line at this y value. */\n  y: number\n  label?: string\n  /** Any CSS color. Defaults to the muted foreground. */\n  color?: string\n  /** Defaults to dashed. */\n  dashed?: boolean\n}\n\nexport interface BarChartProps\n  extends Omit<React.ComponentProps<typeof ChartContainer>, \"config\" | \"children\"> {\n  data: BarRow[]\n  /** Key of the x-axis value in each row. */\n  xKey: string\n  /** Key of the bar value in each row. */\n  yKey: string\n  /**\n   * Key of a coarser label, e.g. \"month\" for weekly rows. When set, the x-axis\n   * shows one tick per group centered under its bars instead of a tick per bar.\n   */\n  groupKey?: string\n  /** \"gradient\" fades each bar toward the bottom; \"striped\" fills it with diagonal bands. */\n  variant?: \"gradient\" | \"striped\" | \"solid\"\n  /** Any CSS color for highlighted bars. Defaults to chart-1. */\n  color?: string\n  /** Any CSS color for bars that are not highlighted. Defaults to the muted foreground. */\n  mutedColor?: string\n  /** Return true for bars drawn in color. Every bar is highlighted when omitted. */\n  highlight?: (row: BarRow, index: number) => boolean\n  grid?: \"dashed\" | \"solid\" | \"none\"\n  showYAxis?: boolean\n  showTooltip?: boolean\n  /** Draw a hollow ring on top of the hovered bar. */\n  showActiveMarker?: boolean\n  barRadius?: number\n  /** Animate bars after hydration and when data changes. Respects reduced motion. */\n  animate?: boolean\n  animationDuration?: number\n  /** Horizontal lines, e.g. a goal or an average. The y-axis extends to fit them. */\n  referenceLines?: BarReferenceLine[]\n  xFormatter?: (value: unknown) => string\n  yFormatter?: (value: number) => string\n  /** Secondary line of the tooltip. Defaults to the formatted x value. */\n  tooltipLabel?: (row: BarRow, index: number) => React.ReactNode\n  onBarClick?: (row: BarRow, index: number) => void\n}\n\nconst reducedMotionQuery = \"(prefers-reduced-motion: reduce)\"\n\nfunction subscribeToReducedMotion(onChange: () => void) {\n  const query = window.matchMedia(reducedMotionQuery)\n  query.addEventListener(\"change\", onChange)\n  return () => query.removeEventListener(\"change\", onChange)\n}\n\nfunction getReducedMotionSnapshot() {\n  return window.matchMedia(reducedMotionQuery).matches\n}\n\nfunction getReducedMotionServerSnapshot() {\n  return true\n}\n\nfunction defaultXFormatter(value: unknown) {\n  if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}/.test(value)) {\n    const [y, m, d] = value.split(\"-\").map(Number)\n    return new Date(y!, m! - 1, d).toLocaleDateString(\"en-US\", {\n      month: \"short\",\n      day: \"numeric\",\n    })\n  }\n  return String(value)\n}\n\n/** One tick per group, placed under the middle bar of the group. */\nfunction groupTicks(data: BarRow[], groupKey: string) {\n  const labels: string[] = Array.from({ length: data.length }, () => \"\")\n  let start = 0\n  for (let i = 1; i <= data.length; i++) {\n    if (i === data.length || data[i]![groupKey] !== data[start]![groupKey]) {\n      labels[Math.floor((start + i - 1) / 2)] = String(data[start]![groupKey] ?? \"\")\n      start = i\n    }\n  }\n  return labels\n}\n\ninterface ShapeProps {\n  x?: number\n  y?: number\n  width?: number\n  height?: number\n  index?: number\n}\n\n/** A single-series bar chart with gradient or striped fills, highlighted bars, and a hover marker. */\nfunction BarChart({\n  data,\n  xKey,\n  yKey,\n  groupKey,\n  variant = \"gradient\",\n  color = \"var(--chart-1)\",\n  mutedColor = \"var(--muted-foreground)\",\n  highlight,\n  grid = \"dashed\",\n  showYAxis = false,\n  showTooltip = true,\n  showActiveMarker = true,\n  barRadius = 6,\n  animate = true,\n  animationDuration = 500,\n  referenceLines = [],\n  xFormatter = defaultXFormatter,\n  yFormatter,\n  tooltipLabel,\n  onBarClick,\n  className,\n  ...props\n}: BarChartProps) {\n  const id = React.useId()\n  const reducedMotion = React.useSyncExternalStore(\n    subscribeToReducedMotion,\n    getReducedMotionSnapshot,\n    getReducedMotionServerSnapshot\n  )\n  const isHighlighted = React.useCallback(\n    (index: number) => (highlight ? highlight(data[index]!, index) : true),\n    [data, highlight]\n  )\n  const tickLabels = React.useMemo(\n    () => (groupKey ? groupTicks(data, groupKey) : null),\n    [data, groupKey]\n  )\n  const fillFor = (index: number) =>\n    `url(#${id}-${isHighlighted(index) ? \"on\" : \"off\"})`\n\n  const activeBar = (shape: ShapeProps) => {\n    const { x = 0, y = 0, width = 0, height = 0, index = 0 } = shape\n    const on = isHighlighted(index)\n    return (\n      <g>\n        <Rectangle {...shape} fill={fillFor(index)} />\n        {showActiveMarker && height > 0 ? (\n          <circle\n            cx={x + width / 2}\n            cy={y}\n            r={4}\n            fill=\"var(--background)\"\n            stroke={on ? color : mutedColor}\n            strokeWidth={2}\n          />\n        ) : null}\n      </g>\n    )\n  }\n\n  return (\n    <ChartContainer\n      data-slot=\"bar-chart\"\n      config={{ [yKey]: { label: yKey, color } }}\n      className={cn(\"aspect-auto h-64 w-full\", className)}\n      {...props}\n    >\n      <RechartsBarChart\n        data={data}\n        margin={{ top: 12, right: 8, bottom: 0, left: showYAxis ? 0 : 8 }}\n        barCategoryGap=\"28%\"\n      >\n        <defs>\n          {variant === \"gradient\" ? (\n            <>\n              <linearGradient id={`${id}-on`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                <stop offset=\"0%\" stopColor={color} stopOpacity={1} />\n                <stop offset=\"100%\" stopColor={color} stopOpacity={0.6} />\n              </linearGradient>\n              <linearGradient id={`${id}-off`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                <stop offset=\"0%\" stopColor={mutedColor} stopOpacity={0.35} />\n                <stop offset=\"100%\" stopColor={mutedColor} stopOpacity={0.18} />\n              </linearGradient>\n            </>\n          ) : variant === \"striped\" ? (\n            <>\n              {([\"on\", \"off\"] as const).map((state) => {\n                const fill = state === \"on\" ? color : mutedColor\n                const opacity = state === \"on\" ? 1 : 0.3\n                return (\n                  <pattern\n                    key={state}\n                    id={`${id}-${state}`}\n                    patternUnits=\"userSpaceOnUse\"\n                    width={10}\n                    height={10}\n                    patternTransform=\"rotate(45)\"\n                  >\n                    <rect width={10} height={10} fill={fill} fillOpacity={opacity} />\n                    <rect\n                      width={5}\n                      height={10}\n                      fill=\"var(--background)\"\n                      fillOpacity={0.25}\n                    />\n                  </pattern>\n                )\n              })}\n            </>\n          ) : (\n            <>\n              <linearGradient id={`${id}-on`}>\n                <stop stopColor={color} />\n              </linearGradient>\n              <linearGradient id={`${id}-off`}>\n                <stop stopColor={mutedColor} stopOpacity={0.3} />\n              </linearGradient>\n            </>\n          )}\n        </defs>\n        {grid !== \"none\" ? (\n          <CartesianGrid\n            vertical={false}\n            strokeDasharray={grid === \"dashed\" ? \"6 6\" : undefined}\n          />\n        ) : null}\n        <XAxis\n          dataKey={xKey}\n          tickLine={false}\n          axisLine={false}\n          tickMargin={10}\n          interval={tickLabels ? 0 : \"preserveStartEnd\"}\n          minTickGap={tickLabels ? 0 : 24}\n          tickFormatter={\n            tickLabels ? (_, index) => tickLabels[index] ?? \"\" : (v) => xFormatter(v)\n          }\n        />\n        <YAxis\n          hide={!showYAxis}\n          tickLine={false}\n          axisLine={false}\n          tickMargin={8}\n          width={44}\n          tickFormatter={yFormatter}\n        />\n        {showTooltip ? (\n          <ChartTooltip\n            cursor={false}\n            isAnimationActive={false}\n            content={({ active, payload }) => {\n              const entry = payload?.[0]\n              if (!active || !entry) return null\n              const row = entry.payload as BarRow\n              const index = data.indexOf(row)\n              const value = Number(row[yKey])\n              return (\n                <div className=\"bg-background rounded-lg border px-3 py-2 text-xs shadow-md\">\n                  <div className=\"text-sm font-semibold tabular-nums\">\n                    {yFormatter ? yFormatter(value) : String(value)}\n                  </div>\n                  <div className=\"text-muted-foreground\">\n                    {tooltipLabel ? tooltipLabel(row, index) : xFormatter(row[xKey])}\n                  </div>\n                </div>\n              )\n            }}\n          />\n        ) : null}\n        <Bar\n          dataKey={yKey}\n          radius={barRadius}\n          isAnimationActive={animate && !reducedMotion}\n          animationDuration={animationDuration}\n          animationEasing=\"ease-out\"\n          activeBar={activeBar}\n          onClick={\n            onBarClick\n              ? (_, index) => onBarClick(data[index]!, index)\n              : undefined\n          }\n          className={onBarClick ? \"cursor-pointer\" : undefined}\n        >\n          {data.map((_, index) => (\n            <Cell key={index} fill={fillFor(index)} />\n          ))}\n        </Bar>\n        {referenceLines.map((line, index) => (\n          <ReferenceLine\n            key={index}\n            y={line.y}\n            ifOverflow=\"extendDomain\"\n            stroke={line.color ?? \"var(--muted-foreground)\"}\n            strokeDasharray={line.dashed === false ? undefined : \"4 4\"}\n            label={\n              line.label\n                ? {\n                    value: line.label,\n                    position: \"insideTopRight\",\n                    fill: line.color ?? \"var(--muted-foreground)\",\n                    fontSize: 11,\n                  }\n                : undefined\n            }\n          />\n        ))}\n      </RechartsBarChart>\n    </ChartContainer>\n  )\n}\n\nexport { BarChart }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}