"use client"; import { useId, type CSSProperties } from "react"; export type ChartPoint = { label: string; value: number }; export function BarChart({ data, label, format = (v) => String(v), className = "", }: { data: readonly ChartPoint[]; label: string; format?: (value: number) => string; className?: string; }) { data = data.filter((d) => Number.isFinite(d.value)); const max = data.reduce((max, d) => Math.max(max, Math.abs(d.value)), 1); return (
{label}
{!data.length &&

No measurements yet.

}
); } export function Sparkline({ values, label, className = "", style, }: { values: readonly number[]; label: string; className?: string; style?: CSSProperties; }) { const id = useId(), finite = values.filter(Number.isFinite); const min = finite.reduce((min, value) => Math.min(min, value), 0), max = finite.reduce((max, value) => Math.max(max, value), 1), span = max - min; const points = finite .map( (v, i) => `${finite.length === 1 ? 120 : (i / Math.max(1, finite.length - 1)) * 240},${56 - ((v - min) / span) * 48}`, ) .join(" "); return ( {`${label}${finite.length ? `: ${finite.join(", ")}` : ": no data"}`} ); } export function Heatmap({ data, label = "Activity", className = "", }: { data: readonly { date: string; value: number }[]; label?: string; className?: string; }) { data = data.filter((d) => Number.isFinite(d.value)); const max = data.reduce((max, d) => Math.max(max, d.value), 1); return (
{label}
    {data.map((d) => (
  1. {d.date}: {d.value}
  2. ))}

Each square is one day. Intensity follows the recorded value.

); }