Skip to content

Commit 9cfbb46

Browse files
committed
chart legend and canvas data synchronized
1 parent b031041 commit 9cfbb46

8 files changed

Lines changed: 93 additions & 108 deletions

File tree

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
import { NumericMeasurementInfo } from "common"
1+
import { NumericMeasurementInfo } from "common";
22

33
interface Props {
4-
measurements: NumericMeasurementInfo[];
5-
}
4+
measurements: NumericMeasurementInfo[];
5+
}
66

7-
export const ChartCanvas = ({ measurements } : Props) => {
8-
return (
9-
<div>
10-
{measurements.map((measurement) => (
11-
<div key={measurement.id}>
12-
{measurement.id}
7+
export const ChartCanvas = ({ measurements }: Props) => {
8+
9+
return (
10+
<div>
11+
{measurements.map((measurement) => (
12+
<div key={measurement.id}>{measurement.id}</div>
13+
))}
1314
</div>
14-
))}
15-
</div>
16-
)
17-
}
15+
);
16+
};
Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,13 @@
11
@use "src/styles/styles";
22

3-
.chartWrapper {
4-
flex-shrink: 0;
3+
.chartWrapper {
4+
flex-shrink: 0;
55

6-
border: 1px solid black;
7-
border-radius: styles.$normal-border-radius;
8-
overflow: hidden;
9-
}
6+
border: 1px solid black;
7+
border-radius: styles.$normal-border-radius;
8+
overflow: hidden;
9+
}
1010

11-
.chart {
12-
padding: 0.8rem;
13-
}
14-
15-
.chartLegend {
16-
display: flex;
17-
gap: 1rem;
18-
flex-wrap: wrap;
19-
align-items: flex-start;
20-
justify-content: flex-start;
11+
.chart {
2112
padding: 0.8rem;
22-
}
23-
24-
.chartLegendItem {
25-
display: flex;
26-
align-items: center;
27-
gap: 0.5rem;
28-
cursor: pointer;
29-
}
30-
31-
.chartLegendItemColor {
32-
width: 1rem;
33-
height: 1rem;
34-
}
13+
}
Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,27 @@
1-
// @ts-ignore
2-
import CanvasJSReact from '@canvasjs/react-charts';
31
import styles from "./ChartElement.module.scss";
42
import { AiOutlineCloseCircle } from 'react-icons/ai'
5-
import { memo } from "react";
6-
import { MeasurementId, useMeasurementsStore } from 'common';
7-
import { ChartId, useChartStore } from '../ChartStore';
3+
import { useMeasurementsStore } from 'common';
4+
import { ChartInfo, useChartStore } from '../ChartStore';
85
import { ChartCanvas } from './ChartCanvas/ChartCanvas';
96
import { ChartLegend } from './ChartLegend/ChartLegend';
7+
import { useCallback } from "react";
108

119
type Props = {
12-
chartId: ChartId;
10+
chart: ChartInfo;
1311
};
1412

