restructure project, add claudemd
This commit is contained in:
91
web/kit/Chart.tsx
Normal file
91
web/kit/Chart.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user