@mui/x-charts 7.13.0 → 7.14.0
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/BarChart/BarPlot.js +4 -3
- package/BarChart/extremums.js +42 -7
- package/CHANGELOG.md +82 -2
- package/ChartContainer/useChartContainerProps.js +1 -1
- package/ChartContainer/useDefaultizeAxis.d.ts +2 -1
- package/ChartContainer/useDefaultizeAxis.js +16 -5
- package/ChartsAxisHighlight/ChartsAxisHighlight.js +23 -9
- package/ChartsGrid/ChartsGrid.js +6 -4
- package/ChartsXAxis/ChartsXAxis.js +14 -3
- package/ChartsYAxis/ChartsYAxis.js +8 -2
- package/LineChart/extremums.js +20 -4
- package/ScatterChart/extremums.js +26 -10
- package/context/CartesianProvider/Cartesian.types.d.ts +59 -0
- package/context/CartesianProvider/Cartesian.types.js +5 -0
- package/context/CartesianProvider/CartesianContext.d.ts +1 -22
- package/context/CartesianProvider/CartesianProvider.d.ts +1 -21
- package/context/CartesianProvider/CartesianProvider.js +2 -5
- package/context/CartesianProvider/computeValue.d.ts +18 -31
- package/context/CartesianProvider/computeValue.js +20 -23
- package/context/CartesianProvider/getAxisExtremum.d.ts +3 -2
- package/context/CartesianProvider/getAxisExtremum.js +11 -5
- package/context/CartesianProvider/index.d.ts +1 -0
- package/context/CartesianProvider/index.js +12 -0
- package/context/CartesianProvider/useCartesianContext.d.ts +1 -1
- package/context/CartesianProvider/zoom.d.ts +10 -0
- package/context/CartesianProvider/zoom.js +26 -0
- package/context/DrawingProvider.d.ts +7 -2
- package/context/DrawingProvider.js +11 -3
- package/context/PluginProvider/ExtremumGetter.types.d.ts +11 -1
- package/esm/BarChart/BarPlot.js +4 -3
- package/esm/BarChart/extremums.js +42 -7
- package/esm/ChartContainer/useChartContainerProps.js +1 -1
- package/esm/ChartContainer/useDefaultizeAxis.js +16 -5
- package/esm/ChartsAxisHighlight/ChartsAxisHighlight.js +23 -9
- package/esm/ChartsGrid/ChartsGrid.js +6 -4
- package/esm/ChartsXAxis/ChartsXAxis.js +14 -3
- package/esm/ChartsYAxis/ChartsYAxis.js +8 -2
- package/esm/LineChart/extremums.js +20 -4
- package/esm/ScatterChart/extremums.js +26 -10
- package/esm/context/CartesianProvider/Cartesian.types.js +1 -0
- package/esm/context/CartesianProvider/CartesianProvider.js +2 -5
- package/esm/context/CartesianProvider/computeValue.js +20 -23
- package/esm/context/CartesianProvider/getAxisExtremum.js +11 -5
- package/esm/context/CartesianProvider/index.js +1 -0
- package/esm/context/CartesianProvider/zoom.js +19 -0
- package/esm/context/DrawingProvider.js +11 -3
- package/esm/hooks/useAxisEvents.js +3 -1
- package/esm/internals/domUtils.js +16 -3
- package/esm/internals/index.js +3 -0
- package/esm/models/axis.js +5 -0
- package/esm/tests/firePointerEvent.js +35 -0
- package/hooks/useAxisEvents.js +3 -1
- package/index.js +1 -1
- package/internals/domUtils.d.ts +2 -0
- package/internals/domUtils.js +19 -5
- package/internals/index.d.ts +2 -0
- package/internals/index.js +17 -1
- package/models/axis.d.ts +4 -0
- package/models/axis.js +5 -0
- package/modern/BarChart/BarPlot.js +4 -3
- package/modern/BarChart/extremums.js +42 -7
- package/modern/ChartContainer/useChartContainerProps.js +1 -1
- package/modern/ChartContainer/useDefaultizeAxis.js +16 -5
- package/modern/ChartsAxisHighlight/ChartsAxisHighlight.js +23 -9
- package/modern/ChartsGrid/ChartsGrid.js +6 -4
- package/modern/ChartsXAxis/ChartsXAxis.js +14 -3
- package/modern/ChartsYAxis/ChartsYAxis.js +8 -2
- package/modern/LineChart/extremums.js +20 -4
- package/modern/ScatterChart/extremums.js +26 -10
- package/modern/context/CartesianProvider/Cartesian.types.js +1 -0
- package/modern/context/CartesianProvider/CartesianProvider.js +2 -5
- package/modern/context/CartesianProvider/computeValue.js +20 -23
- package/modern/context/CartesianProvider/getAxisExtremum.js +11 -5
- package/modern/context/CartesianProvider/index.js +1 -0
- package/modern/context/CartesianProvider/zoom.js +19 -0
- package/modern/context/DrawingProvider.js +11 -3
- package/modern/hooks/useAxisEvents.js +3 -1
- package/modern/index.js +1 -1
- package/modern/internals/domUtils.js +16 -3
- package/modern/internals/index.js +3 -0
- package/modern/models/axis.js +5 -0
- package/modern/tests/firePointerEvent.js +35 -0
- package/package.json +4 -4
- package/tests/firePointerEvent.js +42 -0
- package/context/CartesianProvider/normalizeAxis.d.ts +0 -5
- package/context/CartesianProvider/normalizeAxis.js +0 -23
- package/esm/context/CartesianProvider/normalizeAxis.js +0 -15
- package/modern/context/CartesianProvider/normalizeAxis.js +0 -15
|
@@ -191,6 +191,7 @@ function BarPlot(props) {
|
|
|
191
191
|
barLabel
|
|
192
192
|
} = props,
|
|
193
193
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
194
|
+
const withoutBorderRadius = !borderRadius || borderRadius <= 0;
|
|
194
195
|
const transition = useTransition(completedData, {
|
|
195
196
|
keys: bar => `${bar.seriesId}-${bar.dataIndex}`,
|
|
196
197
|
from: leaveStyle,
|
|
@@ -199,7 +200,7 @@ function BarPlot(props) {
|
|
|
199
200
|
update: enterStyle,
|
|
200
201
|
immediate: skipAnimation
|
|
201
202
|
});
|
|
202
|
-
const maskTransition = useTransition(masksData, {
|
|
203
|
+
const maskTransition = useTransition(withoutBorderRadius ? [] : masksData, {
|
|
203
204
|
keys: v => v.id,
|
|
204
205
|
from: leaveStyle,
|
|
205
206
|
leave: leaveStyle,
|
|
@@ -208,7 +209,7 @@ function BarPlot(props) {
|
|
|
208
209
|
immediate: skipAnimation
|
|
209
210
|
});
|
|
210
211
|
return /*#__PURE__*/_jsxs(React.Fragment, {
|
|
211
|
-
children: [maskTransition((style, {
|
|
212
|
+
children: [!withoutBorderRadius && maskTransition((style, {
|
|
212
213
|
id,
|
|
213
214
|
hasPositive,
|
|
214
215
|
hasNegative,
|
|
@@ -242,7 +243,7 @@ function BarPlot(props) {
|
|
|
242
243
|
}),
|
|
243
244
|
style: style
|
|
244
245
|
}));
|
|
245
|
-
if (
|
|
246
|
+
if (withoutBorderRadius) {
|
|
246
247
|
return barElement;
|
|
247
248
|
}
|
|
248
249
|
return /*#__PURE__*/_jsx("g", {
|
|
@@ -1,22 +1,57 @@
|
|
|
1
|
+
const createResult = (data, direction) => {
|
|
2
|
+
if (direction === 'x') {
|
|
3
|
+
return {
|
|
4
|
+
x: data,
|
|
5
|
+
y: null
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
x: null,
|
|
10
|
+
y: data
|
|
11
|
+
};
|
|
12
|
+
};
|
|
1
13
|
const getBaseExtremum = params => {
|
|
2
14
|
const {
|
|
3
|
-
axis
|
|
15
|
+
axis,
|
|
16
|
+
getFilters,
|
|
17
|
+
isDefaultAxis
|
|
4
18
|
} = params;
|
|
5
|
-
const
|
|
6
|
-
|
|
19
|
+
const filter = getFilters?.({
|
|
20
|
+
currentAxisId: axis.id,
|
|
21
|
+
isDefaultAxis
|
|
22
|
+
});
|
|
23
|
+
const data = filter ? axis.data?.filter((_, i) => filter({
|
|
24
|
+
x: null,
|
|
25
|
+
y: null
|
|
26
|
+
}, i)) : axis.data;
|
|
27
|
+
const minX = Math.min(...(data ?? []));
|
|
28
|
+
const maxX = Math.max(...(data ?? []));
|
|
7
29
|
return [minX, maxX];
|
|
8
30
|
};
|
|
9
|
-
const getValueExtremum = params => {
|
|
31
|
+
const getValueExtremum = direction => params => {
|
|
10
32
|
const {
|
|
11
33
|
series,
|
|
12
34
|
axis,
|
|
35
|
+
getFilters,
|
|
13
36
|
isDefaultAxis
|
|
14
37
|
} = params;
|
|
15
38
|
return Object.keys(series).filter(seriesId => {
|
|
16
39
|
const yAxisId = series[seriesId].yAxisId ?? series[seriesId].yAxisKey;
|
|
17
40
|
return yAxisId === axis.id || isDefaultAxis && yAxisId === undefined;
|
|
18
41
|
}).reduce((acc, seriesId) => {
|
|
19
|
-
const
|
|
42
|
+
const {
|
|
43
|
+
stackedData
|
|
44
|
+
} = series[seriesId];
|
|
45
|
+
const filter = getFilters?.({
|
|
46
|
+
currentAxisId: axis.id,
|
|
47
|
+
isDefaultAxis,
|
|
48
|
+
seriesXAxisId: series[seriesId].xAxisId ?? series[seriesId].xAxisKey,
|
|
49
|
+
seriesYAxisId: series[seriesId].yAxisId ?? series[seriesId].yAxisKey
|
|
50
|
+
});
|
|
51
|
+
const [seriesMin, seriesMax] = stackedData?.reduce((seriesAcc, values, index) => {
|
|
52
|
+
if (filter && (!filter(createResult(values[0], direction), index) || !filter(createResult(values[1], direction), index))) {
|
|
53
|
+
return seriesAcc;
|
|
54
|
+
}
|
|
20
55
|
return [Math.min(...values, seriesAcc[0]), Math.max(...values, seriesAcc[1])];
|
|
21
56
|
}, [Infinity, -Infinity]) ?? [Infinity, -Infinity];
|
|
22
57
|
return [Math.min(seriesMin, acc[0]), Math.max(seriesMax, acc[1])];
|
|
@@ -27,7 +62,7 @@ export const getExtremumX = params => {
|
|
|
27
62
|
// Don't think it's a problem for now
|
|
28
63
|
const isHorizontal = Object.keys(params.series).some(seriesId => params.series[seriesId].layout === 'horizontal');
|
|
29
64
|
if (isHorizontal) {
|
|
30
|
-
return getValueExtremum(params);
|
|
65
|
+
return getValueExtremum('x')(params);
|
|
31
66
|
}
|
|
32
67
|
return getBaseExtremum(params);
|
|
33
68
|
};
|
|
@@ -36,5 +71,5 @@ export const getExtremumY = params => {
|
|
|
36
71
|
if (isHorizontal) {
|
|
37
72
|
return getBaseExtremum(params);
|
|
38
73
|
}
|
|
39
|
-
return getValueExtremum(params);
|
|
74
|
+
return getValueExtremum('y')(params);
|
|
40
75
|
};
|
|
@@ -30,7 +30,7 @@ export const useChartContainerProps = (props, ref) => {
|
|
|
30
30
|
const chartSurfaceRef = useForkRef(ref, svgRef);
|
|
31
31
|
useReducedMotion(); // a11y reduce motion (see: https://react-spring.dev/docs/utilities/use-reduced-motion)
|
|
32
32
|
|
|
33
|
-
const [defaultizedXAxis, defaultizedYAxis] = useDefaultizeAxis(xAxis, yAxis);
|
|
33
|
+
const [defaultizedXAxis, defaultizedYAxis] = useDefaultizeAxis(xAxis, yAxis, dataset);
|
|
34
34
|
const drawingProviderProps = {
|
|
35
35
|
width,
|
|
36
36
|
height,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants';
|
|
4
|
-
const defaultizeAxis = (inAxis, axisName) => {
|
|
4
|
+
const defaultizeAxis = (inAxis, dataset, axisName) => {
|
|
5
5
|
const DEFAULT_AXIS_KEY = axisName === 'x' ? DEFAULT_X_AXIS_KEY : DEFAULT_Y_AXIS_KEY;
|
|
6
6
|
return [...(inAxis?.map((axis, index) => _extends({
|
|
7
7
|
id: `defaultized-${axisName}-axis-${index}`
|
|
@@ -10,10 +10,21 @@ const defaultizeAxis = (inAxis, axisName) => {
|
|
|
10
10
|
}) => id === DEFAULT_AXIS_KEY) === -1 ? [{
|
|
11
11
|
id: DEFAULT_AXIS_KEY,
|
|
12
12
|
scaleType: 'linear'
|
|
13
|
-
}] : [])]
|
|
13
|
+
}] : [])].map(axisConfig => {
|
|
14
|
+
const dataKey = axisConfig.dataKey;
|
|
15
|
+
if (dataKey === undefined || axisConfig.data !== undefined) {
|
|
16
|
+
return axisConfig;
|
|
17
|
+
}
|
|
18
|
+
if (dataset === undefined) {
|
|
19
|
+
throw Error(`MUI X: ${axisName}-axis uses \`dataKey\` but no \`dataset\` is provided.`);
|
|
20
|
+
}
|
|
21
|
+
return _extends({}, axisConfig, {
|
|
22
|
+
data: dataset.map(d => d[dataKey])
|
|
23
|
+
});
|
|
24
|
+
});
|
|
14
25
|
};
|
|
15
|
-
export const useDefaultizeAxis = (inXAxis, inYAxis) => {
|
|
16
|
-
const xAxis = React.useMemo(() => defaultizeAxis(inXAxis, 'x'), [inXAxis]);
|
|
17
|
-
const yAxis = React.useMemo(() => defaultizeAxis(inYAxis, 'y'), [inYAxis]);
|
|
26
|
+
export const useDefaultizeAxis = (inXAxis, inYAxis, dataset) => {
|
|
27
|
+
const xAxis = React.useMemo(() => defaultizeAxis(inXAxis, dataset, 'x'), [inXAxis, dataset]);
|
|
28
|
+
const yAxis = React.useMemo(() => defaultizeAxis(inYAxis, dataset, 'y'), [inYAxis, dataset]);
|
|
18
29
|
return [xAxis, yAxis];
|
|
19
30
|
};
|
|
@@ -25,16 +25,30 @@ export const ChartsAxisHighlightPath = styled('path', {
|
|
|
25
25
|
slot: 'Root',
|
|
26
26
|
overridesResolver: (_, styles) => styles.root
|
|
27
27
|
})(({
|
|
28
|
-
ownerState,
|
|
29
28
|
theme
|
|
30
|
-
}) =>
|
|
31
|
-
pointerEvents: 'none'
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
}) => ({
|
|
30
|
+
pointerEvents: 'none',
|
|
31
|
+
variants: [{
|
|
32
|
+
props: {
|
|
33
|
+
axisHighlight: 'band'
|
|
34
|
+
},
|
|
35
|
+
style: _extends({
|
|
36
|
+
fill: 'white',
|
|
37
|
+
fillOpacity: 0.1
|
|
38
|
+
}, theme.applyStyles('light', {
|
|
39
|
+
fill: 'gray'
|
|
40
|
+
}))
|
|
41
|
+
}, {
|
|
42
|
+
props: {
|
|
43
|
+
axisHighlight: 'line'
|
|
44
|
+
},
|
|
45
|
+
style: _extends({
|
|
46
|
+
strokeDasharray: '5 2',
|
|
47
|
+
stroke: '#ffffff'
|
|
48
|
+
}, theme.applyStyles('light', {
|
|
49
|
+
stroke: '#000000'
|
|
50
|
+
}))
|
|
51
|
+
}]
|
|
38
52
|
}));
|
|
39
53
|
/**
|
|
40
54
|
* Demos:
|
|
@@ -8,6 +8,7 @@ import { styled, useThemeProps } from '@mui/material/styles';
|
|
|
8
8
|
import { useCartesianContext } from '../context/CartesianProvider';
|
|
9
9
|
import { useTicks } from '../hooks/useTicks';
|
|
10
10
|
import { getChartsGridUtilityClass, chartsGridClasses } from './chartsGridClasses';
|
|
11
|
+
import { useDrawingArea } from '../hooks/useDrawingArea';
|
|
11
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
13
|
const GridRoot = styled('g', {
|
|
13
14
|
name: 'MuiChartsGrid',
|
|
@@ -53,6 +54,7 @@ function ChartsGrid(props) {
|
|
|
53
54
|
props,
|
|
54
55
|
name: 'MuiChartsGrid'
|
|
55
56
|
});
|
|
57
|
+
const drawingArea = useDrawingArea();
|
|
56
58
|
const {
|
|
57
59
|
vertical,
|
|
58
60
|
horizontal
|
|
@@ -93,8 +95,8 @@ function ChartsGrid(props) {
|
|
|
93
95
|
formattedValue,
|
|
94
96
|
offset
|
|
95
97
|
}) => /*#__PURE__*/_jsx(GridLine, {
|
|
96
|
-
y1:
|
|
97
|
-
y2:
|
|
98
|
+
y1: drawingArea.top,
|
|
99
|
+
y2: drawingArea.top + drawingArea.height,
|
|
98
100
|
x1: offset,
|
|
99
101
|
x2: offset,
|
|
100
102
|
className: classes.verticalLine
|
|
@@ -104,8 +106,8 @@ function ChartsGrid(props) {
|
|
|
104
106
|
}) => /*#__PURE__*/_jsx(GridLine, {
|
|
105
107
|
y1: offset,
|
|
106
108
|
y2: offset,
|
|
107
|
-
x1:
|
|
108
|
-
x2:
|
|
109
|
+
x1: drawingArea.left,
|
|
110
|
+
x2: drawingArea.left + drawingArea.width,
|
|
109
111
|
className: classes.horizontalLine
|
|
110
112
|
}, `horizontal-${formattedValue}`))]
|
|
111
113
|
}));
|
|
@@ -150,7 +150,8 @@ function ChartsXAxis(inProps) {
|
|
|
150
150
|
left,
|
|
151
151
|
top,
|
|
152
152
|
width,
|
|
153
|
-
height
|
|
153
|
+
height,
|
|
154
|
+
isPointInside
|
|
154
155
|
} = useDrawingArea();
|
|
155
156
|
const tickSize = disableTicks ? 4 : tickSizeProp;
|
|
156
157
|
const positionSign = position === 'bottom' ? 1 : -1;
|
|
@@ -225,8 +226,18 @@ function ChartsXAxis(inProps) {
|
|
|
225
226
|
}, index) => {
|
|
226
227
|
const xTickLabel = labelOffset ?? 0;
|
|
227
228
|
const yTickLabel = positionSign * (tickSize + 3);
|
|
228
|
-
const showTick =
|
|
229
|
-
|
|
229
|
+
const showTick = isPointInside({
|
|
230
|
+
x: offset,
|
|
231
|
+
y: -1
|
|
232
|
+
}, {
|
|
233
|
+
direction: 'x'
|
|
234
|
+
});
|
|
235
|
+
const showTickLabel = isPointInside({
|
|
236
|
+
x: offset + xTickLabel,
|
|
237
|
+
y: -1
|
|
238
|
+
}, {
|
|
239
|
+
direction: 'x'
|
|
240
|
+
});
|
|
230
241
|
return /*#__PURE__*/_jsxs("g", {
|
|
231
242
|
transform: `translate(${offset}, 0)`,
|
|
232
243
|
className: classes.tickContainer,
|
|
@@ -92,7 +92,8 @@ function ChartsYAxis(inProps) {
|
|
|
92
92
|
left,
|
|
93
93
|
top,
|
|
94
94
|
width,
|
|
95
|
-
height
|
|
95
|
+
height,
|
|
96
|
+
isPointInside
|
|
96
97
|
} = useDrawingArea();
|
|
97
98
|
const tickSize = disableTicks ? 4 : tickSizeProp;
|
|
98
99
|
const yTicks = useTicks({
|
|
@@ -172,7 +173,12 @@ function ChartsYAxis(inProps) {
|
|
|
172
173
|
const xTickLabel = positionSign * (tickSize + 2);
|
|
173
174
|
const yTickLabel = labelOffset;
|
|
174
175
|
const skipLabel = typeof tickLabelInterval === 'function' && !tickLabelInterval?.(value, index);
|
|
175
|
-
const showLabel =
|
|
176
|
+
const showLabel = isPointInside({
|
|
177
|
+
x: -1,
|
|
178
|
+
y: offset
|
|
179
|
+
}, {
|
|
180
|
+
direction: 'y'
|
|
181
|
+
});
|
|
176
182
|
if (!showLabel) {
|
|
177
183
|
return null;
|
|
178
184
|
}
|
|
@@ -6,9 +6,18 @@ export const getExtremumX = params => {
|
|
|
6
6
|
const maxX = Math.max(...(axis.data ?? []));
|
|
7
7
|
return [minX, maxX];
|
|
8
8
|
};
|
|
9
|
-
function getSeriesExtremums(getValues, stackedData) {
|
|
10
|
-
return stackedData.reduce((seriesAcc, stackedValue) => {
|
|
9
|
+
function getSeriesExtremums(getValues, stackedData, filter) {
|
|
10
|
+
return stackedData.reduce((seriesAcc, stackedValue, index) => {
|
|
11
11
|
const [base, value] = getValues(stackedValue);
|
|
12
|
+
if (filter && (!filter({
|
|
13
|
+
y: base,
|
|
14
|
+
x: null
|
|
15
|
+
}, index) || !filter({
|
|
16
|
+
y: value,
|
|
17
|
+
x: null
|
|
18
|
+
}, index))) {
|
|
19
|
+
return seriesAcc;
|
|
20
|
+
}
|
|
12
21
|
return [Math.min(base, value, seriesAcc[0]), Math.max(base, value, seriesAcc[1])];
|
|
13
22
|
}, [Infinity, -Infinity]);
|
|
14
23
|
}
|
|
@@ -16,7 +25,8 @@ export const getExtremumY = params => {
|
|
|
16
25
|
const {
|
|
17
26
|
series,
|
|
18
27
|
axis,
|
|
19
|
-
isDefaultAxis
|
|
28
|
+
isDefaultAxis,
|
|
29
|
+
getFilters
|
|
20
30
|
} = params;
|
|
21
31
|
return Object.keys(series).filter(seriesId => {
|
|
22
32
|
const yAxisId = series[seriesId].yAxisId ?? series[seriesId].yAxisKey;
|
|
@@ -27,10 +37,16 @@ export const getExtremumY = params => {
|
|
|
27
37
|
stackedData
|
|
28
38
|
} = series[seriesId];
|
|
29
39
|
const isArea = area !== undefined;
|
|
40
|
+
const filter = getFilters?.({
|
|
41
|
+
currentAxisId: axis.id,
|
|
42
|
+
isDefaultAxis,
|
|
43
|
+
seriesXAxisId: series[seriesId].xAxisId ?? series[seriesId].xAxisKey,
|
|
44
|
+
seriesYAxisId: series[seriesId].yAxisId ?? series[seriesId].yAxisKey
|
|
45
|
+
});
|
|
30
46
|
|
|
31
47
|
// Since this series is not used to display an area, we do not consider the base (the d[0]).
|
|
32
48
|
const getValues = isArea && axis.scaleType !== 'log' && typeof series[seriesId].baseline !== 'string' ? d => d : d => [d[1], d[1]];
|
|
33
|
-
const seriesExtremums = getSeriesExtremums(getValues, stackedData);
|
|
49
|
+
const seriesExtremums = getSeriesExtremums(getValues, stackedData, filter);
|
|
34
50
|
const [seriesMin, seriesMax] = seriesExtremums;
|
|
35
51
|
return [Math.min(seriesMin, acc[0]), Math.max(seriesMax, acc[1])];
|
|
36
52
|
}, [Infinity, -Infinity]);
|
|
@@ -5,16 +5,24 @@ export const getExtremumX = params => {
|
|
|
5
5
|
const {
|
|
6
6
|
series,
|
|
7
7
|
axis,
|
|
8
|
-
isDefaultAxis
|
|
8
|
+
isDefaultAxis,
|
|
9
|
+
getFilters
|
|
9
10
|
} = params;
|
|
10
11
|
return Object.keys(series).filter(seriesId => {
|
|
11
12
|
const axisId = series[seriesId].xAxisId ?? series[seriesId].xAxisKey;
|
|
12
13
|
return axisId === axis.id || axisId === undefined && isDefaultAxis;
|
|
13
14
|
}).reduce((acc, seriesId) => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const filter = getFilters?.({
|
|
16
|
+
currentAxisId: axis.id,
|
|
17
|
+
isDefaultAxis,
|
|
18
|
+
seriesXAxisId: series[seriesId].xAxisId ?? series[seriesId].xAxisKey,
|
|
19
|
+
seriesYAxisId: series[seriesId].yAxisId ?? series[seriesId].yAxisKey
|
|
20
|
+
});
|
|
21
|
+
const seriesMinMax = series[seriesId].data.reduce((accSeries, d, dataIndex) => {
|
|
22
|
+
if (filter && !filter(d, dataIndex)) {
|
|
23
|
+
return accSeries;
|
|
24
|
+
}
|
|
25
|
+
return mergeMinMax(accSeries, [d.x, d.x]);
|
|
18
26
|
}, [Infinity, -Infinity]);
|
|
19
27
|
return mergeMinMax(acc, seriesMinMax);
|
|
20
28
|
}, [Infinity, -Infinity]);
|
|
@@ -23,16 +31,24 @@ export const getExtremumY = params => {
|
|
|
23
31
|
const {
|
|
24
32
|
series,
|
|
25
33
|
axis,
|
|
26
|
-
isDefaultAxis
|
|
34
|
+
isDefaultAxis,
|
|
35
|
+
getFilters
|
|
27
36
|
} = params;
|
|
28
37
|
return Object.keys(series).filter(seriesId => {
|
|
29
38
|
const axisId = series[seriesId].yAxisId ?? series[seriesId].yAxisKey;
|
|
30
39
|
return axisId === axis.id || axisId === undefined && isDefaultAxis;
|
|
31
40
|
}).reduce((acc, seriesId) => {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
const filter = getFilters?.({
|
|
42
|
+
currentAxisId: axis.id,
|
|
43
|
+
isDefaultAxis,
|
|
44
|
+
seriesXAxisId: series[seriesId].xAxisId ?? series[seriesId].xAxisKey,
|
|
45
|
+
seriesYAxisId: series[seriesId].yAxisId ?? series[seriesId].yAxisKey
|
|
46
|
+
});
|
|
47
|
+
const seriesMinMax = series[seriesId].data.reduce((accSeries, d, dataIndex) => {
|
|
48
|
+
if (filter && !filter(d, dataIndex)) {
|
|
49
|
+
return accSeries;
|
|
50
|
+
}
|
|
51
|
+
return mergeMinMax(accSeries, [d.y, d.y]);
|
|
36
52
|
}, [Infinity, -Infinity]);
|
|
37
53
|
return mergeMinMax(acc, seriesMinMax);
|
|
38
54
|
}, [Infinity, -Infinity]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -10,7 +10,6 @@ function CartesianProvider(props) {
|
|
|
10
10
|
const {
|
|
11
11
|
xAxis,
|
|
12
12
|
yAxis,
|
|
13
|
-
dataset,
|
|
14
13
|
children
|
|
15
14
|
} = props;
|
|
16
15
|
const formattedSeries = useSeries();
|
|
@@ -22,17 +21,15 @@ function CartesianProvider(props) {
|
|
|
22
21
|
formattedSeries,
|
|
23
22
|
axis: xAxis,
|
|
24
23
|
extremumGetters: xExtremumGetters,
|
|
25
|
-
dataset,
|
|
26
24
|
axisDirection: 'x'
|
|
27
|
-
}), [drawingArea, formattedSeries, xAxis, xExtremumGetters
|
|
25
|
+
}), [drawingArea, formattedSeries, xAxis, xExtremumGetters]);
|
|
28
26
|
const yValues = React.useMemo(() => computeValue({
|
|
29
27
|
drawingArea,
|
|
30
28
|
formattedSeries,
|
|
31
29
|
axis: yAxis,
|
|
32
30
|
extremumGetters: yExtremumGetters,
|
|
33
|
-
dataset,
|
|
34
31
|
axisDirection: 'y'
|
|
35
|
-
}), [drawingArea, formattedSeries, yAxis, yExtremumGetters
|
|
32
|
+
}), [drawingArea, formattedSeries, yAxis, yExtremumGetters]);
|
|
36
33
|
const value = React.useMemo(() => ({
|
|
37
34
|
isInitialized: true,
|
|
38
35
|
data: {
|
|
@@ -4,22 +4,12 @@ import { isBandScaleConfig, isPointScaleConfig } from '../../models/axis';
|
|
|
4
4
|
import { getColorScale, getOrdinalColorScale } from '../../internals/colorScale';
|
|
5
5
|
import { getTickNumber } from '../../hooks/useTicks';
|
|
6
6
|
import { getScale } from '../../internals/getScale';
|
|
7
|
+
import { zoomScaleRange } from './zoom';
|
|
7
8
|
import { getAxisExtremum } from './getAxisExtremum';
|
|
8
|
-
import { normalizeAxis } from './normalizeAxis';
|
|
9
9
|
const getRange = (drawingArea, axisDirection, isReverse) => {
|
|
10
10
|
const range = axisDirection === 'x' ? [drawingArea.left, drawingArea.left + drawingArea.width] : [drawingArea.top + drawingArea.height, drawingArea.top];
|
|
11
11
|
return isReverse ? range.reverse() : range;
|
|
12
12
|
};
|
|
13
|
-
const zoomedScaleRange = (scaleRange, zoomRange) => {
|
|
14
|
-
const rangeGap = scaleRange[1] - scaleRange[0];
|
|
15
|
-
const zoomGap = zoomRange[1] - zoomRange[0];
|
|
16
|
-
|
|
17
|
-
// If current zoom show the scale between p1 and p2 percents
|
|
18
|
-
// The range should be extended by adding [0, p1] and [p2, 100] segments
|
|
19
|
-
const min = scaleRange[0] - zoomRange[0] * rangeGap / zoomGap;
|
|
20
|
-
const max = scaleRange[1] + (100 - zoomRange[1]) * rangeGap / zoomGap;
|
|
21
|
-
return [min, max];
|
|
22
|
-
};
|
|
23
13
|
const isDateData = data => data?.[0] instanceof Date;
|
|
24
14
|
function createDateFormatter(axis, range) {
|
|
25
15
|
const timeScale = scaleTime(axis.data, range);
|
|
@@ -32,32 +22,37 @@ const DEFAULT_BAR_GAP_RATIO = 0.1;
|
|
|
32
22
|
export function computeValue({
|
|
33
23
|
drawingArea,
|
|
34
24
|
formattedSeries,
|
|
35
|
-
axis:
|
|
25
|
+
axis: allAxis,
|
|
36
26
|
extremumGetters,
|
|
37
|
-
dataset,
|
|
38
27
|
axisDirection,
|
|
39
|
-
zoomData
|
|
28
|
+
zoomData,
|
|
29
|
+
zoomOptions,
|
|
30
|
+
getFilters
|
|
40
31
|
}) {
|
|
41
|
-
const allAxis = normalizeAxis(inAxis, dataset, axisDirection);
|
|
42
32
|
const completeAxis = {};
|
|
43
|
-
allAxis.forEach((
|
|
33
|
+
allAxis.forEach((eachAxis, axisIndex) => {
|
|
34
|
+
const axis = eachAxis;
|
|
44
35
|
const isDefaultAxis = axisIndex === 0;
|
|
45
|
-
const
|
|
36
|
+
const zoomOption = zoomOptions?.[axis.id];
|
|
46
37
|
const zoom = zoomData?.find(({
|
|
47
38
|
axisId
|
|
48
39
|
}) => axisId === axis.id);
|
|
49
40
|
const zoomRange = zoom ? [zoom.start, zoom.end] : [0, 100];
|
|
50
41
|
const range = getRange(drawingArea, axisDirection, axis.reverse);
|
|
42
|
+
const [minData, maxData] = getAxisExtremum(axis, extremumGetters, isDefaultAxis, formattedSeries, zoom === undefined && !zoomOption ? getFilters : undefined // Do not apply filtering if zoom is already defined.
|
|
43
|
+
);
|
|
44
|
+
const data = axis.data ?? [];
|
|
51
45
|
if (isBandScaleConfig(axis)) {
|
|
52
46
|
const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO;
|
|
53
47
|
const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO;
|
|
54
48
|
// Reverse range because ordinal scales are presented from top to bottom on y-axis
|
|
55
49
|
const scaleRange = axisDirection === 'x' ? range : [range[1], range[0]];
|
|
56
|
-
const zoomedRange =
|
|
50
|
+
const zoomedRange = zoomScaleRange(scaleRange, zoomRange);
|
|
57
51
|
completeAxis[axis.id] = _extends({
|
|
58
52
|
categoryGapRatio,
|
|
59
53
|
barGapRatio
|
|
60
54
|
}, axis, {
|
|
55
|
+
data,
|
|
61
56
|
scale: scaleBand(axis.data, zoomedRange).paddingInner(categoryGapRatio).paddingOuter(categoryGapRatio / 2),
|
|
62
57
|
tickNumber: axis.data.length,
|
|
63
58
|
colorScale: axis.colorMap && (axis.colorMap.type === 'ordinal' ? getOrdinalColorScale(_extends({
|
|
@@ -71,8 +66,9 @@ export function computeValue({
|
|
|
71
66
|
}
|
|
72
67
|
if (isPointScaleConfig(axis)) {
|
|
73
68
|
const scaleRange = axisDirection === 'x' ? range : [...range].reverse();
|
|
74
|
-
const zoomedRange =
|
|
69
|
+
const zoomedRange = zoomScaleRange(scaleRange, zoomRange);
|
|
75
70
|
completeAxis[axis.id] = _extends({}, axis, {
|
|
71
|
+
data,
|
|
76
72
|
scale: scalePoint(axis.data, zoomedRange),
|
|
77
73
|
tickNumber: axis.data.length,
|
|
78
74
|
colorScale: axis.colorMap && (axis.colorMap.type === 'ordinal' ? getOrdinalColorScale(_extends({
|
|
@@ -89,19 +85,20 @@ export function computeValue({
|
|
|
89
85
|
return;
|
|
90
86
|
}
|
|
91
87
|
const scaleType = axis.scaleType ?? 'linear';
|
|
92
|
-
const
|
|
88
|
+
const axisExtremums = [axis.min ?? minData, axis.max ?? maxData];
|
|
93
89
|
const rawTickNumber = getTickNumber(_extends({}, axis, {
|
|
94
90
|
range,
|
|
95
|
-
domain:
|
|
91
|
+
domain: axisExtremums
|
|
96
92
|
}));
|
|
97
93
|
const tickNumber = rawTickNumber / ((zoomRange[1] - zoomRange[0]) / 100);
|
|
98
|
-
const zoomedRange =
|
|
94
|
+
const zoomedRange = zoomScaleRange(range, zoomRange);
|
|
99
95
|
|
|
100
96
|
// TODO: move nice to prop? Disable when there is zoom?
|
|
101
|
-
const scale = getScale(scaleType,
|
|
97
|
+
const scale = getScale(scaleType, axisExtremums, zoomedRange).nice(rawTickNumber);
|
|
102
98
|
const [minDomain, maxDomain] = scale.domain();
|
|
103
99
|
const domain = [axis.min ?? minDomain, axis.max ?? maxDomain];
|
|
104
100
|
completeAxis[axis.id] = _extends({}, axis, {
|
|
101
|
+
data,
|
|
105
102
|
scaleType: scaleType,
|
|
106
103
|
scale: scale.domain(domain),
|
|
107
104
|
tickNumber,
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
const axisExtremumCallback = (acc, chartType, axis, getters, isDefaultAxis, formattedSeries) => {
|
|
1
|
+
const axisExtremumCallback = (acc, chartType, axis, getters, isDefaultAxis, formattedSeries, getFilters) => {
|
|
2
2
|
const getter = getters[chartType];
|
|
3
3
|
const series = formattedSeries[chartType]?.series ?? {};
|
|
4
4
|
const [minChartTypeData, maxChartTypeData] = getter?.({
|
|
5
5
|
series,
|
|
6
6
|
axis,
|
|
7
|
-
isDefaultAxis
|
|
7
|
+
isDefaultAxis,
|
|
8
|
+
getFilters
|
|
8
9
|
}) ?? [Infinity, -Infinity];
|
|
9
|
-
|
|
10
|
+
const [minData, maxData] = acc;
|
|
11
|
+
return [Math.min(minChartTypeData, minData), Math.max(maxChartTypeData, maxData)];
|
|
10
12
|
};
|
|
11
|
-
export const getAxisExtremum = (axis, getters, isDefaultAxis, formattedSeries) => {
|
|
13
|
+
export const getAxisExtremum = (axis, getters, isDefaultAxis, formattedSeries, getFilters) => {
|
|
12
14
|
const charTypes = Object.keys(getters);
|
|
13
|
-
|
|
15
|
+
const extremums = charTypes.reduce((acc, charType) => axisExtremumCallback(acc, charType, axis, getters, isDefaultAxis, formattedSeries, getFilters), [Infinity, -Infinity]);
|
|
16
|
+
if (Number.isNaN(extremums[0]) || Number.isNaN(extremums[1])) {
|
|
17
|
+
return [Infinity, -Infinity];
|
|
18
|
+
}
|
|
19
|
+
return extremums;
|
|
14
20
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies the zoom into the scale range.
|
|
3
|
+
* It changes the screen coordinates that the scale covers.
|
|
4
|
+
* Not the data that is displayed.
|
|
5
|
+
*
|
|
6
|
+
* @param scaleRange the original range in real screen coordinates.
|
|
7
|
+
* @param zoomRange the zoom range in percentage.
|
|
8
|
+
* @returns zoomed range in real screen coordinates.
|
|
9
|
+
*/
|
|
10
|
+
export const zoomScaleRange = (scaleRange, zoomRange) => {
|
|
11
|
+
const rangeGap = scaleRange[1] - scaleRange[0];
|
|
12
|
+
const zoomGap = zoomRange[1] - zoomRange[0];
|
|
13
|
+
|
|
14
|
+
// If current zoom show the scale between p1 and p2 percents
|
|
15
|
+
// The range should be extended by adding [0, p1] and [p2, 100] segments
|
|
16
|
+
const min = scaleRange[0] - zoomRange[0] * rangeGap / zoomGap;
|
|
17
|
+
const max = scaleRange[1] + (100 - zoomRange[1]) * rangeGap / zoomGap;
|
|
18
|
+
return [min, max];
|
|
19
|
+
};
|
|
@@ -42,12 +42,20 @@ export function DrawingProvider(props) {
|
|
|
42
42
|
const isPointInside = React.useCallback(({
|
|
43
43
|
x,
|
|
44
44
|
y
|
|
45
|
-
},
|
|
45
|
+
}, options) => {
|
|
46
46
|
// For element allowed to overflow, wrapping them in <g data-drawing-container /> make them fully part of the drawing area.
|
|
47
|
-
if (targetElement && targetElement.closest('[data-drawing-container]')) {
|
|
47
|
+
if (options?.targetElement && options?.targetElement.closest('[data-drawing-container]')) {
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
const isInsideX = x >= drawingArea.left - 1 && x <= drawingArea.left + drawingArea.width;
|
|
51
|
+
const isInsideY = y >= drawingArea.top - 1 && y <= drawingArea.top + drawingArea.height;
|
|
52
|
+
if (options?.direction === 'x') {
|
|
53
|
+
return isInsideX;
|
|
54
|
+
}
|
|
55
|
+
if (options?.direction === 'y') {
|
|
56
|
+
return isInsideY;
|
|
57
|
+
}
|
|
58
|
+
return isInsideX && isInsideY;
|
|
51
59
|
}, [drawingArea]);
|
|
52
60
|
const value = React.useMemo(() => _extends({
|
|
53
61
|
chartId: chartId ?? ''
|
|
@@ -98,7 +98,9 @@ export const useAxisEvents = disableAxisListener => {
|
|
|
98
98
|
const svgPoint = getSVGPoint(element, target);
|
|
99
99
|
mousePosition.current.x = svgPoint.x;
|
|
100
100
|
mousePosition.current.y = svgPoint.y;
|
|
101
|
-
if (!drawingArea.isPointInside(svgPoint,
|
|
101
|
+
if (!drawingArea.isPointInside(svgPoint, {
|
|
102
|
+
targetElement: event.target
|
|
103
|
+
})) {
|
|
102
104
|
if (mousePosition.current.isInChart) {
|
|
103
105
|
dispatch({
|
|
104
106
|
type: 'exitChart'
|
package/modern/index.js
CHANGED