1513
// React component that keeps the chart render and measurements represented on it.
16-
export const ChartElement = memo(({ chartId }: Props) => {
17-
18-
const getMeasurementsFromChart = useChartStore((state) => state.getMeasurementsFromChart);
14+
export const ChartElement = ({ chart }: Props) => {
1915
const removeChart = useChartStore((state) => state.removeChart);
2016
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);
24-
2517
const getNumericMeasurementInfo = useMeasurementsStore((state) => state.getNumericMeasurementInfo);
2618

27-
const handleDrop = (ev: React.DragEvent<HTMLDivElement>) => {
28-
ev.preventDefault();
19+
const handleDrop = useCallback((ev: React.DragEvent<HTMLDivElement>) => {
2920
ev.stopPropagation();
3021
const id = ev.dataTransfer.getData("id");
3122
const measurementInfo = getNumericMeasurementInfo(id);
32-
addMeasurementToChart(chartId, measurementInfo);
33-
};
23+
addMeasurementToChart(chart.chartId, measurementInfo);
24+
}, []);
3425

3526
return (
3627
<div
@@ -40,23 +31,14 @@ export const ChartElement = memo(({ chartId }: Props) => {
4031
onDragOver={(ev) => ev.preventDefault()}
4132
>
4233
<div className={styles.chart}>
43-
<AiOutlineCloseCircle
34+
<AiOutlineCloseCircle
4435
size={30}
4536
cursor="pointer"
46-
onClick={() => removeChart(chartId)}
37+
onClick={() => removeChart(chart.chartId)}
4738
/>
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"}
39+
<ChartCanvas measurements={chart.measurements} />
40+
<ChartLegend chartId={chart.chartId} measurements={chart.measurements} />
5941
</div>
6042
</div>
6143
);
62-
});
44+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.chartLegend {
2+
display: flex;
3+
gap: 1rem;
4+
flex-wrap: wrap;
5+
align-items: flex-start;
6+
justify-content: flex-start;
7+
padding: 0.8rem;
8+
}
9+
10+
.chartLegendItem {
11+
display: flex;
12+
align-items: center;
13+
gap: 0.5rem;
14+
cursor: pointer;
15+
}
16+
17+
.chartLegendItemColor {
18+
width: 1rem;
19+
height: 1rem;
20+
}
Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
import styles from './ChartLegend.module.scss';
2-
import { useCallback } from 'react';
3-
import { MeasurementId, NumericMeasurementInfo } from 'common';
1+
import styles from "./ChartLegend.module.scss";
2+
import { useCallback, useEffect, useRef } from "react";
3+
import { MeasurementId, NumericMeasurementInfo } from "common";
4+
import { ChartId, useChartStore } from "components/ChartMenu/ChartStore";
45

56
interface Props {
6-
removeMeasurement: (measurementId: MeasurementId) => void;
7+
chartId: ChartId;
8+
measurements: NumericMeasurementInfo[];
79
}
810

9-
export const ChartLegend = ({ removeMeasurement } : Props) => {
11+
export const ChartLegend = ({ chartId, measurements }: Props) => {
12+
const legendRef = useRef<HTMLDivElement>(null);
13+
const charts = useChartStore((state) => state.charts);
14+
const removeMeasurementFromChart = useChartStore((state) => state.removeMeasurementFromChart);
15+
const removeChart = useChartStore((state) => state.removeChart);
1016

11-
// TODO: IMPLEMENT
12-
// const removeSeriesFromChart = useCallback((ev: MouseEvent, measurementId: MeasurementId) => {
13-
// ev.stopPropagation();
14-
// chartDataSeries.current.delete(measurementId);
15-
// if(chartDataSeries.current.size === 0) removeChart(chartId);
16-
// const target = ev.target as HTMLDivElement;
17-
// const parent = target.parentElement as HTMLDivElement;
18-
// parent.remove();
19-
// }, []);
17+
const removeMeasurement = useCallback((measurementId: MeasurementId) => {
18+
removeMeasurementFromChart(chartId, measurementId);
19+
if (charts.find((chart) => chart.chartId === chartId)?.measurements.length === 0) {
20+
removeChart(chartId);
21+
}
22+
}, []);
2023

21-
return (
22-
<div>ChartLegend</div>
23-
)
24-
}
24+
useEffect(() => {
25+
if (legendRef.current) {
26+
while (legendRef.current.firstChild) {
27+
legendRef.current.removeChild(legendRef.current.firstChild);
28+
}
29+
measurements.forEach((measurement) => {
30+
const newChartLegendItem = createChartLegendItem(measurement);
31+
newChartLegendItem.onclick = (_) => removeMeasurement(measurement.id);
32+
legendRef.current?.appendChild(newChartLegendItem);
33+
});
34+
}
35+
});
36+
37+
return <div className={styles.chartLegend} ref={legendRef}></div>;
38+
};
2539

2640
function createChartLegendItem(measurement: NumericMeasurementInfo) {
2741
const legendItem = document.createElement("div");
@@ -35,4 +49,4 @@ function createChartLegendItem(measurement: NumericMeasurementInfo) {
3549
legendItem.appendChild(seriesColor);
3650
legendItem.appendChild(seriesName);
3751
return legendItem;
38-
}
52+
}

ethernet-view/src/components/ChartMenu/ChartMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ type Props = {
1212
};
1313

1414
export const ChartMenu = ({ sidebarSections }: Props) => {
15-
const getNumericMeasurementInfo = useMeasurementsStore((state) => state.getNumericMeasurementInfo);
1615
const charts = useChartStore((state) => state.charts);
16+
const getNumericMeasurementInfo = useMeasurementsStore((state) => state.getNumericMeasurementInfo);
1717
const addChart = useChartStore((state) => state.addChart);
1818

1919
const handleDrop = (ev: DragEvent<HTMLDivElement>) => {
@@ -44,7 +44,7 @@ export const ChartMenu = ({ sidebarSections }: Props) => {
4444
{charts.map((chart) => (
4545
<ChartElement
4646
key={chart.chartId}
47-
chartId={chart.chartId}
47+
chart={chart}
4848
/>
4949
))}
5050
</div>

ethernet-view/src/components/ChartMenu/ChartStore.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,17 @@ import { NumericMeasurementInfo, MeasurementId } from 'common';
33

44
export type ChartId = string;
55

6-
type ChartInfo = {
6+
export type ChartInfo = {
77
chartId: ChartId,
88
measurements: NumericMeasurementInfo[]
99
};
10-
type ChartSet = ChartInfo[];
10+
export type ChartSet = ChartInfo[];
1111
type ChartStore = {
1212
charts: ChartSet;
1313
addChart: (chartId: ChartId, initialMeasurement: NumericMeasurementInfo) => void;
1414
removeChart: (chartId: ChartId) => void;
1515
addMeasurementToChart: (chartId: ChartId, measurement: NumericMeasurementInfo) => void;
1616
removeMeasurementFromChart: (chartId: ChartId, measurementId: MeasurementId) => void;
17-
getMeasurementsFromChart: (chartId: ChartId) => NumericMeasurementInfo[] | undefined;
1817
};
1918

2019
export const useChartStore = create<ChartStore>((set, get) => ({
@@ -27,18 +26,16 @@ export const useChartStore = create<ChartStore>((set, get) => ({
2726
};
2827

2928
set(state => ({
30-
...state,
3129
charts: [...state.charts, newChart]
3230
}));
3331
},
3432
removeChart: (chartId: ChartId) => {
3533
const chartsDraft = get().charts;
3634
const newCharts = chartsDraft.filter(chart => chart.chartId !== chartId);
3735

38-
set(state => ({
39-
...state,
36+
set(_ => ({
4037
charts: newCharts
41-
}));
38+
}));
4239
},
4340
addMeasurementToChart: (chartId: ChartId, measurement: NumericMeasurementInfo) => {
4441
const chartsDraft = get().charts;
@@ -49,7 +46,6 @@ export const useChartStore = create<ChartStore>((set, get) => ({
4946
chart.measurements.push(measurement);
5047

5148
set(state => ({
52-
...state,
5349
charts: [
5450
...state.charts.slice(0, chartIndex),
5551
chart,
@@ -68,7 +64,6 @@ export const useChartStore = create<ChartStore>((set, get) => ({
6864
chart.measurements = newMeasurements;
6965

7066
set(state => ({
71-
...state,
7267
charts: [
7368
...state.charts.slice(0, chartIndex),
7469
chart,
@@ -77,10 +72,6 @@ export const useChartStore = create<ChartStore>((set, get) => ({
7772
}));
7873
}
7974
},
80-
getMeasurementsFromChart: (chartId: ChartId) => {
81-
const chart = get().charts.find(chart => chart.chartId === chartId);
82-
return chart ? chart.measurements : undefined;
83-
}
8475
}));
8576

8677
function isMeasurementInChart(measurementId: MeasurementId, chart: ChartInfo) {

ethernet-view/src/utils/color.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@ export function lightenHSL(color: string, lOffset: number): string {
6363
const l = matches[3];
6464
const newLightness = Math.min(parseInt(l) + lOffset, 100);
6565
return `hsl(${h}, ${s}%, ${newLightness}%)`;
66-
}
66+
}

0 commit comments

Comments
 (0)