@neo4j-ndl/react 3.0.17 → 3.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/charts/Chart.js +415 -195
- package/lib/cjs/charts/Chart.js.map +1 -1
- package/lib/cjs/charts/ChartTooltip.js +5 -0
- package/lib/cjs/charts/ChartTooltip.js.map +1 -1
- package/lib/cjs/charts/Charts.js +4 -0
- package/lib/cjs/charts/Charts.js.map +1 -1
- package/lib/cjs/charts/Dot.js +1 -1
- package/lib/cjs/charts/Dot.js.map +1 -1
- package/lib/cjs/charts/HollowDot.js +31 -0
- package/lib/cjs/charts/HollowDot.js.map +1 -0
- package/lib/cjs/charts/Line.js +32 -1
- package/lib/cjs/charts/Line.js.map +1 -1
- package/lib/esm/charts/Chart.js +416 -202
- package/lib/esm/charts/Chart.js.map +1 -1
- package/lib/esm/charts/ChartTooltip.js +5 -0
- package/lib/esm/charts/ChartTooltip.js.map +1 -1
- package/lib/esm/charts/Charts.js +4 -0
- package/lib/esm/charts/Charts.js.map +1 -1
- package/lib/esm/charts/Dot.js +1 -1
- package/lib/esm/charts/Dot.js.map +1 -1
- package/lib/esm/charts/HollowDot.js +28 -0
- package/lib/esm/charts/HollowDot.js.map +1 -0
- package/lib/esm/charts/Line.js +34 -3
- package/lib/esm/charts/Line.js.map +1 -1
- package/lib/types/charts/Chart.d.ts +21 -15
- package/lib/types/charts/ChartTooltip.d.ts +1 -1
- package/lib/types/charts/HollowDot.d.ts +27 -0
- package/lib/types/charts/Line.d.ts +1 -0
- package/package.json +2 -2
package/lib/esm/charts/Chart.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) "Neo4j"
|
|
@@ -19,30 +19,26 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
19
19
|
* You should have received a copy of the GNU General Public License
|
|
20
20
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
21
21
|
*/
|
|
22
|
-
import { forwardRef, useEffect, useState, useContext, createContext, useImperativeHandle, useRef, Children, isValidElement,
|
|
23
|
-
// memo,
|
|
24
|
-
useMemo, } from 'react';
|
|
22
|
+
import React, { forwardRef, useEffect, useState, useContext, createContext, useImperativeHandle, useRef, Children, isValidElement, useMemo, useCallback, } from 'react';
|
|
25
23
|
import * as d3 from 'd3';
|
|
26
|
-
import { ChartsContext,
|
|
27
|
-
// type ChartSize,
|
|
28
|
-
// type Metadata,
|
|
29
|
-
} from './Charts';
|
|
24
|
+
import { ChartsContext, } from './Charts';
|
|
30
25
|
import { classNames } from '../_common/defaultImports';
|
|
31
|
-
// import { TopXAxis, BottomXAxis, LeftYAxis, RightYAxis } from './Axis';
|
|
32
26
|
import { Line } from './Line';
|
|
33
|
-
// import { LineThreshold } from './LineThreshold';
|
|
34
27
|
import { Dot } from './Dot';
|
|
35
|
-
// import { da, de, he, set } from 'date-fns/locale';
|
|
36
28
|
import { line } from 'd3-shape';
|
|
37
29
|
import { ChartTooltip } from './ChartTooltip';
|
|
38
30
|
import { useDebounceCallback, useResizeObserver } from 'usehooks-ts';
|
|
39
31
|
import { useThrottle } from './utils';
|
|
32
|
+
// Used to visualize the voronoi polygons.
|
|
33
|
+
const SHOULD_SHOW_VORONOI_DEBUG = false;
|
|
34
|
+
// Used to throttle calls to on mouse move over the polygons.
|
|
35
|
+
const THROTTLE_MOUSE_MOVE_MILLISECONDS = 100;
|
|
40
36
|
const defaultChartProps = {
|
|
41
37
|
scales: {},
|
|
42
38
|
pointSelectionType: 'single',
|
|
43
39
|
};
|
|
44
|
-
export const canvasLeftPadding =
|
|
45
|
-
export const canvasRightPadding =
|
|
40
|
+
export const canvasLeftPadding = 0;
|
|
41
|
+
export const canvasRightPadding = 0;
|
|
46
42
|
// eslint-disable-next-line no-redeclare
|
|
47
43
|
export const ChartsChartContext = createContext({
|
|
48
44
|
chartRef: null,
|
|
@@ -52,36 +48,99 @@ export const ChartsChartContext = createContext({
|
|
|
52
48
|
linePointArray: [],
|
|
53
49
|
mousePositionRef: undefined,
|
|
54
50
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
export function getPointPixels(linePoint, scales) {
|
|
52
|
+
const { accessorXAxis, scaleXAxisId } = linePoint;
|
|
53
|
+
const { accessorYAxis, scaleYAxisId } = linePoint;
|
|
54
|
+
const valueX = linePoint[accessorXAxis];
|
|
55
|
+
const valueY = linePoint[accessorYAxis];
|
|
56
|
+
const scaleXAxisObject = scales[scaleXAxisId];
|
|
57
|
+
const scaleYAxisObject = scales[scaleYAxisId];
|
|
58
|
+
const scaleXAxis = scaleXAxisObject.functionWithContentWidthAsRange;
|
|
59
|
+
const scaleYAxis = scaleYAxisObject.functionWithContentHeightAsRange;
|
|
60
|
+
const pointPixelX = scaleXAxis(valueX);
|
|
61
|
+
const pointPixelY = scaleYAxis(valueY);
|
|
62
|
+
return { pointPixelX, pointPixelY };
|
|
63
|
+
}
|
|
64
|
+
function dotsPropsAreEqual(prevProps, nextProps) {
|
|
65
|
+
const isEqual = JSON.stringify(prevProps.selectedPoints) ==
|
|
66
|
+
JSON.stringify(nextProps.selectedPoints) &&
|
|
67
|
+
JSON.stringify(prevProps.metadata) == JSON.stringify(nextProps.metadata) &&
|
|
68
|
+
prevProps.selectedPointRef.current.dataId ===
|
|
69
|
+
nextProps.selectedPointRef.current.dataId;
|
|
70
|
+
return isEqual;
|
|
71
|
+
}
|
|
72
|
+
const DotsMemoized = React.memo(function Dots({ selectedPoints, metadata, selectedPointRef, tooltipAnchorRefCallback, scales, }) {
|
|
73
|
+
console.info('DotsMemoized >> render');
|
|
74
|
+
return (_jsx(_Fragment, { children: selectedPoints.map((linePoint) => {
|
|
75
|
+
const { key, dataId, pointIndex } = linePoint;
|
|
76
|
+
const { pointPixelX, pointPixelY } = getPointPixels(linePoint, scales);
|
|
77
|
+
const { isVisible, color } = metadata[dataId];
|
|
78
|
+
if (!isVisible)
|
|
79
|
+
return false;
|
|
80
|
+
const shouldAttachTooltipAnchorRef = dataId === selectedPointRef.current.dataId &&
|
|
81
|
+
pointIndex === selectedPointRef.current.pointIndex;
|
|
82
|
+
return (_jsx(Dot, { ref: shouldAttachTooltipAnchorRef ? tooltipAnchorRefCallback : null, x: pointPixelX, y: pointPixelY, color: color }, `chart-dot-${key}`));
|
|
83
|
+
}) }));
|
|
84
|
+
}, dotsPropsAreEqual);
|
|
85
|
+
function tooltipPropsAreEqual(prevProps, nextProps) {
|
|
86
|
+
console.info(prevProps, nextProps);
|
|
87
|
+
const isEqual = JSON.stringify(prevProps.selectedPoints) ==
|
|
88
|
+
JSON.stringify(nextProps.selectedPoints) &&
|
|
89
|
+
JSON.stringify(prevProps.metadata) == JSON.stringify(nextProps.metadata) &&
|
|
90
|
+
prevProps.tooltipAnchorRef.current === nextProps.tooltipAnchorRef.current;
|
|
91
|
+
console.info('tooltip >>>>>>>>>>>>>>>>>>>', { isEqual });
|
|
92
|
+
return isEqual;
|
|
93
|
+
}
|
|
94
|
+
const TooltipMemoized = React.memo(function Tooltip({ selectedPoints, metadata, tooltipAnchorRef, }) {
|
|
60
95
|
var _a, _b;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
96
|
+
console.info('Tooltip >> render', tooltipAnchorRef);
|
|
97
|
+
return (_jsxs(ChartTooltip, { anchorRef: tooltipAnchorRef, isOpen: true, children: [_jsx(ChartTooltip.Title, { children: String((_b = selectedPoints[0][(_a = selectedPoints[0]) === null || _a === void 0 ? void 0 : _a.accessorXAxis]) !== null && _b !== void 0 ? _b : '') }), selectedPoints.map((linePoint) => {
|
|
98
|
+
var _a;
|
|
99
|
+
if (linePoint === undefined)
|
|
100
|
+
return null;
|
|
101
|
+
if (metadata[linePoint.dataId].isVisible === false)
|
|
102
|
+
return null;
|
|
103
|
+
const { key, dataId, accessorYAxis } = linePoint;
|
|
104
|
+
const contentKey = `chart-tooltip-content-${key}`;
|
|
105
|
+
console.info(contentKey);
|
|
106
|
+
return (_jsx(ChartTooltip.Content, { leftElement: metadata[dataId].label, rightElement: ((_a = linePoint[accessorYAxis]) === null || _a === void 0 ? void 0 : _a.toString()) || '', indentSquareColor: metadata[dataId].color }, contentKey));
|
|
107
|
+
})] }));
|
|
108
|
+
}, tooltipPropsAreEqual);
|
|
109
|
+
const ChartComponent = forwardRef(function ChartComponent({ children, className, scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChartProps.pointSelectionType, }, ref) {
|
|
110
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
111
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
112
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
113
|
+
console.info('ChartComponent');
|
|
64
114
|
const chartsContextValue = useContext(ChartsContext);
|
|
65
115
|
const { data, metadata } = chartsContextValue;
|
|
66
116
|
const chartRef = useRef(null);
|
|
67
117
|
const contentRef = useRef(null);
|
|
68
|
-
const tooltipAnchorRef = useRef(null);
|
|
118
|
+
// const tooltipAnchorRef = useRef<SVGSVGElement | null>(null);
|
|
119
|
+
const [tooltipAnchorRef, setTooltipAnchorRef] = useState(null);
|
|
120
|
+
const tooltipAnchorRefCallback = useCallback((node) => {
|
|
121
|
+
console.info('useCallback - dep: - set: setTooltipAnchorRef', node);
|
|
122
|
+
setTooltipAnchorRef({
|
|
123
|
+
current: node,
|
|
124
|
+
});
|
|
125
|
+
}, []);
|
|
69
126
|
const horizontalIntersectionLineRef = useRef(null);
|
|
70
127
|
const mousePositionRef = useRef({ x: 0, y: 0 });
|
|
128
|
+
const [draggingPosition, setDraggingPosition] = useState(undefined);
|
|
129
|
+
const [isMouseDown, setIsMouseDown] = useState(false);
|
|
71
130
|
const [hasLines, setHasLines] = useState(false);
|
|
72
131
|
const [contentSize, setContentSize] = useState(undefined);
|
|
73
132
|
// The svg paths calculated for the voronoi polygons, includes
|
|
74
133
|
// the assigned line point for the section for the onHover event.
|
|
75
|
-
const [
|
|
76
|
-
const [linePointArray, setLinePointArray] = useState([]);
|
|
77
|
-
// The point where the tooltip will be anchored.
|
|
134
|
+
const [voronoiPolygons, setVoronoiPolygons] = useState();
|
|
78
135
|
const selectedPointRef = useRef({});
|
|
79
136
|
// The points that will display a thumb and be included in the tooltip.
|
|
80
|
-
const [selectedPoints, setSelectedPoints] = useState(
|
|
81
|
-
|
|
137
|
+
const [selectedPoints, setSelectedPoints] = useState(null);
|
|
138
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
139
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
140
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
82
141
|
const initialScales = useMemo(() => {
|
|
83
142
|
// Map the string scales to actual d3 function scales.
|
|
84
|
-
//Will only re-run if the scalesProp changes.
|
|
143
|
+
// Will only re-run if the scalesProp changes.
|
|
85
144
|
const initialScales = {};
|
|
86
145
|
Object.entries(scalesProp).forEach(([key, value]) => {
|
|
87
146
|
switch (value.type) {
|
|
@@ -125,38 +184,32 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
125
184
|
return initialScales;
|
|
126
185
|
}, [scalesProp]);
|
|
127
186
|
const [scales, setScales] = useState(initialScales);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
187
|
+
const voronoiScale = useMemo(() => {
|
|
188
|
+
const initialVoronoiScale = {
|
|
189
|
+
originalDomain: null,
|
|
190
|
+
functionWithContentWidthAsRange: d3.scaleLinear(),
|
|
191
|
+
functionWithContentHeightAsRange: d3.scaleLinear(),
|
|
192
|
+
function: d3.scaleLinear(),
|
|
193
|
+
};
|
|
194
|
+
return initialVoronoiScale;
|
|
195
|
+
}, []);
|
|
134
196
|
useEffect(() => {
|
|
135
197
|
// Needs to run after first render to determine size of content container.
|
|
136
|
-
// console.info('Chart.tsx - ChartComponent - useEffect - contentSize');
|
|
137
198
|
if (!contentRef.current)
|
|
138
199
|
return;
|
|
139
|
-
// console.info(
|
|
140
|
-
// 'Chart.tsx - ChartComponent - useEffect - contentSize (past guard)',
|
|
141
|
-
// );
|
|
142
200
|
const contentElement = contentRef.current;
|
|
143
201
|
if (contentElement && contentElement.getBoundingClientRect) {
|
|
144
202
|
const newContentSize = {
|
|
145
203
|
width: contentElement.getBoundingClientRect().width,
|
|
146
204
|
height: contentElement.getBoundingClientRect().height,
|
|
147
205
|
};
|
|
148
|
-
// console.info(
|
|
149
|
-
// 'Chart.tsx - ChartComponent - useEffect - setting contentSize',
|
|
150
|
-
// );
|
|
151
206
|
// Will force a re-render, calculations require
|
|
152
207
|
// width and height of the content container.
|
|
153
208
|
setContentSize(newContentSize);
|
|
154
209
|
}
|
|
155
210
|
}, [contentRef]);
|
|
156
211
|
const onResize = useDebounceCallback(() => {
|
|
157
|
-
|
|
158
|
-
// 'Chart.tsx - ChartComponent - useResizeObserver/useDebounceCallback - contentSize',
|
|
159
|
-
// );
|
|
212
|
+
console.info('useDebounceCallback - set: contentSize');
|
|
160
213
|
// Timeout to ensure all rendering is done before we get the size.
|
|
161
214
|
const contentElement = contentRef.current;
|
|
162
215
|
if (contentElement && contentElement.getBoundingClientRect()) {
|
|
@@ -166,9 +219,6 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
166
219
|
canvasRightPadding,
|
|
167
220
|
height: contentElement.getBoundingClientRect().height,
|
|
168
221
|
};
|
|
169
|
-
// console.info(
|
|
170
|
-
// 'Chart.tsx - ChartComponent - useResizeObserver/useDebounceCallback - setting contentSize',
|
|
171
|
-
// );
|
|
172
222
|
setContentSize(newContentSize);
|
|
173
223
|
}
|
|
174
224
|
}, 200);
|
|
@@ -177,28 +227,18 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
177
227
|
onResize: onResize,
|
|
178
228
|
});
|
|
179
229
|
useImperativeHandle(ref, () => {
|
|
180
|
-
// console.info('Chart.tsx - ChartComponent - useImperativeHandle - ref');
|
|
181
230
|
const svgChart = chartRef.current;
|
|
182
231
|
if (!svgChart) {
|
|
183
232
|
return null;
|
|
184
233
|
}
|
|
185
234
|
return svgChart;
|
|
186
235
|
});
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
useEffect(() => {
|
|
192
|
-
// console.info('Chart.tsx - ChartComponent - useEffect - line');
|
|
236
|
+
// The initial line point array is the representation of the full data
|
|
237
|
+
// that came into the component via the prop.
|
|
238
|
+
// useMemo:initialLinePointArray
|
|
239
|
+
const { initialLinePointArray, lineMap } = useMemo(() => {
|
|
193
240
|
if (!data)
|
|
194
|
-
return;
|
|
195
|
-
if (!contentRef.current)
|
|
196
|
-
return;
|
|
197
|
-
const width = contentSize === null || contentSize === void 0 ? void 0 : contentSize.width;
|
|
198
|
-
const height = contentSize === null || contentSize === void 0 ? void 0 : contentSize.height;
|
|
199
|
-
if (!width || !height)
|
|
200
|
-
return;
|
|
201
|
-
// console.info('Chart.tsx - useEffect - line (past guard)');
|
|
241
|
+
return { initialLinePointArray: [], lineMap: {} };
|
|
202
242
|
// This use effect is specific for Line components.
|
|
203
243
|
// If there are no line components then we don't need to do anything.
|
|
204
244
|
const arrayChildren = Children.toArray(children);
|
|
@@ -206,20 +246,18 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
206
246
|
if (!hasLines)
|
|
207
247
|
return;
|
|
208
248
|
setHasLines(true);
|
|
249
|
+
const lineMap = {};
|
|
250
|
+
// Used by react for the key properties.
|
|
251
|
+
let key = 0;
|
|
209
252
|
// Here we are figuring out the min and max values for all the scales.
|
|
210
253
|
// To do this we need to iterate through all of the lines and assign
|
|
211
254
|
// each lines designated scale to a min max object specific for that scale.
|
|
212
|
-
const
|
|
213
|
-
let newScales = Object.assign({}, scales);
|
|
255
|
+
const newInitialLinePointArray = [];
|
|
214
256
|
arrayChildren.forEach((child) => {
|
|
215
|
-
var _a, _b, _c, _d;
|
|
216
257
|
// Per line.
|
|
217
258
|
if (!isValidElement(child) || child.type !== Line)
|
|
218
259
|
return;
|
|
219
|
-
const { scaleXAxis: scaleXAxisId, scaleYAxis: scaleYAxisId, accessorXAxis: initialAccessorXAxis, accessorYAxis: initialAccessorYAxis, dataId, seriesInterval, seriesIntervalStartValue, } = child.props;
|
|
220
|
-
// If it's not visible, don't include it.
|
|
221
|
-
if (metadata[dataId].isVisible === false)
|
|
222
|
-
return;
|
|
260
|
+
const { scaleXAxis: scaleXAxisId, scaleYAxis: scaleYAxisId, accessorXAxis: initialAccessorXAxis, accessorYAxis: initialAccessorYAxis, accessorHollowDot, dataId, seriesInterval, seriesIntervalStartValue, } = child.props;
|
|
223
261
|
// Need to set these values for series type data.
|
|
224
262
|
const accessorXAxis = seriesInterval
|
|
225
263
|
? 'xAxisValue'
|
|
@@ -238,17 +276,20 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
238
276
|
const seriesLineData = lineData;
|
|
239
277
|
singleLinePointArray = seriesLineData.data.map((dataPoint, index) => {
|
|
240
278
|
const newLinePoint = {
|
|
279
|
+
key,
|
|
241
280
|
dataId,
|
|
242
281
|
pointIndex: index,
|
|
243
282
|
dataIndex: lineDataIndex,
|
|
244
283
|
accessorXAxis,
|
|
245
284
|
accessorYAxis,
|
|
285
|
+
accessorHollowDot,
|
|
246
286
|
xAxisValue: dataPoint !== undefined && dataPoint !== null
|
|
247
287
|
? seriesIntervalStartValue + seriesInterval * index
|
|
248
288
|
: null,
|
|
249
289
|
yAxisValue: dataPoint,
|
|
250
290
|
scaleXAxisId,
|
|
251
291
|
scaleYAxisId,
|
|
292
|
+
selectionGroup: null,
|
|
252
293
|
};
|
|
253
294
|
return newLinePoint;
|
|
254
295
|
});
|
|
@@ -258,13 +299,16 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
258
299
|
const nonSeriesData = lineData;
|
|
259
300
|
singleLinePointArray = nonSeriesData.data.map((dataPointArray, index) => {
|
|
260
301
|
const newLinePoint = {
|
|
302
|
+
key,
|
|
261
303
|
dataId,
|
|
262
304
|
pointIndex: index,
|
|
263
305
|
dataIndex: lineDataIndex,
|
|
264
306
|
accessorXAxis,
|
|
265
307
|
accessorYAxis,
|
|
308
|
+
accessorHollowDot,
|
|
266
309
|
scaleXAxisId,
|
|
267
310
|
scaleYAxisId,
|
|
311
|
+
selectionGroup: null,
|
|
268
312
|
};
|
|
269
313
|
dataPointArray.forEach((dataPoint, index) => {
|
|
270
314
|
newLinePoint[nonSeriesData.metadata[index]] = dataPoint;
|
|
@@ -272,19 +316,45 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
272
316
|
return newLinePoint;
|
|
273
317
|
});
|
|
274
318
|
}
|
|
319
|
+
key += 1;
|
|
275
320
|
// Add this single line to the array that holds all of the lines.
|
|
276
|
-
|
|
321
|
+
newInitialLinePointArray.push(...singleLinePointArray);
|
|
322
|
+
// Line map for easier access to points from the same line. No need
|
|
323
|
+
// to iterate through the line point array.
|
|
324
|
+
// console.info('setting lineMap', lineMap);
|
|
325
|
+
lineMap[dataId] = singleLinePointArray;
|
|
326
|
+
});
|
|
327
|
+
const initialLinePointArray = newInitialLinePointArray;
|
|
328
|
+
return { initialLinePointArray, lineMap };
|
|
329
|
+
}, [data, children]) || {};
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
if (!contentRef.current)
|
|
332
|
+
return;
|
|
333
|
+
const width = contentSize === null || contentSize === void 0 ? void 0 : contentSize.width;
|
|
334
|
+
const height = contentSize === null || contentSize === void 0 ? void 0 : contentSize.height;
|
|
335
|
+
if (!width || !height)
|
|
336
|
+
return;
|
|
337
|
+
if (initialLinePointArray === undefined)
|
|
338
|
+
return;
|
|
339
|
+
// Need to reset the originalDomain for each scale.
|
|
340
|
+
let newScales = {};
|
|
341
|
+
Object.entries(scales).forEach(([key, scale]) => {
|
|
342
|
+
newScales[key] = Object.assign(Object.assign({}, scale), { originalDomain: null });
|
|
343
|
+
});
|
|
344
|
+
initialLinePointArray.forEach((linePoint) => {
|
|
345
|
+
var _a, _b;
|
|
346
|
+
const { accessorXAxis, accessorYAxis, scaleXAxisId, scaleYAxisId } = linePoint;
|
|
277
347
|
// Get min and max values for both x and y axis from the data point array.
|
|
278
|
-
const minValueXAxis = d3.min(
|
|
348
|
+
const minValueXAxis = d3.min(initialLinePointArray,
|
|
279
349
|
// @ts-expect-error d3 types are not correct
|
|
280
350
|
(linePoint) => linePoint[accessorXAxis]);
|
|
281
|
-
const maxValueXAxis = d3.max(
|
|
351
|
+
const maxValueXAxis = d3.max(initialLinePointArray,
|
|
282
352
|
// @ts-expect-error d3 types are not correct
|
|
283
353
|
(linePoint) => linePoint[accessorXAxis]);
|
|
284
|
-
const minValueYAxis = d3.min(
|
|
354
|
+
const minValueYAxis = d3.min(initialLinePointArray,
|
|
285
355
|
// @ts-expect-error d3 types are not correct
|
|
286
356
|
(linePoint) => linePoint[accessorYAxis]);
|
|
287
|
-
const maxValueYAxis = d3.max(
|
|
357
|
+
const maxValueYAxis = d3.max(initialLinePointArray,
|
|
288
358
|
// @ts-expect-error d3 types are not correct
|
|
289
359
|
(linePoint) => linePoint[accessorYAxis]);
|
|
290
360
|
const scaleXAxis = newScales[scaleXAxisId];
|
|
@@ -297,24 +367,36 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
297
367
|
let scaleYAxisMin = undefined;
|
|
298
368
|
let scaleYAxisMax = undefined;
|
|
299
369
|
if (scalesProp[scaleXAxisId].domain !== undefined) {
|
|
370
|
+
// The domain value is set manually.
|
|
300
371
|
scaleXAxisMin = scalesProp[scaleXAxisId].domain[0];
|
|
301
372
|
scaleXAxisMax = scalesProp[scaleXAxisId].domain[1];
|
|
302
373
|
}
|
|
303
|
-
else
|
|
374
|
+
else if (scaleXAxis !== undefined &&
|
|
375
|
+
(scaleXAxis === null || scaleXAxis === void 0 ? void 0 : scaleXAxis.originalDomain) !== null) {
|
|
304
376
|
//@ts-expect-error d3 types are not correct - it can handle undefined
|
|
305
|
-
scaleXAxisMin = d3.min([
|
|
377
|
+
scaleXAxisMin = d3.min([scaleXAxis.originalDomain[0], minValueXAxis]);
|
|
306
378
|
//@ts-expect-error d3 types are not correct - it can handle undefined
|
|
307
|
-
scaleXAxisMax = d3.max([
|
|
379
|
+
scaleXAxisMax = d3.max([scaleXAxis.originalDomain[1], maxValueXAxis]);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
scaleXAxisMin = minValueXAxis;
|
|
383
|
+
scaleXAxisMax = maxValueXAxis;
|
|
308
384
|
}
|
|
309
385
|
if (scalesProp[scaleYAxisId].domain !== undefined) {
|
|
386
|
+
// The domain value is set manually.
|
|
310
387
|
scaleYAxisMin = scalesProp[scaleYAxisId].domain[0];
|
|
311
388
|
scaleYAxisMax = scalesProp[scaleYAxisId].domain[1];
|
|
312
389
|
}
|
|
313
|
-
else
|
|
390
|
+
else if (scaleYAxis !== undefined &&
|
|
391
|
+
scaleXAxis.originalDomain !== null) {
|
|
314
392
|
//@ts-expect-error d3 types are not correct - it can handle undefined
|
|
315
|
-
scaleYAxisMin = d3.min([(
|
|
393
|
+
scaleYAxisMin = d3.min([(_a = scaleYAxis.originalDomain) === null || _a === void 0 ? void 0 : _a[0], minValueYAxis]);
|
|
316
394
|
//@ts-expect-error d3 types are not correct - it can handle undefined
|
|
317
|
-
scaleYAxisMax = d3.max([(
|
|
395
|
+
scaleYAxisMax = d3.max([(_b = scaleYAxis.originalDomain) === null || _b === void 0 ? void 0 : _b[1], maxValueYAxis]);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
scaleYAxisMin = minValueYAxis;
|
|
399
|
+
scaleYAxisMax = maxValueYAxis;
|
|
318
400
|
}
|
|
319
401
|
// D3 types are not correct it will be the same type as the original domain.
|
|
320
402
|
// casting it to [number, number] to avoid type errors.
|
|
@@ -337,85 +419,265 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
337
419
|
// Update the specific scale.
|
|
338
420
|
newScales = Object.assign(Object.assign({}, newScales), { [scaleXAxisId]: scaleXAxis, [scaleYAxisId]: scaleYAxis });
|
|
339
421
|
});
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
422
|
+
voronoiScale.function.domain([0, width !== null && width !== void 0 ? width : 0]);
|
|
423
|
+
voronoiScale.originalDomain = [0, width !== null && width !== void 0 ? width : 0];
|
|
424
|
+
voronoiScale.functionWithContentWidthAsRange.domain([0, width !== null && width !== void 0 ? width : 0]);
|
|
425
|
+
voronoiScale.functionWithContentWidthAsRange.range([0, width !== null && width !== void 0 ? width : 0]);
|
|
426
|
+
setScales(newScales);
|
|
427
|
+
}, [contentSize, scalesProp, children, initialLinePointArray]);
|
|
428
|
+
const { linePointArray, pointSelectionGroups } = useMemo(() => {
|
|
429
|
+
if (initialLinePointArray === undefined)
|
|
430
|
+
return {};
|
|
431
|
+
// The following is only used for pointSelectionType x-axis and y-axis
|
|
432
|
+
const pointSelectionGroups = {};
|
|
433
|
+
const linePointArray = initialLinePointArray.map((linePoint) => {
|
|
434
|
+
const { pointPixelX, pointPixelY } = getPointPixels(linePoint, scales);
|
|
435
|
+
const pointPixelAxis = pointSelectionType === 'x-axis' || pointSelectionType === 'single'
|
|
436
|
+
? pointPixelX
|
|
437
|
+
: pointPixelY;
|
|
438
|
+
if (pointPixelAxis === undefined)
|
|
439
|
+
return linePoint;
|
|
440
|
+
linePoint.selectionGroup = pointPixelAxis;
|
|
441
|
+
if (!pointSelectionGroups[pointPixelAxis]) {
|
|
442
|
+
pointSelectionGroups[pointPixelAxis] = [];
|
|
443
|
+
pointSelectionGroups[pointPixelAxis].push(linePoint);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
// Pixel point already exists.
|
|
447
|
+
pointSelectionGroups[pointPixelAxis].push(linePoint);
|
|
448
|
+
}
|
|
449
|
+
return linePoint;
|
|
450
|
+
});
|
|
451
|
+
return { linePointArray, pointSelectionGroups };
|
|
452
|
+
}, [pointSelectionType, initialLinePointArray, scales]);
|
|
453
|
+
const voronoiPolygonPointsArray = useMemo(() => {
|
|
454
|
+
const width = contentSize === null || contentSize === void 0 ? void 0 : contentSize.width;
|
|
455
|
+
const height = contentSize === null || contentSize === void 0 ? void 0 : contentSize.height;
|
|
456
|
+
if (!width || !height)
|
|
457
|
+
return;
|
|
458
|
+
if (lineMap === undefined)
|
|
459
|
+
return;
|
|
460
|
+
if (linePointArray === undefined)
|
|
461
|
+
return;
|
|
462
|
+
const visibleLinePointArray = linePointArray.filter((linePoint) => {
|
|
463
|
+
return metadata[linePoint.dataId].isVisible;
|
|
464
|
+
});
|
|
465
|
+
const delaunay = d3.Delaunay.from(visibleLinePointArray,
|
|
466
|
+
// @ts-expect-error - d3 types are not correct, it can handle null being returned.
|
|
467
|
+
(linePoint) => {
|
|
468
|
+
const { accessorXAxis, scaleXAxisId } = linePoint;
|
|
347
469
|
const valueX = linePoint[accessorXAxis];
|
|
348
|
-
const
|
|
349
|
-
const scaleXAxisObject = newScales[scaleXAxisId];
|
|
350
|
-
const scaleYAxisObject = newScales[scaleYAxisId];
|
|
470
|
+
const scaleXAxisObject = scales[scaleXAxisId];
|
|
351
471
|
const scaleXAxis = scaleXAxisObject.functionWithContentWidthAsRange;
|
|
472
|
+
const scaleXAxisCopy = scaleXAxis.copy();
|
|
473
|
+
scaleXAxisCopy.domain(scaleXAxisObject.originalDomain);
|
|
474
|
+
const pointPixelX = scaleXAxisCopy(valueX);
|
|
475
|
+
// We must return null otherwise some points won't work with voronoi/tooltips.
|
|
476
|
+
return pointPixelX === undefined ? null : pointPixelX;
|
|
477
|
+
}, (linePoint) => {
|
|
478
|
+
const { accessorYAxis, scaleYAxisId } = linePoint;
|
|
479
|
+
const valueY = linePoint[accessorYAxis];
|
|
480
|
+
const scaleYAxisObject = scales[scaleYAxisId];
|
|
352
481
|
const scaleYAxis = scaleYAxisObject.functionWithContentHeightAsRange;
|
|
353
|
-
const
|
|
482
|
+
const scaleYAxisCopy = scaleYAxis.copy();
|
|
483
|
+
scaleYAxisCopy.domain(scaleYAxisObject.originalDomain);
|
|
354
484
|
const pointPixelY = scaleYAxis(valueY);
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
newLinePointArray.push(newLinePoint);
|
|
358
|
-
// We diss line points that have null values as they don't require
|
|
359
|
-
// an on hover event and therfor polygon paths.
|
|
360
|
-
if (valueX === null || valueY === null)
|
|
361
|
-
return;
|
|
362
|
-
if (pointPixelX === null || pointPixelY === null)
|
|
363
|
-
return;
|
|
364
|
-
// Calculating the voroni polygons requires knowing the exact
|
|
365
|
-
// pixel positions of all the line points.
|
|
366
|
-
delaunayPointArray.push([pointPixelX, pointPixelY, newLinePoint]);
|
|
485
|
+
// We must return null otherwise some points won't work with voronoi/tooltips.
|
|
486
|
+
return pointPixelY === undefined ? null : pointPixelY;
|
|
367
487
|
});
|
|
368
|
-
// We have now created the line points and scales.
|
|
369
|
-
// From those we can now calculate the on hover voroni polygons.
|
|
370
|
-
// This must also be calculated after all updates as the polygon
|
|
371
|
-
// paths will change depending on the line points available.
|
|
372
|
-
const delaunayPointArrayLike = Object.assign(Object.assign({}, delaunayPointArray), { length: delaunayPointArray.length });
|
|
373
|
-
const delaunay = d3.Delaunay.from(delaunayPointArrayLike);
|
|
374
488
|
const voronoi = delaunay.voronoi([0, 0, width, height]);
|
|
375
489
|
const voronoiPolygons = voronoi.cellPolygons();
|
|
376
490
|
const voronoiPolygonPointsArray = Array.from(voronoiPolygons);
|
|
491
|
+
return voronoiPolygonPointsArray;
|
|
492
|
+
}, [metadata, linePointArray]);
|
|
493
|
+
useEffect(() => {
|
|
494
|
+
if (isMouseDown)
|
|
495
|
+
return;
|
|
496
|
+
if (voronoiPolygonPointsArray === undefined)
|
|
497
|
+
return;
|
|
498
|
+
if (linePointArray === undefined)
|
|
499
|
+
return;
|
|
500
|
+
const visibleLinePointArray = linePointArray.filter((linePoint) => {
|
|
501
|
+
return metadata[linePoint.dataId].isVisible;
|
|
502
|
+
});
|
|
377
503
|
const delaunayLineFn = line();
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
504
|
+
const newVoronoiPolygons = voronoiPolygonPointsArray.map((voronoiPolygonPoints) => {
|
|
505
|
+
const newVoronoiPolygonPoints = voronoiPolygonPoints.map((voronoiPolygonPoint) => {
|
|
506
|
+
const [x, y] = voronoiPolygonPoint;
|
|
507
|
+
const scale = voronoiScale.functionWithContentWidthAsRange;
|
|
508
|
+
const newX = scale(x);
|
|
509
|
+
const newVoronoiPolygonPoint = [newX, y];
|
|
510
|
+
return newVoronoiPolygonPoint;
|
|
511
|
+
});
|
|
512
|
+
newVoronoiPolygonPoints.index = voronoiPolygonPoints.index;
|
|
513
|
+
const { index } = newVoronoiPolygonPoints;
|
|
514
|
+
const path = delaunayLineFn(newVoronoiPolygonPoints);
|
|
515
|
+
const linePoint = visibleLinePointArray[index];
|
|
516
|
+
const voronoiIndex = index;
|
|
517
|
+
return {
|
|
518
|
+
path,
|
|
519
|
+
linePoint,
|
|
520
|
+
voronoiIndex,
|
|
521
|
+
};
|
|
382
522
|
});
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}, [contentSize, scalesProp, children, data, metadata]);
|
|
388
|
-
const handleMouseMove = (event, linePoint) => {
|
|
523
|
+
setVoronoiPolygons(newVoronoiPolygons);
|
|
524
|
+
}, [voronoiPolygonPointsArray, linePointArray, scales, isMouseDown]);
|
|
525
|
+
const handleMouseMove = (event, voronoiPolygon) => {
|
|
526
|
+
const { linePoint } = voronoiPolygon;
|
|
389
527
|
const [xm, ym] = d3.pointer(event, contentRef.current);
|
|
390
528
|
const mousePixelX = xm;
|
|
391
529
|
const mousePixelY = ym;
|
|
392
530
|
mousePositionRef.current.x = mousePixelX;
|
|
393
531
|
mousePositionRef.current.y = mousePixelY;
|
|
394
|
-
|
|
532
|
+
// Zooming
|
|
533
|
+
if (isMouseDown) {
|
|
534
|
+
// We care about zooming when dragging because we want to fill space
|
|
535
|
+
// showing what is being selected.
|
|
536
|
+
const newDraggingPosition = Object.assign(Object.assign({}, draggingPosition), { endX: mousePixelX });
|
|
537
|
+
setDraggingPosition(newDraggingPosition);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const { pointPixelX, pointPixelY } = getPointPixels(linePoint, scales);
|
|
541
|
+
if (pointPixelX === undefined || pointPixelY === undefined)
|
|
542
|
+
return;
|
|
543
|
+
// Line point
|
|
544
|
+
const { pointIndex, dataId } = linePoint;
|
|
395
545
|
const distance = Math.hypot(pointPixelX - mousePixelX, pointPixelY - mousePixelY);
|
|
396
546
|
const radius = 32;
|
|
397
|
-
const shouldTrigger = distance < radius;
|
|
547
|
+
const shouldTrigger = distance < radius && pointSelectionGroups !== undefined;
|
|
398
548
|
if (shouldTrigger) {
|
|
399
|
-
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
549
|
+
const selectionGroup = linePoint.selectionGroup;
|
|
550
|
+
const hasSelectionGroup = selectionGroup !== null;
|
|
551
|
+
if (!hasSelectionGroup) {
|
|
552
|
+
console.warn('No selection group for point');
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const newSelectedPoints = pointSelectionGroups[selectionGroup];
|
|
556
|
+
const isNewSelectedPoints = newSelectedPoints !== selectedPoints;
|
|
557
|
+
if (isNewSelectedPoints) {
|
|
404
558
|
selectedPointRef.current = { dataId, pointIndex };
|
|
559
|
+
}
|
|
405
560
|
setSelectedPoints(newSelectedPoints);
|
|
406
561
|
}
|
|
407
|
-
else if (selectedPoints.length > 0) {
|
|
408
|
-
// console.info('unset selected points');
|
|
562
|
+
else if (selectedPoints !== null && selectedPoints.length > 0) {
|
|
409
563
|
selectedPointRef.current = {};
|
|
410
|
-
setSelectedPoints(
|
|
564
|
+
setSelectedPoints(null);
|
|
411
565
|
}
|
|
412
566
|
};
|
|
413
|
-
const throttledHandleMouseMove = useThrottle(handleMouseMove,
|
|
567
|
+
const throttledHandleMouseMove = useThrottle(handleMouseMove, THROTTLE_MOUSE_MOVE_MILLISECONDS);
|
|
414
568
|
const handleMouseLeave = () => {
|
|
569
|
+
if (isMouseDown) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
setIsMouseDown(false);
|
|
573
|
+
setDraggingPosition(undefined);
|
|
415
574
|
// Mouse moves outside of the content area.
|
|
416
|
-
if (selectedPoints.length > 0)
|
|
417
|
-
setSelectedPoints(
|
|
575
|
+
if (selectedPoints !== null && selectedPoints.length > 0)
|
|
576
|
+
setSelectedPoints(null);
|
|
418
577
|
};
|
|
578
|
+
const handleMouseDown = (event) => {
|
|
579
|
+
const amountOfMouseClicks = event.detail;
|
|
580
|
+
const isDoubleClick = amountOfMouseClicks === 2;
|
|
581
|
+
if (isDoubleClick &&
|
|
582
|
+
contentSize !== undefined &&
|
|
583
|
+
contentSize.width !== undefined) {
|
|
584
|
+
// If zoomed in, return to the original view.
|
|
585
|
+
Object.entries(scales).forEach(([scaleId, scale]) => {
|
|
586
|
+
if (scale.originalDomain === null)
|
|
587
|
+
return;
|
|
588
|
+
const newScale = Object.assign({}, scale);
|
|
589
|
+
newScale.function.domain(scale.originalDomain);
|
|
590
|
+
newScale.functionWithContentWidthAsRange.domain(scale.originalDomain);
|
|
591
|
+
scales[scaleId] = newScale;
|
|
592
|
+
});
|
|
593
|
+
voronoiScale.function.domain([0, contentSize.width]);
|
|
594
|
+
voronoiScale.functionWithContentWidthAsRange.domain([
|
|
595
|
+
0,
|
|
596
|
+
contentSize.width,
|
|
597
|
+
]);
|
|
598
|
+
}
|
|
599
|
+
// Set start position for dragging.
|
|
600
|
+
const [xm, ym] = d3.pointer(event, contentRef.current);
|
|
601
|
+
const mousePixelX = xm;
|
|
602
|
+
const mousePixelY = ym;
|
|
603
|
+
mousePositionRef.current.x = mousePixelX;
|
|
604
|
+
mousePositionRef.current.y = mousePixelY;
|
|
605
|
+
// Initial dragging position.
|
|
606
|
+
const newDraggingPosition = {
|
|
607
|
+
startX: mousePixelX,
|
|
608
|
+
endX: undefined,
|
|
609
|
+
};
|
|
610
|
+
// Needed for the dragging motion.
|
|
611
|
+
setIsMouseDown(true);
|
|
612
|
+
setDraggingPosition(newDraggingPosition);
|
|
613
|
+
};
|
|
614
|
+
// must attach mouse listener to window
|
|
615
|
+
const handleMouseUp = () => {
|
|
616
|
+
const [xm] = d3.pointer(event, contentRef.current);
|
|
617
|
+
const mousePixelX = xm;
|
|
618
|
+
if ((draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX) === mousePixelX) {
|
|
619
|
+
// The user clicked on the same place, no zooming.
|
|
620
|
+
setIsMouseDown(false);
|
|
621
|
+
setDraggingPosition(undefined);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const leftX = (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX) > (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX)
|
|
625
|
+
? draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX
|
|
626
|
+
: draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX;
|
|
627
|
+
const rightX = (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX) > (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX)
|
|
628
|
+
? draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX
|
|
629
|
+
: draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX;
|
|
630
|
+
Object.entries(scales).forEach(([scaleId, scale]) => {
|
|
631
|
+
const newScale = Object.assign({}, scale);
|
|
632
|
+
newScale.function.domain([
|
|
633
|
+
newScale.function.invert(leftX),
|
|
634
|
+
newScale.function.invert(rightX),
|
|
635
|
+
]);
|
|
636
|
+
newScale.functionWithContentWidthAsRange.domain([
|
|
637
|
+
newScale.functionWithContentWidthAsRange.invert(leftX),
|
|
638
|
+
newScale.functionWithContentWidthAsRange.invert(rightX),
|
|
639
|
+
]);
|
|
640
|
+
scales[scaleId] = newScale;
|
|
641
|
+
});
|
|
642
|
+
// update voronoi scale
|
|
643
|
+
voronoiScale.function.domain([
|
|
644
|
+
voronoiScale.function.invert(leftX),
|
|
645
|
+
voronoiScale.function.invert(rightX),
|
|
646
|
+
]);
|
|
647
|
+
voronoiScale.functionWithContentWidthAsRange.domain([
|
|
648
|
+
voronoiScale.functionWithContentWidthAsRange.invert(leftX),
|
|
649
|
+
voronoiScale.functionWithContentWidthAsRange.invert(rightX),
|
|
650
|
+
]);
|
|
651
|
+
setIsMouseDown(false);
|
|
652
|
+
setDraggingPosition(undefined);
|
|
653
|
+
};
|
|
654
|
+
const isCanvasComponent = (child) => {
|
|
655
|
+
return isValidElement(child) && child.type === Line;
|
|
656
|
+
};
|
|
657
|
+
const width = (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX) && (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX)
|
|
658
|
+
? draggingPosition.endX > draggingPosition.startX
|
|
659
|
+
? draggingPosition.endX - draggingPosition.startX
|
|
660
|
+
: draggingPosition.startX - draggingPosition.endX
|
|
661
|
+
: 1;
|
|
662
|
+
const leftX = (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX) && (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX)
|
|
663
|
+
? (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX) > (draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX)
|
|
664
|
+
? draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.endX
|
|
665
|
+
: draggingPosition === null || draggingPosition === void 0 ? void 0 : draggingPosition.startX
|
|
666
|
+
: undefined;
|
|
667
|
+
const shouldRenderDots = hasLines &&
|
|
668
|
+
contentSize !== undefined &&
|
|
669
|
+
selectedPoints !== null &&
|
|
670
|
+
selectedPoints.length > 0 &&
|
|
671
|
+
metadata !== undefined;
|
|
672
|
+
const shouldRenderTooltip = hasLines &&
|
|
673
|
+
contentSize !== undefined &&
|
|
674
|
+
selectedPoints !== null &&
|
|
675
|
+
selectedPoints.length > 0 &&
|
|
676
|
+
metadata !== undefined &&
|
|
677
|
+
tooltipAnchorRef !== null &&
|
|
678
|
+
tooltipAnchorRef.current !== null;
|
|
679
|
+
if (linePointArray === undefined)
|
|
680
|
+
return null;
|
|
419
681
|
const chartContextValue = {
|
|
420
682
|
chartRef,
|
|
421
683
|
contentRef,
|
|
@@ -425,90 +687,42 @@ scales: scalesProp = defaultChartProps.scales, pointSelectionType = defaultChart
|
|
|
425
687
|
mousePositionRef,
|
|
426
688
|
};
|
|
427
689
|
const chartClasses = classNames(`ndl-charts-chart`, className);
|
|
428
|
-
const contentClasses = classNames(`ndl-charts-chart-content
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
for (let i = 0; i < linePointArray.length; i += 1) {
|
|
435
|
-
const linePoint = linePointArray[i];
|
|
436
|
-
const pointPixelAxis = pointSelectionType === 'x-axis' || pointSelectionType === 'single'
|
|
437
|
-
? 'pointPixelX'
|
|
438
|
-
: 'pointPixelY';
|
|
439
|
-
const pointPixelAxisValue = linePoint[pointPixelAxis];
|
|
440
|
-
if (pointPixelAxisValue === undefined)
|
|
441
|
-
continue;
|
|
442
|
-
if (!pointSelectionGroups[pointPixelAxisValue]) {
|
|
443
|
-
pointSelectionGroups[pointPixelAxisValue] = [];
|
|
444
|
-
pointSelectionGroups[pointPixelAxisValue].push(linePoint);
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
// Pixel point already exists.
|
|
448
|
-
pointSelectionGroups[pointPixelAxisValue].push(linePoint);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return pointSelectionGroups;
|
|
452
|
-
}, [pointSelectionType, linePointArray]);
|
|
453
|
-
const isCanvasComponent = (child) => {
|
|
454
|
-
return isValidElement(child) && child.type === Line;
|
|
455
|
-
};
|
|
456
|
-
return (_jsx(ChartsChartContext.Provider, { value: chartContextValue, children: _jsxs("div", { ref: chartRef, className: chartClasses, onMouseLeave: handleMouseLeave, children: [_jsxs("svg", { ref: contentRef, className: contentClasses, children: [_jsxs("g", { children: [Children.toArray(children).map((child) => {
|
|
690
|
+
const contentClasses = classNames(`ndl-charts-chart-content`);
|
|
691
|
+
const zoomingClasses = classNames(`ndl-charts-chart-zoom`);
|
|
692
|
+
return (_jsx(ChartsChartContext.Provider, { value: chartContextValue, children: _jsxs("div", { ref: chartRef, className: chartClasses, onMouseLeave: handleMouseLeave, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, children: [_jsxs("svg", { ref: contentRef, className: contentClasses, children: [_jsxs("g", { children: [draggingPosition && contentSize && (
|
|
693
|
+
// The zooming rectangle visualizing the selected area.
|
|
694
|
+
_jsx("g", { children: _jsx("rect", { className: zoomingClasses, x: leftX, width: width, height: contentSize.height }) })), Children.toArray(children).map((child) => {
|
|
695
|
+
// Render the lines.
|
|
457
696
|
return isCanvasComponent(child) ? child : null;
|
|
458
697
|
}), hasLines &&
|
|
459
698
|
contentSize &&
|
|
460
699
|
selectedPoints &&
|
|
461
700
|
selectedPoints.length > 0 && (
|
|
462
701
|
// Dotted line for x-axis selection.
|
|
463
|
-
_jsx("line", { ref: horizontalIntersectionLineRef, x1: selectedPoints[0].pointPixelX, x2: selectedPoints[0].pointPixelX, y1: "0", y2: contentSize.height, style: {
|
|
702
|
+
_jsx("line", { ref: horizontalIntersectionLineRef, x1: getPointPixels(selectedPoints[0], scales).pointPixelX, x2: getPointPixels(selectedPoints[0], scales).pointPixelX, y1: "0", y2: contentSize.height, style: {
|
|
464
703
|
strokeDasharray: '5,7',
|
|
465
704
|
stroke: 'rgb(111, 117, 126)',
|
|
466
|
-
} })),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const shouldAttachTooltipAnchorRef = dataId === selectedPointRefDataId &&
|
|
478
|
-
pointIndex === selectedPointRefPointIndex;
|
|
479
|
-
return (_jsx(Dot, { ref: shouldAttachTooltipAnchorRef ? tooltipAnchorRef : null, x: pointPixelX, y: pointPixelY, color: color }, index));
|
|
480
|
-
}
|
|
481
|
-
return false;
|
|
482
|
-
})] }), _jsx("g", { children: hasLines &&
|
|
483
|
-
voroniPolygonPaths &&
|
|
484
|
-
voroniPolygonPaths.map((voroniPolygonPath, index) => {
|
|
485
|
-
const { path, linePoint } = voroniPolygonPath;
|
|
486
|
-
return (_jsx("path", { d: path, className: "action-voronoi", onMouseMove: (event) => {
|
|
487
|
-
throttledHandleMouseMove(event, linePoint);
|
|
705
|
+
} })), shouldRenderDots && (_jsx(DotsMemoized
|
|
706
|
+
// Dots together with tooltip to be rendered
|
|
707
|
+
, {
|
|
708
|
+
// Dots together with tooltip to be rendered
|
|
709
|
+
selectedPoints: selectedPoints, metadata: metadata, selectedPointRef: selectedPointRef, tooltipAnchorRefCallback: tooltipAnchorRefCallback, scales: scales }))] }), _jsx("g", { children: hasLines &&
|
|
710
|
+
voronoiPolygons &&
|
|
711
|
+
voronoiPolygons.map((voronoiPolygon, index) => {
|
|
712
|
+
// Hover area for each line point.
|
|
713
|
+
const { path } = voronoiPolygon;
|
|
714
|
+
return (_jsx("path", { d: path, onMouseMove: (event) => {
|
|
715
|
+
throttledHandleMouseMove(event, voronoiPolygon);
|
|
488
716
|
}, style: {
|
|
489
717
|
strokeWidth: 1,
|
|
490
718
|
stroke: 'black',
|
|
491
719
|
strokeOpacity: 0.5,
|
|
492
720
|
fill: '#fff',
|
|
493
|
-
opacity:
|
|
721
|
+
opacity: SHOULD_SHOW_VORONOI_DEBUG ? 0.5 : 0,
|
|
494
722
|
} }, index));
|
|
495
|
-
}) })] }),
|
|
496
|
-
|
|
497
|
-
, {
|
|
498
|
-
//@ts-expect-error SVG element ref will work in this case.
|
|
499
|
-
anchorRef: tooltipAnchorRef, isOpen: Boolean(hasLines &&
|
|
500
|
-
contentSize &&
|
|
501
|
-
selectedPoints &&
|
|
502
|
-
selectedPoints.length > 0), children: hasLines &&
|
|
503
|
-
contentSize &&
|
|
504
|
-
selectedPoints &&
|
|
505
|
-
selectedPoints.length > 0 && (_jsxs(_Fragment, { children: [_jsx(ChartTooltip.Title, { children: String((_b = selectedPoints[0][(_a = selectedPoints[0]) === null || _a === void 0 ? void 0 : _a.accessorXAxis]) !== null && _b !== void 0 ? _b : '') }), selectedPoints.map((linePoint) => {
|
|
506
|
-
var _a;
|
|
507
|
-
if (linePoint === undefined)
|
|
508
|
-
return null;
|
|
509
|
-
const { dataId, accessorYAxis } = linePoint;
|
|
510
|
-
return (_jsx(ChartTooltip.Content, { leftElement: chartsContextValue.metadata[dataId].label, rightElement: ((_a = linePoint[accessorYAxis]) === null || _a === void 0 ? void 0 : _a.toString()) || '', indentSquareColor: chartsContextValue.metadata[dataId].color }, dataId));
|
|
511
|
-
})] })) })] }) }));
|
|
723
|
+
}) })] }), shouldRenderTooltip && (
|
|
724
|
+
// Tooltip together with the dots to be rendered
|
|
725
|
+
_jsx(TooltipMemoized, { selectedPoints: selectedPoints, metadata: metadata, tooltipAnchorRef: tooltipAnchorRef }))] }) }));
|
|
512
726
|
});
|
|
513
727
|
// Issue with TypeScript forwardRef and subcomponents: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34757#issuecomment-894053907
|
|
514
728
|
const Chart = Object.assign(ChartComponent, {
|