Files
kjol/go/jsruntime/uikit/Chart.tsx

92 lines
2.6 KiB
TypeScript

import { createEffect, onCleanup, onMount } from "solid-js";
import { Chart, registerables } from "chart.js";
// chart.js v4 is tree-shakeable and ships nothing registered by default; register
// all controllers/elements/scales once so any chart type works (the old UMD shim
// did this implicitly).
Chart.register(...registerables);
type ChartType = "line" | "bar" | "radar" | "doughnut" | "polarArea" | "bubble" | "pie" | "scatter";
interface ReactiveChartProps {
type: ChartType;
data: unknown;
options?: object;
class?: string;
}
interface ChartInstance {
destroy(): void;
update(): void;
data: unknown;
options: object;
}
type ChartCtor = new (ctx: CanvasRenderingContext2D, cfg: object) => ChartInstance;
export default function ReactiveChart(props: ReactiveChartProps) {
let canvasRef: HTMLCanvasElement | undefined;
let chartInstance: ChartInstance | null = null;
onMount(() => {
// Defer initialization until the canvas is connected to the document.
// @solidjs/router creates route components before inserting them into
// the DOM, and Chart.js needs getComputedStyle which requires a
// connected element with ownerDocument.defaultView.
const init = () => {
if (!canvasRef) return;
if (!canvasRef.isConnected) {
requestAnimationFrame(init);
return;
}
const ctx = canvasRef.getContext("2d");
if (!ctx) return;
chartInstance = new (Chart as unknown as ChartCtor)(ctx, {
type: props.type,
data: props.data,
options: {
responsive: true,
maintainAspectRatio: false,
...(props.options ?? {}),
},
});
};
init();
});
onCleanup(() => {
if (chartInstance) {
chartInstance.destroy();
chartInstance = null;
}
});
createEffect(() => {
const data = props.data;
if (chartInstance) {
chartInstance.data = data;
chartInstance.update();
}
});
createEffect(() => {
const options = props.options;
if (chartInstance) {
chartInstance.options = {
responsive: true,
maintainAspectRatio: false,
...(options ?? {}),
};
chartInstance.update();
}
});
return (
<div class={"h-full " + (props.class || "")}>
<canvas ref={(el: HTMLCanvasElement) => canvasRef = el}></canvas>
</div>
);
}