68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
'use client';
|
|
|
|
import 'uplot/dist/uPlot.min.css';
|
|
import uPlot from 'uplot';
|
|
import { CardShell } from './CardShell';
|
|
import { AXIS_BASE, CURSOR_NO_DRAG, makeYScale } from './plotHelpers';
|
|
import { formatSI } from '@/lib/formatNumber';
|
|
import { usePlot } from './usePlot';
|
|
import type { ChartConfig, UpsRow } from '@/lib/types';
|
|
import type { TimeMode } from '@/lib/types';
|
|
|
|
interface Props {
|
|
config: ChartConfig;
|
|
upsRows: UpsRow[];
|
|
timeMode: TimeMode;
|
|
onEdit: () => void;
|
|
onDelete: () => void;
|
|
}
|
|
|
|
export default function UpsChart({ config, upsRows, timeMode, onEdit, onDelete }: Props) {
|
|
const { containerRef, legendRef } = usePlot(
|
|
(el, w, h, lRef) => {
|
|
if (upsRows.length < 2) return null;
|
|
|
|
const sorted = [...upsRows].sort((a, b) =>
|
|
timeMode === 'tick'
|
|
? a.game_tick - b.game_tick
|
|
: new Date(a.real_time).getTime() - new Date(b.real_time).getTime(),
|
|
);
|
|
const xs = sorted.map(r => timeMode === 'tick' ? r.game_tick : new Date(r.real_time).getTime() / 1000);
|
|
const ys = sorted.map(r => r.ups);
|
|
|
|
const xAxis: uPlot.Axis = {
|
|
...AXIS_BASE,
|
|
values: timeMode === 'real'
|
|
? (_u, vals) => vals.map(v => v == null ? '' : new Date(v * 1000).toLocaleTimeString())
|
|
: (_u, vals) => vals.map(v => v == null ? '' : formatSI(v)),
|
|
};
|
|
|
|
return new uPlot({
|
|
width: w,
|
|
height: h,
|
|
cursor: CURSOR_NO_DRAG,
|
|
legend: {
|
|
mount: (_u, legendEl) => {
|
|
if (lRef.current) lRef.current.appendChild(legendEl);
|
|
},
|
|
},
|
|
series: [
|
|
{ label: timeMode === 'tick' ? 'Tick' : 'Time' },
|
|
{ label: 'UPS', stroke: '#4ade80', width: 1.5 },
|
|
],
|
|
axes: [xAxis, { ...AXIS_BASE, values: (_u, vals) => vals.map(v => v == null ? '' : formatSI(v)) }],
|
|
scales: {
|
|
x: { time: false },
|
|
y: makeYScale(config.y_min ?? null, config.y_max ?? null, config.y_scale),
|
|
},
|
|
}, [xs, ys], el);
|
|
},
|
|
[upsRows, config.y_min, config.y_max, config.y_scale, timeMode],
|
|
);
|
|
|
|
return (
|
|
<CardShell title={config.title} onEdit={onEdit} onDelete={onDelete} empty={upsRows.length === 0} legendContainerRef={legendRef}>
|
|
<div ref={containerRef as React.RefObject<HTMLDivElement>} className="w-full h-full" />
|
|
</CardShell>
|
|
);
|
|
} |