"use client";
import { useId } from "react";
interface SparklineProps {
data: number[];
width?: number;
height?: number;
color?: string;
fillOpacity?: number;
strokeWidth?: number;
className?: string;
animated?: boolean;
}
function buildPoints(
data: number[],
width: number,
height: number,
strokeWidth: number,
): string {
if (data.length === 0) return "";
const padding = strokeWidth;
const drawWidth = width - padding * 2;
const drawHeight = height - padding * 2;
const min = Math.min(...data);
const max = Math.max(...data);
const range = max - min || 1; // avoid division by zero when all values equal
return data
.map((v, i) => {
const x =
data.length === 1
? width / 2
: padding + (i / (data.length - 1)) * drawWidth;
const y = padding + drawHeight - ((v - min) / range) * drawHeight;
return `${x},${y}`;
})
.join(" ");
}
function buildAreaPoints(
linePoints: string,
width: number,
height: number,
strokeWidth: number,
dataLength: number,
): string {
if (!linePoints) return "";
const padding = strokeWidth;
const drawWidth = width - padding * 2;
const bottom = height - padding;
const lastX =
dataLength === 1 ? width / 2 : padding + drawWidth;
const firstX = dataLength === 1 ? width / 2 : padding;
return `${linePoints} ${lastX},${bottom} ${firstX},${bottom}`;
}
export function Sparkline({
data,
width = 60,
height = 20,
color = "currentColor",
fillOpacity = 0.1,
strokeWidth = 1.5,
className,
animated = true,
}: SparklineProps) {
const id = useId();
if (data.length === 0) {
return (
);
}
const linePoints = buildPoints(data, width, height, strokeWidth);
const areaPoints = buildAreaPoints(
linePoints,
width,
height,
strokeWidth,
data.length,
);
// Approximate path length for dash animation
const approxLength = width * 2;
return (
<>
{animated && (
)}
>
);
}