'use client'; import { useMemo } from 'react'; import { t } from '@/i18n'; import { formatValue } from '@/lib/format'; import type { FormatSpec as Spec } from '@/lib/types'; import { ChartFrame } from './chart-frame'; import { CHART, MARK, seriesVar } from './palette'; import { yScale } from './scales'; import { useMeasure } from './use-measure'; export interface SlopeRow { id: string; label: string; a: number | null; b: number | null; colorIndex?: number; highlight?: boolean; } /** Two-period slope chart; labels on both ends, highlighted row in the accent, others in muted ink. */ export function SlopeChart({ rows, spec, yearA, yearB, height = 260, title, className }: { rows: SlopeRow[]; spec: Spec; yearA: number; yearB: number; height?: number; title?: React.ReactNode; className?: string }) { const { ref, width } = useMeasure(480); const model = useMemo(() => { const clean = rows.filter((r) => r.a != null && r.b != null) as Array; const vals = clean.flatMap((r) => [r.a, r.b]); const lo = Math.min(...vals); const hi = Math.max(...vals); const m = { top: 20, bottom: 24, left: 110, right: 110 }; const innerH = height - m.top - m.bottom; const y = yScale([lo, hi], [innerH, 0], { includeZero: false }); const x0 = m.left; const x1 = width - m.right; return { clean, y, m, x0, x1, innerH }; }, [rows, width, height]); const summary = t('chart.summary.slope', { y0: yearA, y1: yearB, n: model.clean.length }); return ( ({ label: r.label, a: formatValue(r.a, spec), b: formatValue(r.b, spec) })) }}>
{yearA} {yearB} {model.clean.map((r, i) => { const color = r.highlight ? CHART.accent : rows.length > 8 ? CHART.ruleStrong : seriesVar(r.colorIndex ?? i); return ( {r.label} {formatValue(r.a, spec)} {formatValue(r.b, spec)} ); })}
); }