import type { DsProps } from './props'; import * as React from 'react'; import { ResponsiveContainer } from 'recharts'; import { cn } from '@/lib/utils'; import { Surface } from '@/components/ds/Surface'; import { SectionHeader } from '@/components/ds/SectionHeader'; import { EmptyState } from '@/components/ds/EmptyState'; import { SkeletonChart } from '@/components/ds/Skeleton'; import { BarChart3 } from 'lucide-react'; /** * The chart palette. Recharts needs literal colours, not Tailwind classes, so * the brand values are mirrored here — this array is the only place a chart * should get a colour from. */ export const CHART_COLORS = ['#0838E0', '#F9E547', '#333F48', '#F8E08E', '#D1E0D7', '#062BAF']; export const CHART_TONES = { brand: '#0838E0', brandDark: '#062BAF', accent: '#F9E547', accentPale: '#F8E08E', navy: '#333F48', mint: '#D1E0D7', grid: 'hsl(150 8% 87%)', axis: 'hsl(220 9% 64%)', }; /** Shared axis props so every chart's ticks match. */ export const AXIS_PROPS = { stroke: CHART_TONES.axis, tick: { fontSize: 11, fill: CHART_TONES.axis }, tickLine: false, axisLine: false, }; /** * A tooltip matching the design system's surfaces. Recharts' default is a plain * white box that ignores the token set. */ /** @param {any} props */ export function ChartTooltip({ active, payload, label, valueFormatter, labelFormatter }: DsProps) { if (!active || !payload?.length) return null; return (
{label != null && (

{labelFormatter ? labelFormatter(label) : label}

)}
{payload.map((entry, i) => (
))}
); } /** * A legend that reads as part of the card rather than as chart furniture. * Items: `{ label, color, value? }`. */ export function ChartLegend({ items = [], className }: DsProps) { return (
{items.map((item) => (
))}
); } /** * ChartContainer — the card a chart lives in. * * Owns the title row, the fixed plot height, the loading skeleton, the empty * state, and an optional headline figure. Charts inside it are pure Recharts * children, so swapping a bar chart for a line chart changes nothing else. */ /** @param {any} props */ export function ChartContainer({ title, subtitle, icon, /** A big number in the top-right — an average, a total. */ headline, headlineLabel, /** Right-aligned controls, e.g. a SegmentedToggle for the range. */ actions, legend, height = 260, loading = false, isEmpty = false, emptyMessage = 'No data for this period', children, /** Skips ResponsiveContainer for children that are not Recharts charts. */ raw = false, footer, className, }: DsProps) { if (loading) return ; return ( {(title || headline || actions) && (
{title && ( )}
{actions} {headline != null && (
{headline}
{headlineLabel && (
{headlineLabel}
)}
)}
)} {legend && }
{isEmpty ? ( ) : raw ? ( children ) : ( {children} )}
{footer &&
{footer}
}
); }