63 lines
1.6 KiB
TypeScript
63 lines
1.6 KiB
TypeScript
import { twMerge } from "tailwind-merge";
|
|
|
|
type Props = {
|
|
data: number[];
|
|
width?: number;
|
|
height?: number;
|
|
className?: string;
|
|
};
|
|
|
|
export default function Sparkline({
|
|
data,
|
|
width = 120,
|
|
height = 44,
|
|
className,
|
|
}: Props) {
|
|
const min = Math.min(...data);
|
|
const max = Math.max(...data);
|
|
const range = max - min || 1;
|
|
const stepX = width / (data.length - 1);
|
|
const pad = 3;
|
|
|
|
const points: [number, number][] = data.map((value, index) => [
|
|
index * stepX,
|
|
height - pad - ((value - min) / range) * (height - pad * 2),
|
|
]);
|
|
|
|
const linePath = points
|
|
.map(
|
|
([x, y], index) =>
|
|
`${index == 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`,
|
|
)
|
|
.join(" ");
|
|
|
|
const areaPath = `${linePath} L${width},${height} L0,${height} Z`;
|
|
const last = points[points.length - 1]!;
|
|
|
|
return (
|
|
<svg
|
|
width={width}
|
|
height={height}
|
|
viewBox={`0 0 ${width} ${height}`}
|
|
className={twMerge("overflow-visible", className)}
|
|
aria-hidden="true"
|
|
>
|
|
<path d={areaPath} fill="currentColor" opacity="0.08" />
|
|
<path
|
|
d={linePath}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="1.5"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
<circle
|
|
cx={last[0]}
|
|
cy={last[1]}
|
|
r="2.5"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
);
|
|
}
|