import { ChartFrame, SvgA11y } from "./frame.tsx"; import { chartId, finiteDomain, formatValue, plotArea, truncateLabel } from "./format.ts"; import { linearScale, niceTicks } from "./scale.ts"; import type { ComparisonChartProps } from "./types.ts"; export function ComparisonChart({ meta, data, baselineLabel = "Baseline", trainedLabel = "Trained", domain, format = "number", digits = 2, width = 720, height = 340, }: ComparisonChartProps) { const valid = data.filter((datum) => Number.isFinite(datum.baseline) && Number.isFinite(datum.trained)); const invalid = data.length - valid.length; if (valid.length === 0) { return ; } const values = valid.flatMap((datum) => [datum.baseline, datum.trained]); const dataMin = Math.min(0, ...values); const dataMax = Math.max(0, ...values); const [yMin, yMax] = finiteDomain(domain, [dataMin, dataMax]); const area = plotArea(width, height, { marginLeft: 68, marginBottom: 68 }); const y = linearScale([yMin, yMax], [area.marginTop + area.innerHeight, area.marginTop]); const zeroY = y(Math.min(yMax, Math.max(yMin, 0))); const ticks = niceTicks(yMin, yMax, 5); const step = area.innerWidth / valid.length; const groupWidth = Math.min(68, step * 0.7); const gap = Math.max(3, groupWidth * 0.08); const barWidth = Math.max(2, (groupWidth - gap) / 2); const titleId = chartId(meta, "title"); const descriptionId = chartId(meta, "description"); const graphic = ( {valid.map((datum, index) => { const center = area.marginLeft + step * index + step / 2; const baselineY = y(datum.baseline); const trainedY = y(datum.trained); return ( {`${datum.label}, ${baselineLabel}: ${formatValue(datum.baseline, format, digits)}`} {`${datum.label}, ${trainedLabel}: ${formatValue(datum.trained, format, digits)}`} {truncateLabel(datum.label, 16)} ); })} {meta.yLabel && ( {meta.yLabel} )} {meta.xLabel && ( {meta.xLabel} )} ); const table = ( {valid.map((datum) => ( ))}
{meta.title}
Measure{baselineLabel}{trainedLabel}Change
{datum.label} {formatValue(datum.baseline, format, digits)} {formatValue(datum.trained, format, digits)} {formatValue(datum.trained - datum.baseline, format, digits)}
); return ( 0 ? `${invalid} row${invalid === 1 ? " was" : "s were"} omitted because it contained a non-finite value.` : undefined} /> ); }