import { useId, useMemo, type CSSProperties } from 'react'; export interface SparklineProps { /** Series values. Length 2+. */ data: number[]; /** Width in px. Default 120. */ width?: number; /** Height in px. Default 36. */ height?: number; /** Stroke color. Default `var(--bl-accent)`. */ stroke?: string; /** Stroke width. Default 1.75. */ strokeWidth?: number; /** Fill the area under the line. Default true. */ fill?: boolean; /** Highlight the final point. Default true. */ showLastPoint?: boolean; /** Aria label. */ ariaLabel?: string; className?: string; style?: CSSProperties; } /** * `` — tiny inline trend line. Pure SVG, no runtime layout * cost. Auto-scales the Y axis to the data range; clamps tiny ranges * so flat lines still render visibly. */ export function Sparkline({ data, width = 120, height = 36, stroke = 'var(--bl-accent, #6366f1)', strokeWidth = 1.75, fill = true, showLastPoint = true, ariaLabel, className, style, }: SparklineProps) { const { path, areaPath, lastX, lastY } = useMemo(() => { if (data.length < 2) { return { path: '', areaPath: '', lastX: 0, lastY: 0 }; } const min = Math.min(...data); const max = Math.max(...data); const range = max - min || 1; const pad = strokeWidth + 1; const innerW = width - pad * 2; const innerH = height - pad * 2; const stepX = innerW / (data.length - 1); const pts = data.map((v, i) => { const x = pad + i * stepX; const y = pad + innerH - ((v - min) / range) * innerH; return [x, y] as const; }); const path = pts.map(([x, y], i) => (i === 0 ? `M${x},${y}` : `L${x},${y}`)).join(' '); const areaPath = pts.length > 1 ? `${path} L${pts[pts.length - 1]![0]},${height} L${pts[0]![0]},${height} Z` : ''; const [lx, ly] = pts[pts.length - 1]!; return { path, areaPath, lastX: lx, lastY: ly }; }, [data, width, height, strokeWidth]); // useId() is SSR-stable; using Math.random() here would mismatch on hydration. const reactId = useId(); const gradId = `bl-spark-${reactId.replace(/[^a-zA-Z0-9_-]/g, '')}`; return ( {fill && ( <> )} {showLastPoint && data.length >= 2 && ( )} ); }