import { useId } from "react" import { cn } from "cn" import { Bar, BarChart, CartesianGrid, Rectangle, ReferenceLine, XAxis, YAxis, type BarShapeProps } from "recharts" import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "./chart" import { chartColors, chartLightColors } from "./series" export type HMSignedBarChartProps = { data: ReadonlyArray> /** Field of each data row used for the category (X) axis. */ categoryKey: string /** Numeric field of each data row. Positive values rise above the 0 line, negative values fall below it. */ valueKey: string /** Name of the value, used as the tooltip row label. */ valueLabel: string /** Accessible name of the chart. */ label: string className?: string } const POSITIVE = 0 // HM series 1 (green) const NEGATIVE = 1 // HM series 2 (brown) /** * Vertical bar chart for positive and negative values (shadcn Chart + Recharts) around a clearly marked 0 line. * Color follows the sign only: positive uses the first HM series color, negative the second. Each bar is a gradient that is * light at the 0 line and full color at the outer end (opaque at both ends, light end = the palette's lighter step of the same * family), and only the outer end is rounded. The tooltip indicator uses the same solid color as the bar. */ export function HMSignedBarChart({ data, categoryKey, valueKey, valueLabel, label, className }: HMSignedBarChartProps) { const scope = `hm-signed-${useId().replace(/:/g, "")}` const positiveGradient = `${scope}-positive` const negativeGradient = `${scope}-negative` const config: ChartConfig = { [valueKey]: { label: valueLabel, color: chartColors[POSITIVE] }, positive: { label: "양수", color: chartColors[POSITIVE] }, negative: { label: "음수", color: chartColors[NEGATIVE] }, } // `fill` on the row feeds the tooltip indicator, so it always matches the bar's solid color. const rows = data.map(row => ({ ...row, fill: Number(row[valueKey]) < 0 ? "var(--color-negative)" : "var(--color-positive)" })) const isNegative = (value: BarShapeProps["value"]) => Number(Array.isArray(value) ? value[1] : value) < 0 return ( {/* Positive: full color at the top (outer end), light at the bottom (0 line). */} {/* Negative: light at the top (0 line), full color at the bottom (outer end). */} } /> { const negative = isNegative(props.value) return ( ) }} /> ) }