bar-chart

PreviousNext

bar-chart

Docs
intentuiui

Preview

Loading preview…
components/ui/bar-chart.tsx
"use client"

import { type ComponentProps, startTransition } from "react"
import { Bar, BarChart as BarChartPrimitive } from "recharts"
import type { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"
import {
  type BaseChartProps,
  CartesianGrid,
  Chart,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
  constructCategoryColors,
  DEFAULT_COLORS,
  getColorValue,
  valueToPercent,
  XAxis,
  YAxis,
} from "./chart"

export interface BarChartProps<TValue extends ValueType, TName extends NameType>
  extends BaseChartProps<TValue, TName> {
  barCategoryGap?: number
  barRadius?: number
  barGap?: number
  barSize?: number
  barProps?: Partial<React.ComponentProps<typeof Bar>>

  chartProps?: Omit<ComponentProps<typeof BarChartPrimitive>, "data" | "stackOffset">
}

export function BarChart<TValue extends ValueType, TName extends NameType>({
  data = [],
  dataKey,
  colors = DEFAULT_COLORS,
  type = "default",
  config,
  children,
  layout = "horizontal",

  // Components
  tooltip = true,
  tooltipProps,

  legend = true,
  legendProps,

  intervalType = "equidistantPreserveStart",

  barCategoryGap = 5,
  barGap,
  barSize,
  barRadius,
  barProps,

  valueFormatter = (value: number) => value.toString(),

  // XAxis
  displayEdgeLabelsOnly = false,
  xAxisProps,
  hideXAxis = false,

  // YAxis
  yAxisProps,
  hideYAxis = false,

  hideGridLines = false,
  chartProps,

  ...props
}: BarChartProps<TValue, TName>) {
  const categoryColors = constructCategoryColors(Object.keys(config), colors)

  const stacked = type === "stacked" || type === "percent"
  return (
    <Chart config={config} data={data} dataKey={dataKey} layout={layout} {...props}>
      {({ onLegendSelect, selectedLegend }) => (
        <BarChartPrimitive
          onClick={() => {
            onLegendSelect(null)
          }}
          data={data}
          margin={{
            bottom: 0,
            left: 5,
            right: 0,
            top: 5,
          }}
          layout={layout === "radial" ? "horizontal" : layout}
          barGap={barGap}
          barSize={barSize}
          barCategoryGap={barCategoryGap}
          stackOffset={type === "percent" ? "expand" : stacked ? "sign" : undefined}
          {...chartProps}
        >
          {!hideGridLines && <CartesianGrid strokeDasharray="4 4" />}
          <XAxis
            hide={hideXAxis}
            className="**:[text]:fill-muted-fg"
            displayEdgeLabelsOnly={displayEdgeLabelsOnly}
            intervalType={intervalType}
            {...xAxisProps}
          />
          <YAxis
            hide={hideYAxis}
            className="**:[text]:fill-muted-fg"
            tickFormatter={type === "percent" ? valueToPercent : valueFormatter}
            {...yAxisProps}
          />

          {legend && (
            <ChartLegend
              content={typeof legend === "boolean" ? <ChartLegendContent /> : legend}
              {...legendProps}
            />
          )}

          {tooltip && (
            <ChartTooltip
              content={
                typeof tooltip === "boolean" ? <ChartTooltipContent accessibilityLayer /> : tooltip
              }
              {...tooltipProps}
            />
          )}

          {!children
            ? Object.entries(config).map(([category, values]) => {
                return (
                  <Bar
                    key={category}
                    name={category}
                    dataKey={category}
                    stroke={getColorValue(values.color || categoryColors.get(category))}
                    strokeWidth={1}
                    stackId={stacked ? "stack" : undefined}
                    onClick={(_item, _number, event) => {
                      event.stopPropagation()

                      startTransition(() => {
                        onLegendSelect(category)
                      })
                    }}
                    radius={barRadius ?? (stacked ? undefined : 4)}
                    strokeOpacity={selectedLegend && selectedLegend !== category ? 0.2 : 0}
                    fillOpacity={selectedLegend && selectedLegend !== category ? 0.1 : 1}
                    fill={getColorValue(values.color || categoryColors.get(category))}
                    {...barProps}
                  />
                )
              })
            : children}
        </BarChartPrimitive>
      )}
    </Chart>
  )
}

Installation

npx shadcn@latest add @intentui/bar-chart

Usage

import { BarChart } from "@/components/ui/bar-chart"
<BarChart />