|
2 | 2 | import CanvasJSReact from '@canvasjs/react-charts'; |
3 | 3 | import styles from "./ChartElement.module.scss"; |
4 | 4 | import { AiOutlineCloseCircle } from 'react-icons/ai' |
5 | | -import { memo, useRef, useEffect, useCallback } from "react"; |
6 | | -import { IChartApi, ISeriesApi, UTCTimestamp } from 'lightweight-charts'; |
7 | | -import { MeasurementId, NumericMeasurementInfo, UpdateFunction, useGlobalTicker } from 'common'; |
| 5 | +import { memo } from "react"; |
| 6 | +import { MeasurementId, useMeasurementsStore } from 'common'; |
8 | 7 | import { ChartId, useChartStore } from '../ChartStore'; |
9 | | - |
10 | | -type MeasurementToSerieAndUpdater = Map<MeasurementId, { DataSerie: ISeriesApi<"Line">, Updater: UpdateFunction }>; |
| 8 | +import { ChartCanvas } from './ChartCanvas/ChartCanvas'; |
| 9 | +import { ChartLegend } from './ChartLegend/ChartLegend'; |
11 | 10 |
|
12 | 11 | type Props = { |
13 | 12 | chartId: ChartId; |
14 | | - chartHeight: number; |
15 | 13 | }; |
16 | 14 |
|
17 | 15 | // React component that keeps the chart render and measurements represented on it. |
18 | | -export const ChartElement = memo(({ chartId, chartHeight }: Props) => { |
| 16 | +export const ChartElement = memo(({ chartId }: Props) => { |
19 | 17 |
|
20 | | - // Ref to the CanvasJS chart render |
21 | | - const chartContainerRef = useRef<HTMLDivElement>(null); |
22 | | - // Chart generated into chartContainer |
23 | | - let chart : IChartApi; |
24 | | - let chartLegend = useRef<HTMLDivElement>(null); |
25 | | - // Ref to chart's data series |
26 | | - const chartDataSeries = useRef<MeasurementToSerieAndUpdater>(new Map()); |
| 18 | + const getMeasurementsFromChart = useChartStore((state) => state.getMeasurementsFromChart); |
27 | 19 | const removeChart = useChartStore((state) => state.removeChart); |
| 20 | + const addMeasurementToChart = useChartStore((state) => state.addMeasurementToChart); |
| 21 | + const removeMeasurementFromChart = useChartStore((state) => state.removeMeasurementFromChart); |
| 22 | + const measurements = getMeasurementsFromChart(chartId); |
| 23 | + const charts = useChartStore((state) => state.charts); |
28 | 24 |
|
29 | | - // INITIALIZE ALL THE CHART INITIAL SETTINGS. |
30 | | - useEffect(() => { |
31 | | - // const measurement = getNumericMeasurementInfo(measurementId); |
32 | | - // const chartLegendItem = createChartLegendItem(measurement); |
33 | | - // chartLegendItem.onclick = (ev) => removeSeriesFromChart(ev, measurementId); |
34 | | - // if (chartLegend) chartLegend.current?.appendChild(chartLegendItem); |
35 | | - |
36 | | - // const handleResize = () => { |
37 | | - // if (chartContainerRef.current) |
38 | | - // chart.applyOptions({ width: chartContainerRef.current.clientWidth }); |
39 | | - // }; |
40 | | - |
41 | | - // const resizeObserver = new ResizeObserver(handleResize); |
42 | | - // if (chartContainerRef.current) |
43 | | - // resizeObserver.observe(chartContainerRef.current); |
44 | | - |
45 | | - // if (!chartContainerRef.current) return; |
46 | | - // chart = createChart(chartContainerRef.current, { |
47 | | - // layout: { |
48 | | - // background: { type: ColorType.Solid, color: "white" }, |
49 | | - // textColor: "black", |
50 | | - // }, |
51 | | - // width: chartContainerRef.current.clientWidth, |
52 | | - // height: chartHeight, |
53 | | - // timeScale: { |
54 | | - // timeVisible: true, |
55 | | - // secondsVisible: true, |
56 | | - // fixLeftEdge: true, |
57 | | - // fixRightEdge: true, |
58 | | - // lockVisibleTimeRangeOnResize: true, |
59 | | - // } |
60 | | - // }); |
61 | | - |
62 | | - // chartDataSeries.current.set(measurementId, {DataSerie: chart.addLineSeries({ color: measurement.color }), Updater: measurement.getUpdate}); |
| 25 | + const getNumericMeasurementInfo = useMeasurementsStore((state) => state.getNumericMeasurementInfo); |
63 | 26 |
|
64 | | - // return () => { |
65 | | - // resizeObserver.disconnect(); |
66 | | - // chart.remove(); |
67 | | - // }; |
68 | | - }); |
69 | | - |
70 | | - useEffect(() => { |
71 | | - // if(chartContainerRef.current) |
72 | | - // chartContainerRef.current.ondrop = (ev) => { |
73 | | - // ev.stopPropagation(); |
74 | | - // const measurementId = ev.dataTransfer?.getData("id"); |
75 | | - // if (measurementId === undefined || chartDataSeries.current.get(measurementId)) return; |
76 | | - // const measurement = getMeasurementInfo(measurementId); |
77 | | - // chartDataSeries.current.set(measurementId, {DataSerie: chart.addLineSeries({ color: measurement.color }), Updater: measurement.getUpdate}); |
78 | | - // const chartLegendItem = createChartLegendItem(measurement); |
79 | | - // chartLegendItem.onclick = (ev) => removeSeriesFromChart(ev, measurementId); |
80 | | - // if (chartLegend) chartLegend.current?.appendChild(chartLegendItem); |
81 | | - // }; |
82 | | - }); |
83 | | - |
84 | | - // Update the chart with the new measurements values. |
85 | | - useGlobalTicker(() => { |
86 | | - chartDataSeries.current.forEach((serie) => { |
87 | | - const {DataSerie, Updater} = serie; |
88 | | - const update = Updater(); |
89 | | - const now = (Date.now() / 1000) as UTCTimestamp; |
90 | | - DataSerie.update({ time: now, value: update }); |
91 | | - }); |
92 | | - }); |
93 | | - |
94 | | - const removeSeriesFromChart = useCallback((ev: MouseEvent, measurementId: MeasurementId) => { |
| 27 | + const handleDrop = (ev: React.DragEvent<HTMLDivElement>) => { |
| 28 | + ev.preventDefault(); |
95 | 29 | ev.stopPropagation(); |
96 | | - chartDataSeries.current.delete(measurementId); |
97 | | - if(chartDataSeries.current.size === 0) removeChart(chartId); |
98 | | - const target = ev.target as HTMLDivElement; |
99 | | - const parent = target.parentElement as HTMLDivElement; |
100 | | - parent.remove(); |
101 | | - }, []); |
| 30 | + const id = ev.dataTransfer.getData("id"); |
| 31 | + const measurementInfo = getNumericMeasurementInfo(id); |
| 32 | + addMeasurementToChart(chartId, measurementInfo); |
| 33 | + }; |
102 | 34 |
|
103 | 35 | return ( |
104 | 36 | <div |
105 | 37 | className={styles.chartWrapper} |
| 38 | + onDrop={handleDrop} |
106 | 39 | onDragEnter={(ev) => ev.preventDefault()} |
107 | 40 | onDragOver={(ev) => ev.preventDefault()} |
108 | | - > |
109 | | - |
| 41 | + > |
110 | 42 | <div className={styles.chart}> |
111 | 43 | <AiOutlineCloseCircle |
112 | 44 | size={30} |
113 | 45 | cursor="pointer" |
114 | 46 | onClick={() => removeChart(chartId)} |
115 | 47 | /> |
116 | | - <div ref={chartContainerRef}></div> |
117 | | - <div className={styles.chartLegend} ref={chartLegend}></div> |
| 48 | + {measurements ? |
| 49 | + ( |
| 50 | + <> |
| 51 | + <ChartCanvas |
| 52 | + measurements={measurements} |
| 53 | + /> |
| 54 | + <ChartLegend |
| 55 | + removeMeasurement={(measurementId: MeasurementId) => removeMeasurementFromChart(chartId, measurementId)} |
| 56 | + /> |
| 57 | + </>) |
| 58 | + : "Error: No measurements found"} |
118 | 59 | </div> |
119 | 60 | </div> |
120 | 61 | ); |
121 | 62 | }); |
122 | | - |
123 | | -function createChartLegendItem(measurement: NumericMeasurementInfo) { |
124 | | - const legendItem = document.createElement("div"); |
125 | | - legendItem.setAttribute("data-id", measurement.id); |
126 | | - legendItem.className = styles.chartLegendItem; |
127 | | - const seriesColor = document.createElement("div"); |
128 | | - seriesColor.className = styles.chartLegendItemColor; |
129 | | - seriesColor.style.backgroundColor = measurement.color; |
130 | | - const seriesName = document.createElement("p"); |
131 | | - seriesName.innerText = measurement.name; |
132 | | - legendItem.appendChild(seriesColor); |
133 | | - legendItem.appendChild(seriesName); |
134 | | - return legendItem; |
135 | | -} |
0 commit comments