114 lines
2.9 KiB
TypeScript
114 lines
2.9 KiB
TypeScript
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<HTMLDivElement>(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 (
|
|
<div
|
|
className="line-chart"
|
|
data-chart-library="uplot"
|
|
data-severity={severity}
|
|
data-values={values.join(",")}
|
|
role="img"
|
|
aria-label={label}
|
|
>
|
|
<div ref={chartElementRef} className="line-chart__canvas" aria-hidden="true" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
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";
|
|
}
|