@mui/x-charts-pro 8.12.0 → 8.13.1
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/BarChartPro/BarChartPro.js +24 -1
- package/CHANGELOG.md +207 -0
- package/ChartContainerPro/useChartContainerProProps.js +3 -1
- package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
- package/FunnelChart/funnelAxisPlugin/computeAxisValue.js +1 -4
- package/LineChartPro/LineChartPro.js +24 -1
- package/ScatterChartPro/ScatterChartPro.js +24 -1
- package/esm/BarChartPro/BarChartPro.js +24 -1
- package/esm/ChartContainerPro/useChartContainerProProps.js +3 -1
- package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
- package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.js +2 -5
- package/esm/LineChartPro/LineChartPro.js +24 -1
- package/esm/ScatterChartPro/ScatterChartPro.js +24 -1
- package/esm/index.js +1 -1
- package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +22 -0
- package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.js +4 -0
- package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.d.ts +92 -0
- package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.js +1 -0
- package/esm/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.js +21 -6
- package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.js +1 -1
- package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnPinch.js +18 -9
- package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.d.ts +8 -0
- package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.js +73 -0
- package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnWheel.js +14 -4
- package/esm/internals/plugins/useChartProZoom/initializeZoomData.d.ts +2 -0
- package/esm/internals/plugins/useChartProZoom/initializeZoomData.js +26 -0
- package/esm/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.d.ts +2 -0
- package/esm/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.js +86 -0
- package/esm/internals/plugins/useChartProZoom/useChartProZoom.d.ts +1 -2
- package/esm/internals/plugins/useChartProZoom/useChartProZoom.js +25 -44
- package/esm/internals/plugins/useChartProZoom/useChartProZoom.types.d.ts +9 -0
- package/index.js +1 -1
- package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +22 -0
- package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.js +10 -0
- package/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.d.ts +92 -0
- package/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.js +5 -0
- package/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.js +21 -6
- package/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.js +1 -1
- package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnPinch.js +18 -9
- package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.d.ts +8 -0
- package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.js +80 -0
- package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnWheel.js +14 -4
- package/internals/plugins/useChartProZoom/initializeZoomData.d.ts +2 -0
- package/internals/plugins/useChartProZoom/initializeZoomData.js +31 -0
- package/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.d.ts +2 -0
- package/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.js +92 -0
- package/internals/plugins/useChartProZoom/useChartProZoom.d.ts +1 -2
- package/internals/plugins/useChartProZoom/useChartProZoom.js +26 -45
- package/internals/plugins/useChartProZoom/useChartProZoom.types.d.ts +9 -0
- package/package.json +6 -6
|
@@ -4,6 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { useSelector, selectorChartDrawingArea, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
|
|
5
5
|
import { rafThrottle } from '@mui/x-internals/rafThrottle';
|
|
6
6
|
import { translateZoom } from "./useZoom.utils.js";
|
|
7
|
+
import { selectorPanInteractionConfig } from "../ZoomInteractionConfig.selectors.js";
|
|
7
8
|
export const usePanOnDrag = ({
|
|
8
9
|
store,
|
|
9
10
|
instance,
|
|
@@ -12,12 +13,26 @@ export const usePanOnDrag = ({
|
|
|
12
13
|
const drawingArea = useSelector(store, selectorChartDrawingArea);
|
|
13
14
|
const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
|
|
14
15
|
const startRef = React.useRef(null);
|
|
16
|
+
const config = useSelector(store, selectorPanInteractionConfig, ['drag']);
|
|
17
|
+
const isPanOnDragEnabled = React.useMemo(() => Object.values(optionsLookup).some(v => v.panning) && config || false, [optionsLookup, config]);
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
if (!isPanOnDragEnabled) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
instance.updateZoomInteractionListeners('zoomPan', {
|
|
23
|
+
requiredKeys: config.requiredKeys,
|
|
24
|
+
pointerMode: config.pointerMode,
|
|
25
|
+
pointerOptions: {
|
|
26
|
+
mouse: config.mouse,
|
|
27
|
+
touch: config.touch
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}, [isPanOnDragEnabled, config, instance]);
|
|
15
31
|
|
|
16
32
|
// Add event for chart panning
|
|
17
|
-
const isPanEnabled = React.useMemo(() => Object.values(optionsLookup).some(v => v.panning) || false, [optionsLookup]);
|
|
18
33
|
React.useEffect(() => {
|
|
19
34
|
const element = svgRef.current;
|
|
20
|
-
if (element === null || !
|
|
35
|
+
if (element === null || !isPanOnDragEnabled) {
|
|
21
36
|
return () => {};
|
|
22
37
|
}
|
|
23
38
|
const handlePanStart = event => {
|
|
@@ -45,14 +60,14 @@ export const usePanOnDrag = ({
|
|
|
45
60
|
}
|
|
46
61
|
throttledCallback(event, zoomData);
|
|
47
62
|
};
|
|
48
|
-
const panHandler = instance.addInteractionListener('
|
|
49
|
-
const panStartHandler = instance.addInteractionListener('
|
|
50
|
-
const panEndHandler = instance.addInteractionListener('
|
|
63
|
+
const panHandler = instance.addInteractionListener('zoomPan', handlePan);
|
|
64
|
+
const panStartHandler = instance.addInteractionListener('zoomPanStart', handlePanStart);
|
|
65
|
+
const panEndHandler = instance.addInteractionListener('zoomPanEnd', handlePanEnd);
|
|
51
66
|
return () => {
|
|
52
67
|
panStartHandler.cleanup();
|
|
53
68
|
panHandler.cleanup();
|
|
54
69
|
panEndHandler.cleanup();
|
|
55
70
|
throttledCallback.clear();
|
|
56
71
|
};
|
|
57
|
-
}, [instance, svgRef,
|
|
72
|
+
}, [instance, svgRef, isPanOnDragEnabled, optionsLookup, drawingArea.width, drawingArea.height, setZoomDataCallback, store, startRef]);
|
|
58
73
|
};
|
|
@@ -39,7 +39,7 @@ export const zoomAtPoint = (centerRatio, scaleRatio, currentZoomData, options) =
|
|
|
39
39
|
*/
|
|
40
40
|
export function isSpanValid(minRange, maxRange, isZoomIn, option) {
|
|
41
41
|
const newSpanPercent = maxRange - minRange;
|
|
42
|
-
if (isZoomIn && newSpanPercent < option.minSpan || !isZoomIn && newSpanPercent > option.maxSpan) {
|
|
42
|
+
if (newSpanPercent < 0 || isZoomIn && newSpanPercent < option.minSpan || !isZoomIn && newSpanPercent > option.maxSpan) {
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
45
|
if (minRange < option.minStart || maxRange > option.maxEnd) {
|
|
@@ -4,6 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { useSelector, getSVGPoint, selectorChartDrawingArea, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
|
|
5
5
|
import { rafThrottle } from '@mui/x-internals/rafThrottle';
|
|
6
6
|
import { getHorizontalCenterRatio, getVerticalCenterRatio, isSpanValid, zoomAtPoint } from "./useZoom.utils.js";
|
|
7
|
+
import { selectorZoomInteractionConfig } from "../ZoomInteractionConfig.selectors.js";
|
|
7
8
|
export const useZoomOnPinch = ({
|
|
8
9
|
store,
|
|
9
10
|
instance,
|
|
@@ -11,15 +12,28 @@ export const useZoomOnPinch = ({
|
|
|
11
12
|
}, setZoomDataCallback) => {
|
|
12
13
|
const drawingArea = useSelector(store, selectorChartDrawingArea);
|
|
13
14
|
const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
|
|
14
|
-
const
|
|
15
|
+
const config = useSelector(store, selectorZoomInteractionConfig, ['pinch']);
|
|
16
|
+
const isZoomOnPinchEnabled = React.useMemo(() => Object.keys(optionsLookup).length > 0 && config || false, [optionsLookup, config]);
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (!isZoomOnPinchEnabled) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
instance.updateZoomInteractionListeners('zoomPinch', {
|
|
22
|
+
requiredKeys: config.requiredKeys
|
|
23
|
+
});
|
|
24
|
+
}, [config, isZoomOnPinchEnabled, instance]);
|
|
15
25
|
|
|
16
26
|
// Zoom on pinch
|
|
17
27
|
React.useEffect(() => {
|
|
18
28
|
const element = svgRef.current;
|
|
19
|
-
if (element === null || !
|
|
29
|
+
if (element === null || !isZoomOnPinchEnabled) {
|
|
20
30
|
return () => {};
|
|
21
31
|
}
|
|
22
32
|
const rafThrottledCallback = rafThrottle(event => {
|
|
33
|
+
// If the delta is 0, it means the pinch gesture is not valid.
|
|
34
|
+
if (event.detail.direction === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
23
37
|
setZoomDataCallback(prev => {
|
|
24
38
|
return prev.map(zoom => {
|
|
25
39
|
const option = optionsLookup[zoom.axisId];
|
|
@@ -28,11 +42,6 @@ export const useZoomOnPinch = ({
|
|
|
28
42
|
}
|
|
29
43
|
const isZoomIn = event.detail.direction > 0;
|
|
30
44
|
const scaleRatio = 1 + event.detail.deltaScale;
|
|
31
|
-
|
|
32
|
-
// If the delta is 0, it means the pinch gesture is not valid.
|
|
33
|
-
if (event.detail.direction === 0) {
|
|
34
|
-
return zoom;
|
|
35
|
-
}
|
|
36
45
|
const point = getSVGPoint(element, {
|
|
37
46
|
clientX: event.detail.centroid.x,
|
|
38
47
|
clientY: event.detail.centroid.y
|
|
@@ -50,10 +59,10 @@ export const useZoomOnPinch = ({
|
|
|
50
59
|
});
|
|
51
60
|
});
|
|
52
61
|
});
|
|
53
|
-
const zoomHandler = instance.addInteractionListener('
|
|
62
|
+
const zoomHandler = instance.addInteractionListener('zoomPinch', rafThrottledCallback);
|
|
54
63
|
return () => {
|
|
55
64
|
zoomHandler.cleanup();
|
|
56
65
|
rafThrottledCallback.clear();
|
|
57
66
|
};
|
|
58
|
-
}, [svgRef, drawingArea,
|
|
67
|
+
}, [svgRef, drawingArea, isZoomOnPinchEnabled, optionsLookup, store, instance, setZoomDataCallback]);
|
|
59
68
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ChartPlugin, ZoomData } from '@mui/x-charts/internals';
|
|
3
|
+
import { UseChartProZoomSignature } from "../useChartProZoom.types.js";
|
|
4
|
+
export declare const useZoomOnTapAndDrag: ({
|
|
5
|
+
store,
|
|
6
|
+
instance,
|
|
7
|
+
svgRef
|
|
8
|
+
}: Pick<Parameters<ChartPlugin<UseChartProZoomSignature>>[0], "store" | "instance" | "svgRef">, setZoomDataCallback: React.Dispatch<ZoomData[] | ((prev: ZoomData[]) => ZoomData[])>) => void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useSelector, getSVGPoint, selectorChartDrawingArea, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
|
|
5
|
+
import { rafThrottle } from '@mui/x-internals/rafThrottle';
|
|
6
|
+
import { getHorizontalCenterRatio, getVerticalCenterRatio, isSpanValid, zoomAtPoint } from "./useZoom.utils.js";
|
|
7
|
+
import { selectorZoomInteractionConfig } from "../ZoomInteractionConfig.selectors.js";
|
|
8
|
+
export const useZoomOnTapAndDrag = ({
|
|
9
|
+
store,
|
|
10
|
+
instance,
|
|
11
|
+
svgRef
|
|
12
|
+
}, setZoomDataCallback) => {
|
|
13
|
+
const drawingArea = useSelector(store, selectorChartDrawingArea);
|
|
14
|
+
const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
|
|
15
|
+
const config = useSelector(store, selectorZoomInteractionConfig, ['tapAndDrag']);
|
|
16
|
+
const isZoomOnTapAndDragEnabled = React.useMemo(() => Object.keys(optionsLookup).length > 0 && config || false, [optionsLookup, config]);
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (!isZoomOnTapAndDragEnabled) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
instance.updateZoomInteractionListeners('zoomTapAndDrag', {
|
|
22
|
+
requiredKeys: config.requiredKeys,
|
|
23
|
+
pointerMode: config.pointerMode,
|
|
24
|
+
pointerOptions: {
|
|
25
|
+
mouse: config.mouse,
|
|
26
|
+
touch: config.touch
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}, [config, isZoomOnTapAndDragEnabled, instance]);
|
|
30
|
+
|
|
31
|
+
// Zoom on tap and drag
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
const element = svgRef.current;
|
|
34
|
+
if (element === null || !isZoomOnTapAndDragEnabled) {
|
|
35
|
+
return () => {};
|
|
36
|
+
}
|
|
37
|
+
const rafThrottledCallback = rafThrottle(event => {
|
|
38
|
+
// If the delta is 0, we didn't move
|
|
39
|
+
if (event.detail.deltaY === 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setZoomDataCallback(prev => {
|
|
43
|
+
return prev.map(zoom => {
|
|
44
|
+
const option = optionsLookup[zoom.axisId];
|
|
45
|
+
if (!option) {
|
|
46
|
+
return zoom;
|
|
47
|
+
}
|
|
48
|
+
const isZoomIn = event.detail.deltaY > 0;
|
|
49
|
+
const scaleRatio = 1 + event.detail.deltaY / 100;
|
|
50
|
+
const point = getSVGPoint(element, {
|
|
51
|
+
clientX: event.detail.initialCentroid.x,
|
|
52
|
+
clientY: event.detail.initialCentroid.y
|
|
53
|
+
});
|
|
54
|
+
const centerRatio = option.axisDirection === 'x' ? getHorizontalCenterRatio(point, drawingArea) : getVerticalCenterRatio(point, drawingArea);
|
|
55
|
+
const [newMinRange, newMaxRange] = zoomAtPoint(centerRatio, scaleRatio, zoom, option);
|
|
56
|
+
if (!isSpanValid(newMinRange, newMaxRange, isZoomIn, option)) {
|
|
57
|
+
return zoom;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
axisId: zoom.axisId,
|
|
61
|
+
start: newMinRange,
|
|
62
|
+
end: newMaxRange
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
const zoomHandler = instance.addInteractionListener('zoomTapAndDrag', rafThrottledCallback);
|
|
68
|
+
return () => {
|
|
69
|
+
zoomHandler.cleanup();
|
|
70
|
+
rafThrottledCallback.clear();
|
|
71
|
+
};
|
|
72
|
+
}, [svgRef, drawingArea, isZoomOnTapAndDragEnabled, optionsLookup, store, instance, setZoomDataCallback]);
|
|
73
|
+
};
|
|
@@ -4,6 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { useSelector, getSVGPoint, selectorChartDrawingArea, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
|
|
5
5
|
import { rafThrottle } from '@mui/x-internals/rafThrottle';
|
|
6
6
|
import { getHorizontalCenterRatio, getVerticalCenterRatio, getWheelScaleRatio, isSpanValid, zoomAtPoint } from "./useZoom.utils.js";
|
|
7
|
+
import { selectorZoomInteractionConfig } from "../ZoomInteractionConfig.selectors.js";
|
|
7
8
|
export const useZoomOnWheel = ({
|
|
8
9
|
store,
|
|
9
10
|
instance,
|
|
@@ -11,18 +12,27 @@ export const useZoomOnWheel = ({
|
|
|
11
12
|
}, setZoomDataCallback) => {
|
|
12
13
|
const drawingArea = useSelector(store, selectorChartDrawingArea);
|
|
13
14
|
const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
|
|
14
|
-
const isZoomEnabled = Object.keys(optionsLookup).length > 0;
|
|
15
15
|
const startedOutsideRef = React.useRef(false);
|
|
16
16
|
const startedOutsideTimeoutRef = React.useRef(null);
|
|
17
|
+
const config = useSelector(store, selectorZoomInteractionConfig, ['wheel']);
|
|
18
|
+
const isZoomOnWheelEnabled = React.useMemo(() => Object.keys(optionsLookup).length > 0 && config || false, [optionsLookup, config]);
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
if (!isZoomOnWheelEnabled) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
instance.updateZoomInteractionListeners('zoomTurnWheel', {
|
|
24
|
+
requiredKeys: config.requiredKeys
|
|
25
|
+
});
|
|
26
|
+
}, [config, isZoomOnWheelEnabled, instance]);
|
|
17
27
|
|
|
18
28
|
// Add event for chart zoom in/out
|
|
19
29
|
React.useEffect(() => {
|
|
20
30
|
const element = svgRef.current;
|
|
21
|
-
if (element === null || !
|
|
31
|
+
if (element === null || !isZoomOnWheelEnabled) {
|
|
22
32
|
return () => {};
|
|
23
33
|
}
|
|
24
34
|
const rafThrottledSetZoomData = rafThrottle(setZoomDataCallback);
|
|
25
|
-
const zoomOnWheelHandler = instance.addInteractionListener('
|
|
35
|
+
const zoomOnWheelHandler = instance.addInteractionListener('zoomTurnWheel', event => {
|
|
26
36
|
const point = getSVGPoint(element, {
|
|
27
37
|
clientX: event.detail.centroid.x,
|
|
28
38
|
clientY: event.detail.centroid.y
|
|
@@ -75,5 +85,5 @@ export const useZoomOnWheel = ({
|
|
|
75
85
|
startedOutsideRef.current = false;
|
|
76
86
|
rafThrottledSetZoomData.clear();
|
|
77
87
|
};
|
|
78
|
-
}, [svgRef, drawingArea,
|
|
88
|
+
}, [svgRef, drawingArea, isZoomOnWheelEnabled, optionsLookup, instance, setZoomDataCallback, store]);
|
|
79
89
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// This is helpful to avoid the need to provide the possibly auto-generated id for each axis.
|
|
4
|
+
export function initializeZoomData(options, zoomData) {
|
|
5
|
+
const zoomDataMap = new Map();
|
|
6
|
+
zoomData?.forEach(zoom => {
|
|
7
|
+
const option = options[zoom.axisId];
|
|
8
|
+
if (option) {
|
|
9
|
+
zoomDataMap.set(zoom.axisId, zoom);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return Object.values(options).map(({
|
|
13
|
+
axisId,
|
|
14
|
+
minStart: start,
|
|
15
|
+
maxEnd: end
|
|
16
|
+
}) => {
|
|
17
|
+
if (zoomDataMap.has(axisId)) {
|
|
18
|
+
return zoomDataMap.get(axisId);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
axisId,
|
|
22
|
+
start,
|
|
23
|
+
end
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
export const initializeZoomInteractionConfig = zoomInteractionConfig => {
|
|
4
|
+
const defaultizedConfig = {
|
|
5
|
+
zoom: {},
|
|
6
|
+
pan: {}
|
|
7
|
+
};
|
|
8
|
+
if (!zoomInteractionConfig?.zoom) {
|
|
9
|
+
defaultizedConfig.zoom = {
|
|
10
|
+
wheel: {
|
|
11
|
+
type: 'wheel',
|
|
12
|
+
requiredKeys: [],
|
|
13
|
+
mouse: {},
|
|
14
|
+
touch: {}
|
|
15
|
+
},
|
|
16
|
+
pinch: {
|
|
17
|
+
type: 'pinch',
|
|
18
|
+
requiredKeys: [],
|
|
19
|
+
mouse: {},
|
|
20
|
+
touch: {}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
} else {
|
|
24
|
+
defaultizedConfig.zoom = initializeFor(zoomInteractionConfig.zoom);
|
|
25
|
+
}
|
|
26
|
+
if (!zoomInteractionConfig?.pan) {
|
|
27
|
+
defaultizedConfig.pan = {
|
|
28
|
+
drag: {
|
|
29
|
+
type: 'drag',
|
|
30
|
+
requiredKeys: [],
|
|
31
|
+
mouse: {},
|
|
32
|
+
touch: {}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
} else {
|
|
36
|
+
defaultizedConfig.pan = initializeFor(zoomInteractionConfig.pan);
|
|
37
|
+
}
|
|
38
|
+
return defaultizedConfig;
|
|
39
|
+
};
|
|
40
|
+
function initializeFor(zoomInteractionConfig) {
|
|
41
|
+
// We aggregate interactions by type
|
|
42
|
+
const aggregation = zoomInteractionConfig.reduce((acc, interaction) => {
|
|
43
|
+
if (typeof interaction === 'string') {
|
|
44
|
+
if (!acc[interaction]) {
|
|
45
|
+
acc[interaction] = [];
|
|
46
|
+
}
|
|
47
|
+
acc[interaction].push({
|
|
48
|
+
type: interaction,
|
|
49
|
+
requiredKeys: []
|
|
50
|
+
});
|
|
51
|
+
return acc;
|
|
52
|
+
}
|
|
53
|
+
const type = interaction.type;
|
|
54
|
+
if (!acc[type]) {
|
|
55
|
+
acc[type] = [];
|
|
56
|
+
}
|
|
57
|
+
acc[type].push({
|
|
58
|
+
type,
|
|
59
|
+
pointerMode: interaction.pointerMode,
|
|
60
|
+
requiredKeys: interaction.requiredKeys
|
|
61
|
+
});
|
|
62
|
+
return acc;
|
|
63
|
+
}, {});
|
|
64
|
+
|
|
65
|
+
// We then need to generate a usable config by type
|
|
66
|
+
// When a gesture type is provided without options, it means we enable it for all pointer modes
|
|
67
|
+
// Any interaction with a specific pointer mode should be restricted to that mode
|
|
68
|
+
const acc = {};
|
|
69
|
+
for (const [type, config] of Object.entries(aggregation)) {
|
|
70
|
+
const lastEmpty = config.findLast(item => !item.pointerMode);
|
|
71
|
+
const lastMouse = config.findLast(item => item.pointerMode === 'mouse');
|
|
72
|
+
const lastTouch = config.findLast(item => item.pointerMode === 'touch');
|
|
73
|
+
acc[type] = {
|
|
74
|
+
type,
|
|
75
|
+
pointerMode: lastEmpty ? [] : Array.from(new Set(config.filter(c => c.pointerMode).map(c => c.pointerMode))),
|
|
76
|
+
requiredKeys: lastEmpty?.requiredKeys ?? [],
|
|
77
|
+
mouse: lastMouse ? {
|
|
78
|
+
requiredKeys: lastMouse?.requiredKeys ?? []
|
|
79
|
+
} : {},
|
|
80
|
+
touch: lastTouch ? {
|
|
81
|
+
requiredKeys: lastTouch?.requiredKeys ?? []
|
|
82
|
+
} : {}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return acc;
|
|
86
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ChartPlugin
|
|
1
|
+
import { ChartPlugin } from '@mui/x-charts/internals';
|
|
2
2
|
import { UseChartProZoomSignature } from "./useChartProZoom.types.js";
|
|
3
|
-
export declare function initializeZoomData(options: Record<AxisId, Pick<DefaultizedZoomOptions, 'axisId' | 'minStart' | 'maxEnd'>>, zoomData?: readonly ZoomData[]): ZoomData[];
|
|
4
3
|
export declare const useChartProZoom: ChartPlugin<UseChartProZoomSignature>;
|
|
@@ -4,48 +4,36 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { useSelector, selectorChartZoomOptionsLookup, createZoomLookup, selectorChartAxisZoomOptionsLookup } from '@mui/x-charts/internals';
|
|
6
6
|
import debounce from '@mui/utils/debounce';
|
|
7
|
+
import { useEffectAfterFirstRender } from '@mui/x-internals/useEffectAfterFirstRender';
|
|
7
8
|
import { useEventCallback } from '@mui/material/utils';
|
|
8
9
|
import { calculateZoom } from "./calculateZoom.js";
|
|
9
10
|
import { useZoomOnWheel } from "./gestureHooks/useZoomOnWheel.js";
|
|
10
11
|
import { useZoomOnPinch } from "./gestureHooks/useZoomOnPinch.js";
|
|
11
12
|
import { usePanOnDrag } from "./gestureHooks/usePanOnDrag.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return Object.values(options).map(({
|
|
23
|
-
axisId,
|
|
24
|
-
minStart: start,
|
|
25
|
-
maxEnd: end
|
|
26
|
-
}) => {
|
|
27
|
-
if (zoomDataMap.has(axisId)) {
|
|
28
|
-
return zoomDataMap.get(axisId);
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
axisId,
|
|
32
|
-
start,
|
|
33
|
-
end
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
export const useChartProZoom = ({
|
|
38
|
-
store,
|
|
39
|
-
instance,
|
|
40
|
-
svgRef,
|
|
41
|
-
params
|
|
42
|
-
}) => {
|
|
13
|
+
import { useZoomOnTapAndDrag } from "./gestureHooks/useZoomOnTapAndDrag.js";
|
|
14
|
+
import { initializeZoomInteractionConfig } from "./initializeZoomInteractionConfig.js";
|
|
15
|
+
import { initializeZoomData } from "./initializeZoomData.js";
|
|
16
|
+
export const useChartProZoom = pluginData => {
|
|
17
|
+
const {
|
|
18
|
+
store,
|
|
19
|
+
params
|
|
20
|
+
} = pluginData;
|
|
43
21
|
const {
|
|
44
22
|
zoomData: paramsZoomData,
|
|
45
|
-
onZoomChange: onZoomChangeProp
|
|
23
|
+
onZoomChange: onZoomChangeProp,
|
|
24
|
+
zoomInteractionConfig
|
|
46
25
|
} = params;
|
|
47
26
|
const onZoomChange = useEventCallback(onZoomChangeProp ?? (() => {}));
|
|
48
27
|
const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
|
|
28
|
+
useEffectAfterFirstRender(() => {
|
|
29
|
+
store.update(prevState => {
|
|
30
|
+
return _extends({}, prevState, {
|
|
31
|
+
zoom: _extends({}, prevState.zoom, {
|
|
32
|
+
zoomInteractionConfig: initializeZoomInteractionConfig(zoomInteractionConfig)
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}, [store, zoomInteractionConfig]);
|
|
49
37
|
|
|
50
38
|
// Manage controlled state
|
|
51
39
|
React.useEffect(() => {
|
|
@@ -144,14 +132,10 @@ export const useChartProZoom = ({
|
|
|
144
132
|
}, [removeIsInteracting]);
|
|
145
133
|
|
|
146
134
|
// Add events
|
|
147
|
-
const pluginData = {
|
|
148
|
-
store,
|
|
149
|
-
instance,
|
|
150
|
-
svgRef
|
|
151
|
-
};
|
|
152
135
|
usePanOnDrag(pluginData, setZoomDataCallback);
|
|
153
136
|
useZoomOnWheel(pluginData, setZoomDataCallback);
|
|
154
137
|
useZoomOnPinch(pluginData, setZoomDataCallback);
|
|
138
|
+
useZoomOnTapAndDrag(pluginData, setZoomDataCallback);
|
|
155
139
|
const zoom = React.useCallback(step => {
|
|
156
140
|
setZoomDataCallback(prev => prev.map(zoomData => {
|
|
157
141
|
const zoomOptions = selectorChartAxisZoomOptionsLookup(store.getSnapshot(), zoomData.axisId);
|
|
@@ -179,12 +163,8 @@ export const useChartProZoom = ({
|
|
|
179
163
|
useChartProZoom.params = {
|
|
180
164
|
initialZoom: true,
|
|
181
165
|
onZoomChange: true,
|
|
182
|
-
zoomData: true
|
|
183
|
-
|
|
184
|
-
useChartProZoom.getDefaultizedParams = ({
|
|
185
|
-
params
|
|
186
|
-
}) => {
|
|
187
|
-
return _extends({}, params);
|
|
166
|
+
zoomData: true,
|
|
167
|
+
zoomInteractionConfig: true
|
|
188
168
|
};
|
|
189
169
|
useChartProZoom.getInitialState = params => {
|
|
190
170
|
const {
|
|
@@ -201,7 +181,8 @@ useChartProZoom.getInitialState = params => {
|
|
|
201
181
|
zoom: {
|
|
202
182
|
zoomData: initializeZoomData(optionsLookup, userZoomData),
|
|
203
183
|
isInteracting: false,
|
|
204
|
-
isControlled: zoomData !== undefined
|
|
184
|
+
isControlled: zoomData !== undefined,
|
|
185
|
+
zoomInteractionConfig: initializeZoomInteractionConfig(params.zoomInteractionConfig)
|
|
205
186
|
}
|
|
206
187
|
};
|
|
207
188
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UseChartSeriesSignature, ChartPluginSignature, UseChartCartesianAxisSignature, UseChartCartesianAxisDefaultizedParameters, ZoomData, AxisId } from '@mui/x-charts/internals';
|
|
2
|
+
import { ZoomInteractionConfig, DefaultizedZoomInteractionConfig } from "./ZoomInteractionConfig.types.js";
|
|
2
3
|
export interface UseChartProZoomParameters {
|
|
3
4
|
/**
|
|
4
5
|
* The list of zoom data related to each axis.
|
|
@@ -15,6 +16,10 @@ export interface UseChartProZoomParameters {
|
|
|
15
16
|
* The list of zoom data related to each axis.
|
|
16
17
|
*/
|
|
17
18
|
zoomData?: readonly ZoomData[];
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for zoom interactions.
|
|
21
|
+
*/
|
|
22
|
+
zoomInteractionConfig?: ZoomInteractionConfig;
|
|
18
23
|
}
|
|
19
24
|
export type UseChartProZoomDefaultizedParameters = UseChartProZoomParameters & UseChartCartesianAxisDefaultizedParameters;
|
|
20
25
|
export interface UseChartProZoomState {
|
|
@@ -32,6 +37,10 @@ export interface UseChartProZoomState {
|
|
|
32
37
|
* Internal information to know if the user control the state or not.
|
|
33
38
|
*/
|
|
34
39
|
isControlled: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Configuration for zoom interactions.
|
|
42
|
+
*/
|
|
43
|
+
zoomInteractionConfig: DefaultizedZoomInteractionConfig;
|
|
35
44
|
};
|
|
36
45
|
}
|
|
37
46
|
export interface UseChartProZoomPublicApi {
|
package/index.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const selectorZoomInteractionConfig: import("reselect").Selector<any, (Omit<import("./ZoomInteractionConfig.types.js").ZoomInteraction, "pointerMode"> & {
|
|
2
|
+
mouse: {
|
|
3
|
+
requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
|
|
4
|
+
};
|
|
5
|
+
touch: {
|
|
6
|
+
requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
|
|
7
|
+
};
|
|
8
|
+
pointerMode?: import("@mui/x-internal-gestures/core").PointerMode[];
|
|
9
|
+
}) | null, [interactionName: "wheel" | "pinch" | "tapAndDrag"]>;
|
|
10
|
+
export declare const selectorPanInteractionConfig: import("reselect").Selector<any, (Omit<{
|
|
11
|
+
type: "drag";
|
|
12
|
+
pointerMode?: import("./ZoomInteractionConfig.types.js").InteractionMode | undefined;
|
|
13
|
+
requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[] | undefined;
|
|
14
|
+
}, "pointerMode"> & {
|
|
15
|
+
mouse: {
|
|
16
|
+
requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
|
|
17
|
+
};
|
|
18
|
+
touch: {
|
|
19
|
+
requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
|
|
20
|
+
};
|
|
21
|
+
pointerMode?: import("@mui/x-internal-gestures/core").PointerMode[];
|
|
22
|
+
}) | null, [interactionName: "drag"]>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.selectorZoomInteractionConfig = exports.selectorPanInteractionConfig = void 0;
|
|
7
|
+
var _internals = require("@mui/x-charts/internals");
|
|
8
|
+
var _useChartProZoom = require("./useChartProZoom.selectors");
|
|
9
|
+
const selectorZoomInteractionConfig = exports.selectorZoomInteractionConfig = (0, _internals.createSelector)([_useChartProZoom.selectorChartZoomState, (_state, interactionName) => interactionName], (zoomState, interactionName) => zoomState.zoomInteractionConfig.zoom[interactionName] ?? null);
|
|
10
|
+
const selectorPanInteractionConfig = exports.selectorPanInteractionConfig = (0, _internals.createSelector)([_useChartProZoom.selectorChartZoomState, (_state, interactionName) => interactionName], (zoomState, interactionName) => zoomState.zoomInteractionConfig.pan[interactionName] ?? null);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { KeyboardKey, PointerMode } from '@mui/x-internal-gestures/core';
|
|
2
|
+
export type ZoomInteractionConfig = {
|
|
3
|
+
/**
|
|
4
|
+
* Defines the interactions that trigger zooming.
|
|
5
|
+
* - `wheel`: Zooms in or out when the mouse wheel is scrolled.
|
|
6
|
+
* - `pinch`: Zooms in or out when a pinch gesture is detected.
|
|
7
|
+
* - `tapAndDrag`: Zooms in or out by tapping twice and then dragging vertically. Dragging up zooms in, dragging down zooms out.
|
|
8
|
+
*
|
|
9
|
+
* @default ['wheel', 'pinch']
|
|
10
|
+
*/
|
|
11
|
+
zoom?: (ZoomInteraction | ZoomInteraction['type'])[];
|
|
12
|
+
/**
|
|
13
|
+
* Defines the interactions that trigger panning.
|
|
14
|
+
* - `drag`: Pans the chart when dragged with the mouse.
|
|
15
|
+
*
|
|
16
|
+
* @default ['drag']
|
|
17
|
+
*/
|
|
18
|
+
pan?: (PanInteraction | PanInteraction['type'])[];
|
|
19
|
+
};
|
|
20
|
+
type Entry<T extends AnyInteraction> = { [K in T['type']]?: Omit<T, 'pointerMode'> & {
|
|
21
|
+
mouse: {
|
|
22
|
+
requiredKeys?: KeyboardKey[];
|
|
23
|
+
};
|
|
24
|
+
touch: {
|
|
25
|
+
requiredKeys?: KeyboardKey[];
|
|
26
|
+
};
|
|
27
|
+
pointerMode?: PointerMode[];
|
|
28
|
+
} };
|
|
29
|
+
export type DefaultizedZoomInteractionConfig = {
|
|
30
|
+
zoom: Entry<ZoomInteraction>;
|
|
31
|
+
pan: Entry<PanInteraction>;
|
|
32
|
+
};
|
|
33
|
+
export type ZoomInteraction = WheelInteraction | PinchInteraction | TapAndDragInteraction;
|
|
34
|
+
export type PanInteraction = DragInteraction;
|
|
35
|
+
export type ZoomInteractionName = ZoomInteraction['type'];
|
|
36
|
+
export type PanInteractionName = PanInteraction['type'];
|
|
37
|
+
export type InteractionMode = Exclude<PointerMode, 'pen'>;
|
|
38
|
+
type AllKeysProp = {
|
|
39
|
+
/**
|
|
40
|
+
* The keys that must be pressed to trigger the interaction.
|
|
41
|
+
*/
|
|
42
|
+
requiredKeys?: KeyboardKey[];
|
|
43
|
+
};
|
|
44
|
+
type AllModeProp = {
|
|
45
|
+
/**
|
|
46
|
+
* Defines which type of pointer can trigger the interaction.
|
|
47
|
+
* - `mouse`: Only mouse interactions will trigger the interaction.
|
|
48
|
+
* - `touch`: Only touch interactions will trigger the interaction.
|
|
49
|
+
* - undefined: All interactions will trigger the interaction.
|
|
50
|
+
*/
|
|
51
|
+
pointerMode?: InteractionMode;
|
|
52
|
+
};
|
|
53
|
+
type NoKeysProp = {
|
|
54
|
+
/**
|
|
55
|
+
* This interaction does not support key combinations.
|
|
56
|
+
*/
|
|
57
|
+
requiredKeys?: any[];
|
|
58
|
+
};
|
|
59
|
+
type NoModeProp = {
|
|
60
|
+
/**
|
|
61
|
+
* This gesture only works on a specific pointer mode. Mode has no effect.
|
|
62
|
+
*/
|
|
63
|
+
pointerMode?: any;
|
|
64
|
+
};
|
|
65
|
+
type Unpack<T> = { [K in keyof T]: T[K] extends object ? Unpack<T[K]> : T[K] };
|
|
66
|
+
export type WheelInteraction = Unpack<{
|
|
67
|
+
type: 'wheel';
|
|
68
|
+
} & NoModeProp & AllKeysProp>;
|
|
69
|
+
export type PinchInteraction = Unpack<{
|
|
70
|
+
type: 'pinch';
|
|
71
|
+
} & NoModeProp & NoKeysProp>;
|
|
72
|
+
export type DragInteraction = Unpack<{
|
|
73
|
+
type: 'drag';
|
|
74
|
+
} & AllModeProp & AllKeysProp>;
|
|
75
|
+
export type TapAndDragInteraction = Unpack<{
|
|
76
|
+
type: 'tapAndDrag';
|
|
77
|
+
} & AllModeProp & AllKeysProp>;
|
|
78
|
+
export type AnyInteraction = {
|
|
79
|
+
type: string;
|
|
80
|
+
pointerMode?: InteractionMode;
|
|
81
|
+
requiredKeys?: KeyboardKey[];
|
|
82
|
+
};
|
|
83
|
+
export type AnyEntry = Omit<AnyInteraction, 'pointerMode'> & {
|
|
84
|
+
mouse: {
|
|
85
|
+
requiredKeys?: KeyboardKey[];
|
|
86
|
+
};
|
|
87
|
+
touch: {
|
|
88
|
+
requiredKeys?: KeyboardKey[];
|
|
89
|
+
};
|
|
90
|
+
pointerMode?: PointerMode[];
|
|
91
|
+
};
|
|
92
|
+
export {};
|