@mui/x-charts-pro 8.12.0 → 8.13.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.
Files changed (50) hide show
  1. package/BarChartPro/BarChartPro.js +24 -1
  2. package/CHANGELOG.md +145 -0
  3. package/ChartContainerPro/useChartContainerProProps.js +3 -1
  4. package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  5. package/FunnelChart/funnelAxisPlugin/computeAxisValue.js +1 -4
  6. package/LineChartPro/LineChartPro.js +24 -1
  7. package/ScatterChartPro/ScatterChartPro.js +24 -1
  8. package/esm/BarChartPro/BarChartPro.js +24 -1
  9. package/esm/ChartContainerPro/useChartContainerProProps.js +3 -1
  10. package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  11. package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.js +2 -5
  12. package/esm/LineChartPro/LineChartPro.js +24 -1
  13. package/esm/ScatterChartPro/ScatterChartPro.js +24 -1
  14. package/esm/index.js +1 -1
  15. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +22 -0
  16. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.js +4 -0
  17. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.d.ts +92 -0
  18. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.js +1 -0
  19. package/esm/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.js +21 -6
  20. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.js +1 -1
  21. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnPinch.js +18 -9
  22. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.d.ts +8 -0
  23. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.js +73 -0
  24. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoomOnWheel.js +14 -4
  25. package/esm/internals/plugins/useChartProZoom/initializeZoomData.d.ts +2 -0
  26. package/esm/internals/plugins/useChartProZoom/initializeZoomData.js +26 -0
  27. package/esm/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.d.ts +2 -0
  28. package/esm/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.js +86 -0
  29. package/esm/internals/plugins/useChartProZoom/useChartProZoom.d.ts +1 -2
  30. package/esm/internals/plugins/useChartProZoom/useChartProZoom.js +25 -44
  31. package/esm/internals/plugins/useChartProZoom/useChartProZoom.types.d.ts +9 -0
  32. package/index.js +1 -1
  33. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +22 -0
  34. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.js +10 -0
  35. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.d.ts +92 -0
  36. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.js +5 -0
  37. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.js +21 -6
  38. package/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.js +1 -1
  39. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnPinch.js +18 -9
  40. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.d.ts +8 -0
  41. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnTapAndDrag.js +80 -0
  42. package/internals/plugins/useChartProZoom/gestureHooks/useZoomOnWheel.js +14 -4
  43. package/internals/plugins/useChartProZoom/initializeZoomData.d.ts +2 -0
  44. package/internals/plugins/useChartProZoom/initializeZoomData.js +31 -0
  45. package/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.d.ts +2 -0
  46. package/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.js +92 -0
  47. package/internals/plugins/useChartProZoom/useChartProZoom.d.ts +1 -2
  48. package/internals/plugins/useChartProZoom/useChartProZoom.js +26 -45
  49. package/internals/plugins/useChartProZoom/useChartProZoom.types.d.ts +9 -0
  50. package/package.json +6 -6
@@ -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 isZoomEnabled = Object.keys(optionsLookup).length > 0;
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 || !isZoomEnabled) {
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('pinch', rafThrottledCallback);
62
+ const zoomHandler = instance.addInteractionListener('zoomPinch', rafThrottledCallback);
54
63
  return () => {
55
64
  zoomHandler.cleanup();
56
65
  rafThrottledCallback.clear();
57
66
  };
58
- }, [svgRef, drawingArea, isZoomEnabled, optionsLookup, store, instance, setZoomDataCallback]);
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 || !isZoomEnabled) {
31
+ if (element === null || !isZoomOnWheelEnabled) {
22
32
  return () => {};
23
33
  }
24
34
  const rafThrottledSetZoomData = rafThrottle(setZoomDataCallback);
25
- const zoomOnWheelHandler = instance.addInteractionListener('turnWheel', event => {
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, isZoomEnabled, optionsLookup, instance, setZoomDataCallback, store]);
88
+ }, [svgRef, drawingArea, isZoomOnWheelEnabled, optionsLookup, instance, setZoomDataCallback, store]);
79
89
  };
