Chart as common component, accepts signals instead of pure values
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { onMount, onCleanup, createSignal, Component } from "solid-js";
|
||||
import { Chart } from "chart.js";
|
||||
|
||||
import { CityLargeChart } from "./CityLargeChart";
|
||||
import { createCustomChart } from "./CustomChart";
|
||||
import { DataQuery, formatDateString } from "../../cities/helpers";
|
||||
|
||||
export const CityChart: Component<{
|
||||
city: () => string;
|
||||
data: () => [string, number | null][];
|
||||
query: () => DataQuery;
|
||||
}> = (props) => {
|
||||
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>();
|
||||
const [getIsLarge, setIsLarge] = createSignal(false);
|
||||
|
||||
let chart: Chart | undefined;
|
||||
const data: [string, number | null][] = props.data().map(([dateStr, value]) => [
|
||||
formatDateString(dateStr),
|
||||
value,
|
||||
]);
|
||||
|
||||
// Split the data into two arrays for the x and y axis
|
||||
const timestamps = data.map(item => item[0]);
|
||||
const values = data.map(item => item[1]);
|
||||
|
||||
onMount(() => {
|
||||
const ctx = getCanvas()!.getContext("2d")!;
|
||||
chart = createCustomChart(
|
||||
ctx,
|
||||
false,
|
||||
timestamps,
|
||||
values,
|
||||
props.city(),
|
||||
props.query().field,
|
||||
props.query().granularity,
|
||||
);
|
||||
});
|
||||
|
||||
function handleCanvasClick() {
|
||||
setIsLarge(!getIsLarge());
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
chart?.destroy();
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={setCanvas} onClick={handleCanvasClick} />
|
||||
<div style={{ display: getIsLarge() ? "block" : "none" }}>
|
||||
<CityLargeChart city={props.city} data={props.data} query={props.query} close={() => setIsLarge(false)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { onMount, createSignal, Component } from "solid-js";
|
||||
import { Chart } from "chart.js";
|
||||
|
||||
import "../../css/overlay.css"
|
||||
import { createCustomChart } from "./CustomChart";
|
||||
import { DataQuery, formatDateString } from "../../cities/helpers";
|
||||
|
||||
export const CityLargeChart: Component<{
|
||||
city: () => string;
|
||||
data: () => [string, number | null][];
|
||||
query: () => DataQuery;
|
||||
close: () => void
|
||||
}> = (props) => {
|
||||
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>();
|
||||
let chart: Chart;
|
||||
const data: [string, number | null][] = props.data().map(([dateStr, value]) => [
|
||||
formatDateString(dateStr),
|
||||
value,
|
||||
]);
|
||||
|
||||
// Split the data into two arrays for the x and y axis
|
||||
const timestamps = data.map(item => item[0]);
|
||||
const values = data.map(item => item[1]);
|
||||
|
||||
onMount(() => {
|
||||
const ctx = getCanvas()!.getContext('2d')!;
|
||||
|
||||
chart = createCustomChart(
|
||||
ctx,
|
||||
true,
|
||||
timestamps,
|
||||
values,
|
||||
props.city(),
|
||||
props.query().field,
|
||||
props.query().granularity,
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="overlay" onClick={props.close}>
|
||||
<div class="overlay-content">
|
||||
<canvas ref={setCanvas} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
BarElement,
|
||||
BarController,
|
||||
CategoryScale,
|
||||
Chart,
|
||||
LinearScale,
|
||||
LineController,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
} from "chart.js";
|
||||
|
||||
// Register chart elements which will be used
|
||||
Chart.register(
|
||||
BarElement,
|
||||
BarController,
|
||||
LineController,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
CategoryScale,
|
||||
);
|
||||
|
||||
const barChartData = ["precipitation", "sunDuration", "snowAvg"];
|
||||
|
||||
export function createCustomChart(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
showBig: boolean,
|
||||
timestamps: string[],
|
||||
values: Array<number | null>,
|
||||
city: string,
|
||||
field: string,
|
||||
granularity: string,
|
||||
): Chart {
|
||||
const dataConfig = {
|
||||
labels: timestamps,
|
||||
datasets: [{
|
||||
label: field,
|
||||
data: values,
|
||||
fill: false,
|
||||
borderColor: "rgb(75, 192, 192)",
|
||||
backgroundColor: "rgb(75, 192, 192)",
|
||||
tension: 0.1,
|
||||
}]
|
||||
};
|
||||
|
||||
const optionsConfig = {
|
||||
plugins: {
|
||||
title: {
|
||||
display: showBig,
|
||||
text: city,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: showBig,
|
||||
text: granularity,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: showBig,
|
||||
text: field,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (!barChartData.includes(field)) {
|
||||
dataConfig.datasets[0].backgroundColor = "#FFFFFF";
|
||||
}
|
||||
|
||||
return new Chart(ctx, {
|
||||
type: barChartData.includes(field) ? "bar" : "line",
|
||||
data: dataConfig,
|
||||
options: {
|
||||
...optionsConfig,
|
||||
animation: false,
|
||||
spanGaps: false,
|
||||
},
|
||||
plugins: [ canvas_bg_plugin ],
|
||||
});
|
||||
}
|
||||
|
||||
const canvas_bg_plugin = {
|
||||
id: "canvas_bg_plugin",
|
||||
beforeDraw: (chart: any, args: any, options: any) => {
|
||||
const { ctx } = chart;
|
||||
ctx.save();
|
||||
ctx.fillStyle = "#FFFFFF";
|
||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.restore();
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user