"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.map((d, i) => ( {d.label} {format(d.value)} ))} {!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) => ( {d.date}: {d.value} ))} Each square is one day. Intensity follows the recorded value. ); }
No measurements yet.
Each square is one day. Intensity follows the recorded value.