@@ -0,0 +1,2 @@
1
+ import type { AxisId, DefaultizedZoomOptions, ZoomData } from '@mui/x-charts/internals';
2
+ export declare function initializeZoomData(options: Record<AxisId, Pick<DefaultizedZoomOptions, 'axisId' | 'minStart' | 'maxEnd'>>, zoomData?: readonly ZoomData[]): ZoomData[];
@@ -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,2 @@
1
+ import type { ZoomInteractionConfig, DefaultizedZoomInteractionConfig } from "./ZoomInteractionConfig.types.js";
2
+ export declare const initializeZoomInteractionConfig: (zoomInteractionConfig?: ZoomInteractionConfig) => DefaultizedZoomInteractionConfig;
@@ -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, AxisId, DefaultizedZoomOptions, ZoomData } from '@mui/x-charts/internals';
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
- // It is helpful to avoid the need to provide the possibly auto-generated id for each axis.
14
- export function initializeZoomData(options, zoomData) {
15
- const zoomDataMap = new Map();
16
- zoomData?.forEach(zoom => {
17
- const option = options[zoom.axisId];
18
- if (option) {
19
- zoomDataMap.set(zoom.axisId, zoom);
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts-pro v8.12.0
2
+ * @mui/x-charts-pro v8.13.0
3
3
  *
4
4
  * @license SEE LICENSE IN LICENSE
5
5
  * This source code is licensed under the SEE LICENSE IN LICENSE license found in the
@@ -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 {};
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -10,6 +10,7 @@ var React = _interopRequireWildcard(require("react"));
10
10
  var _internals = require("@mui/x-charts/internals");
11
11
  var _rafThrottle = require("@mui/x-internals/rafThrottle");
12
12
  var _useZoom = require("./useZoom.utils");
13
+ var _ZoomInteractionConfig = require("../ZoomInteractionConfig.selectors");
13
14
  const usePanOnDrag = ({
14
15
  store,
15
16
  instance,
@@ -18,12 +19,26 @@ const usePanOnDrag = ({
18
19
  const drawingArea = (0, _internals.useSelector)(store, _internals.selectorChartDrawingArea);
19
20
  const optionsLookup = (0, _internals.useSelector)(store, _internals.selectorChartZoomOptionsLookup);
20
21
  const startRef = React.useRef(null);
22
+ const config = (0, _internals.useSelector)(store, _ZoomInteractionConfig.selectorPanInteractionConfig, ['drag']);
23
+ const isPanOnDragEnabled = React.useMemo(() => Object.values(optionsLookup).some(v => v.panning) && config || false, [optionsLookup, config]);
24
+ React.useEffect(() => {
25
+ if (!isPanOnDragEnabled) {
26
+ return;
27
+ }
28
+ instance.updateZoomInteractionListeners('zoomPan', {
29
+ requiredKeys: config.requiredKeys,
30
+ pointerMode: config.pointerMode,
31
+ pointerOptions: {
32
+ mouse: config.mouse,
33
+ touch: config.touch
34
+ }
35
+ });
36
+ }, [isPanOnDragEnabled, config, instance]);
21
37
 
22
38
  // Add event for chart panning
23
- const isPanEnabled = React.useMemo(() => Object.values(optionsLookup).some(v => v.panning) || false, [optionsLookup]);
24
39
  React.useEffect(() => {
25
40
  const element = svgRef.current;
26
- if (element === null || !isPanEnabled) {
41
+ if (element === null || !isPanOnDragEnabled) {
27
42
  return () => {};
28
43
  }
29
44
  const handlePanStart = event => {
@@ -51,15 +66,15 @@ const usePanOnDrag = ({
51
66
  }
52
67
  throttledCallback(event, zoomData);
53
68
  };
54
- const panHandler = instance.addInteractionListener('pan', handlePan);
55
- const panStartHandler = instance.addInteractionListener('panStart', handlePanStart);
56
- const panEndHandler = instance.addInteractionListener('panEnd', handlePanEnd);
69
+ const panHandler = instance.addInteractionListener('zoomPan', handlePan);
70
+ const panStartHandler = instance.addInteractionListener('zoomPanStart', handlePanStart);
71
+ const panEndHandler = instance.addInteractionListener('zoomPanEnd', handlePanEnd);
57
72
  return () => {
58
73
  panStartHandler.cleanup();
59
74
  panHandler.cleanup();
60
75
  panEndHandler.cleanup();
61
76
  throttledCallback.clear();
62
77
  };
63
- }, [instance, svgRef, isPanEnabled, optionsLookup, drawingArea.width, drawingArea.height, setZoomDataCallback, store, startRef]);
78
+ }, [instance, svgRef, isPanOnDragEnabled, optionsLookup, drawingArea.width, drawingArea.height, setZoomDataCallback, store, startRef]);
64
79
  };
65
80
  exports.usePanOnDrag = usePanOnDrag;
@@ -52,7 +52,7 @@ const zoomAtPoint = (centerRatio, scaleRatio, currentZoomData, options) => {
52
52
  exports.zoomAtPoint = zoomAtPoint;
53
53
  function isSpanValid(minRange, maxRange, isZoomIn, option) {
54
54
  const newSpanPercent = maxRange - minRange;
55
- if (isZoomIn && newSpanPercent < option.minSpan || !isZoomIn && newSpanPercent > option.maxSpan) {
55
+ if (newSpanPercent < 0 || isZoomIn && newSpanPercent < option.minSpan || !isZoomIn && newSpanPercent > option.maxSpan) {
56
56
  return false;
57
57
  }
58
58
  if (minRange < option.minStart || maxRange > option.maxEnd) {