All files / src/components/dashboard Sparkline.tsx

0% Statements 0/58
0% Branches 0/1
0% Functions 0/1
0% Lines 0/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88                                                                                                                                                                               
/**
 * Inline SVG sparkline — no external dependencies.
 *
 * Renders a polyline + gradient area fill for a series of numeric values.
 */
 
import { useId } from "react";
 
type SparklineProps = {
  values: number[];
  width?: number;
  height?: number;
  /** CSS colour for the line. Defaults to the accent token. */
  color?: string;
};
 
export function Sparkline({
  values,
  width = 120,
  height = 32,
  color = "var(--color-accent)",
}: SparklineProps) {
  const gradientId = useId();
 
  if (values.length < 2) {
    return (
      <svg
        width={width}
        height={height}
        viewBox={`0 0 ${width} ${height}`}
        className="sparkline sparkline--empty"
      />
    );
  }
 
  const pad = 2;
  const innerW = width - pad * 2;
  const innerH = height - pad * 2;
 
  const max = Math.max(...values);
  const min = Math.min(...values);
  const range = max - min || 1;
 
  const points = values.map((v, i) => {
    const x = pad + (i / (values.length - 1)) * innerW;
    const y = pad + innerH - ((v - min) / range) * innerH;
    return `${x.toFixed(1)},${y.toFixed(1)}`;
  });
 
  const polyline = points.join(" ");
 
  // Area fill: close the path along the bottom edge.
  const first = points[0];
  const last = points[points.length - 1];
  const areaPath = [
    `M${first}`,
    ...points.slice(1).map((p) => `L${p}`),
    `L${last.split(",")[0]},${height}`,
    `L${first.split(",")[0]},${height}`,
    "Z",
  ].join(" ");
 
  return (
    <svg
      width={width}
      height={height}
      viewBox={`0 0 ${width} ${height}`}
      className="sparkline"
    >
      <defs>
        <linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity={0.25} />
          <stop offset="100%" stopColor={color} stopOpacity={0} />
        </linearGradient>
      </defs>
      <path d={areaPath} fill={`url(#${gradientId})`} />
      <polyline
        points={polyline}
        fill="none"
        stroke={color}
        strokeWidth={1.5}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}