From c0d1bbef602996316ac67e9516968658a899eabf Mon Sep 17 00:00:00 2001 From: Kevin Midboe Date: Tue, 30 May 2023 17:24:50 +0200 Subject: [PATCH] Simplified Graph component. Date scales based on timeframe selected --- src/lib/components/Graph.svelte | 180 ++++++++++++++++---------------- 1 file changed, 88 insertions(+), 92 deletions(-) diff --git a/src/lib/components/Graph.svelte b/src/lib/components/Graph.svelte index ca6a80c..5d604de 100644 --- a/src/lib/components/Graph.svelte +++ b/src/lib/components/Graph.svelte @@ -7,9 +7,11 @@ CategoryScale, LinearScale, PointElement, + Tooltip, Title, Legend } from 'chart.js'; + import { getRelativePosition } from 'chart.js/helpers'; import type { ChartDataset } from 'chart.js'; import type IChartFrame from '../interfaces/IChartFrame'; @@ -20,123 +22,112 @@ CategoryScale, LinearScale, PointElement, + Tooltip, Title, Legend ); export let name: string; export let dataFrames: IChartFrame[]; - export let beginAtZero: boolean = true; + export let hideTitle: boolean; let chartCanvas: HTMLCanvasElement; let chart: Chart; - let prevData: any = {}; - interface IDataset { - labels: string[]; - data?: ChartDataset<'line', number[]>; - } + onMount(() => renderChart()); + afterUpdate(() => { + chart.destroy(); + renderChart(); + }); - interface ITemperatureDataset extends IDataset { - inside: ChartDataset<'line', number[]>; - outside?: ChartDataset<'line', number[]>; - } + // Converts Date to format suitable for the current range displayed + function dateLabelsFormatedBasedOnResolution(dataFrames: IChartFrame[]): string[] { + const firstFrame = dataFrames[0]; + const lastFrame = dataFrames[dataFrames.length - 1]; + const deltaSeconds = + (new Date(lastFrame.key).getTime() - new Date(firstFrame.key).getTime()) / 1000; + let dateOptions: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: 'numeric', + day: 'numeric' + }; - interface IHumidityDataset extends IDataset {} - interface IPressureDataset extends IDataset {} - - function pad(num) { - if (num < 10) { - return `0${num}`; + if (deltaSeconds < 3600) { + dateOptions = { hour: 'numeric', minute: 'numeric', second: 'numeric' }; + } else if (deltaSeconds <= 86400) { + dateOptions = { hour: 'numeric', minute: 'numeric' }; + } else if (deltaSeconds <= 2592000) { + dateOptions = { + day: 'numeric', + month: 'numeric', + year: '2-digit', + hour: 'numeric', + minute: 'numeric' + }; } - return num; + + const scaledDate = new Intl.DateTimeFormat('no-NB', dateOptions); + return dataFrames.map((frame) => scaledDate.format(frame.key)); } - function prettierDateString(date) { - return `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${pad(date.getYear() - 100)}`; - } - - function computeTemperatureDataset(): ITemperatureDataset { - const labels: string[] = dataFrames.map( - (frame) => prettierDateString(new Date(frame.key)) || String(frame.key_as_string) - ); - const data: number[] = dataFrames.map((frame) => frame.value); - - return { - labels, - inside: { - label: '℃ inside', - borderColor: '#10e783', - backgroundColor: '#c8f9df', - lineTension: 0.5, - borderWidth: 3, - data - } - }; - } - - function computeHumidityDataset(): IHumidityDataset { - const labels: string[] = dataFrames.map( - (frame) => prettierDateString(new Date(frame.key)) || String(frame.key_as_string) - ); - const data: number[] = dataFrames.map((frame) => frame.value); - - return { - labels, - data: { - label: '% humidity', - borderColor: '#57d2fb', - backgroundColor: '#d4f2fe', - lineTension: 0.5, - borderWidth: 3, - data - } - }; - } - - function computePressureDataset(): IPressureDataset { - const labels: string[] = dataFrames.map( - (frame) => prettierDateString(new Date(frame.key)) || String(frame.key_as_string) - ); - const data: number[] = dataFrames.map((frame) => frame.value); - - return { - labels, - data: { + // set dataset label & colors matching the name sent as prop + function setDataColorAndName(data: ChartDataset) { + if (name === 'Pressure') { + Object.assign(data, { label: 'Bar of pressure', borderColor: '#ef5878', - backgroundColor: '#fbd7de', - lineTension: 0.5, - borderWidth: 3, - data - } - }; + backgroundColor: '#fbd7de' + }); + } else if (name === 'Humidity') { + Object.assign(data, { + label: '% humidity', + borderColor: '#57d2fb', + backgroundColor: '#d4f2fe' + }); + } else if (name === 'Temperature') { + Object.assign(data, { + label: '℃ inside', + borderColor: '#10e783', + backgroundColor: '#c8f9df' + }); + } } function renderChart() { - const context: CanvasRenderingContext2D = chartCanvas.getContext('2d'); + const context: CanvasRenderingContext2D | null = chartCanvas.getContext('2d'); + if (!context) return - let dataset: IDataset | ITemperatureDataset | IHumidityDataset | IPressureDataset; - if (name === 'Temperature') dataset = computeTemperatureDataset(); - else if (name === 'Humidity') dataset = computeHumidityDataset(); - else if (name === 'Pressure') dataset = computePressureDataset(); + // create labels and singular dataset (data) + const labels: string[] = dateLabelsFormatedBasedOnResolution(dataFrames); + const data: ChartDataset = { + data: dataFrames.map((frame) => frame.value), + borderWidth: 3, + }; + // based on name, add label and color options to dataset + setDataColorAndName(data) + + // create chart instance, most here is chart options chart = new Chart(context, { type: 'line', data: { - labels: dataset.labels, - datasets: [dataset?.inside || dataset.data] + labels: labels, + datasets: [data] }, options: { elements: { point: { - radius: 1 + radius: 2 + }, + line: { + tension: 0.5 } }, maintainAspectRatio: false, plugins: { title: { - display: true, + display: !hideTitle, position: 'left', + position: 'top', text: `${name} over time`, font: { size: 20 @@ -163,8 +154,21 @@ // }, mode: 'xy' } + }, + tooltip: { + titleFont: { + size: 14 + }, + bodyFont: { + size: 14 + }, + enabled: true } }, + interaction: { + intersect: false, + mode: 'index' + }, scales: { y: { beginAtZero: false, @@ -187,14 +191,6 @@ chart.update(); } - - onMount(() => renderChart()); - afterUpdate(() => { - console.log('after update run'); - chart.destroy(); - renderChart(); - }); - - +