import "uplot/dist/uPlot.min.css"; import { useEffect, useRef } from "react"; import type { UiSeverity } from "../../types"; export interface LineChartProps { values?: number[]; severity?: UiSeverity; label?: string; } export function LineChart({ values = [], severity = "neutral", label = "Telemetry trend", }: LineChartProps) { const chartElementRef = useRef(null); const valuesKey = values.join(","); useEffect(() => { const chartElement = chartElementRef.current; if (!chartElement) return; let chart: ChartInstance | null = null; let resizeObserver: ResizeObserver | null = null; let disposed = false; void import("uplot").then((module) => { if (disposed || !chartElementRef.current) return; const element = chartElementRef.current; chart = new module.default(chartOptions(element, severity), chartData(values), element); if (typeof ResizeObserver !== "undefined") { resizeObserver = new ResizeObserver(() => { chart?.setSize(chartSize(element)); }); resizeObserver.observe(element); } }); return () => { disposed = true; resizeObserver?.disconnect(); chart?.destroy(); }; }, [severity, valuesKey, values]); return (
); } type ChartInstance = { destroy(): void; setSize(size: { width: number; height: number }): void; }; function chartData(values: number[]): import("uplot").AlignedData { const normalized = values.length ? values : [0, 0]; return [ normalized.map((_, index) => index), normalized.map((value) => Math.max(0, Number(value) || 0)), ]; } function chartOptions( element: HTMLElement, severity: UiSeverity, ): import("uplot").Options { return { ...chartSize(element), cursor: { show: false }, legend: { show: false }, padding: [2, 0, 2, 0], scales: { x: { time: false }, y: { auto: true }, }, axes: [{ show: false }, { show: false }], series: [ {}, { stroke: chartStroke(element, severity), width: 2, points: { show: false }, }, ], }; } function chartSize(element?: HTMLElement): { width: number; height: number } { return { width: Math.max(80, Math.round(element?.clientWidth || 120)), height: Math.max(20, Math.round(element?.clientHeight || 24)), }; } function chartStroke(element: HTMLElement, severity: UiSeverity): string { const styles = window.getComputedStyle(element.closest(".line-chart") || element); const currentColor = styles.color; if (currentColor) return currentColor; if (severity === "danger") return "#ff1744"; if (severity === "warning") return "#ffb020"; return "#d7ff00"; }