@mui/x-charts-pro 8.18.0 → 8.20.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 (54) hide show
  1. package/BarChartPro/BarChartPro.js +6 -1
  2. package/CHANGELOG.md +175 -0
  3. package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  4. package/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +6 -3
  5. package/ChartZoomSlider/internals/ChartAxisZoomSliderThumb.js +20 -6
  6. package/ChartZoomSlider/internals/previews/AreaPreviewPlot.js +1 -1
  7. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +1 -1
  8. package/LineChartPro/LineChartPro.js +6 -1
  9. package/SankeyChart/plugins/useSankeyHighlight.selectors.js +10 -10
  10. package/ScatterChartPro/ScatterChartPro.js +6 -1
  11. package/esm/BarChartPro/BarChartPro.js +6 -1
  12. package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  13. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +6 -3
  14. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderThumb.js +20 -6
  15. package/esm/ChartZoomSlider/internals/previews/AreaPreviewPlot.js +1 -1
  16. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +3 -3
  17. package/esm/LineChartPro/LineChartPro.js +6 -1
  18. package/esm/SankeyChart/plugins/useSankeyHighlight.selectors.js +11 -11
  19. package/esm/ScatterChartPro/ScatterChartPro.js +6 -1
  20. package/esm/hooks/useFunnelSeries.js +3 -5
  21. package/esm/hooks/useHeatmapSeries.js +3 -5
  22. package/esm/hooks/useSankeySeries.js +3 -5
  23. package/esm/index.js +1 -1
  24. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +4 -12
  25. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.js +5 -4
  26. package/esm/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.d.ts +17 -2
  27. package/esm/internals/plugins/useChartProZoom/gestureHooks/usePanOnWheel.d.ts +8 -0
  28. package/esm/internals/plugins/useChartProZoom/gestureHooks/usePanOnWheel.js +98 -0
  29. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.d.ts +1 -1
  30. package/esm/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.js +2 -2
  31. package/esm/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.d.ts +2 -1
  32. package/esm/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.js +38 -5
  33. package/esm/internals/plugins/useChartProZoom/useChartProZoom.js +9 -3
  34. package/esm/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +7 -6
  35. package/esm/utils/index.d.ts +1 -0
  36. package/esm/utils/index.js +2 -0
  37. package/hooks/useFunnelSeries.js +2 -5
  38. package/hooks/useHeatmapSeries.js +2 -5
  39. package/hooks/useSankeySeries.js +2 -5
  40. package/index.js +1 -1
  41. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.d.ts +4 -12
  42. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.selectors.js +4 -3
  43. package/internals/plugins/useChartProZoom/ZoomInteractionConfig.types.d.ts +17 -2
  44. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnWheel.d.ts +8 -0
  45. package/internals/plugins/useChartProZoom/gestureHooks/usePanOnWheel.js +105 -0
  46. package/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.d.ts +1 -1
  47. package/internals/plugins/useChartProZoom/gestureHooks/useZoom.utils.js +2 -2
  48. package/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.d.ts +2 -1
  49. package/internals/plugins/useChartProZoom/initializeZoomInteractionConfig.js +38 -5
  50. package/internals/plugins/useChartProZoom/useChartProZoom.js +9 -3
  51. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +6 -5
  52. package/package.json +6 -6
  53. package/utils/index.d.ts +1 -0
  54. package/utils/index.js +16 -0
@@ -1,7 +1,7 @@
1
- import { createSelector } from '@mui/x-charts/internals';
1
+ import { createSelector } from '@mui/x-internals/store';
2
+ import { selectorChartSeriesProcessed } from '@mui/x-charts/internals';
2
3
  const selectorSankeyHighlight = state => state.highlight;
3
- const selectSeries = state => state.series;
4
- const selectorSankeySeries = createSelector([selectSeries], series => series.processedSeries.sankey?.series[series.processedSeries.sankey?.seriesOrder[0]] || null);
4
+ const selectorSankeySeries = createSelector(selectorChartSeriesProcessed, processedSeries => processedSeries.sankey?.series[processedSeries.sankey?.seriesOrder[0]] || null);
5
5
  const DEFAULT_NODE_HIGHLIGHT = 'links';
6
6
  const DEFAULT_LINK_HIGHLIGHT = 'links';
7
7
  const DEFAULT_FADE = 'none';
@@ -10,32 +10,32 @@ const DEFAULT_FADE = 'none';
10
10
  * Get the node highlight configuration from the Sankey series.
11
11
  * Defaults to 'nodes' if not specified.
12
12
  */
13
- export const selectorNodeHighlightConfig = createSelector([selectorSankeySeries], series => series?.nodeOptions?.highlight ?? DEFAULT_NODE_HIGHLIGHT);
13
+ export const selectorNodeHighlightConfig = createSelector(selectorSankeySeries, series => series?.nodeOptions?.highlight ?? DEFAULT_NODE_HIGHLIGHT);
14
14
 
15
15
  /**
16
16
  * Get the node fade configuration from the Sankey series.
17
17
  * Defaults to 'none' if not specified.
18
18
  */
19
- export const selectorNodeFadeConfig = createSelector([selectorSankeySeries], series => series?.nodeOptions?.fade ?? DEFAULT_FADE);
19
+ export const selectorNodeFadeConfig = createSelector(selectorSankeySeries, series => series?.nodeOptions?.fade ?? DEFAULT_FADE);
20
20
 
21
21
  /**
22
22
  * Get the link highlight configuration from the Sankey series.
23
23
  * Defaults to 'links' if not specified.
24
24
  */
25
- export const selectorLinkHighlightConfig = createSelector([selectorSankeySeries], series => series?.linkOptions?.highlight ?? DEFAULT_LINK_HIGHLIGHT);
25
+ export const selectorLinkHighlightConfig = createSelector(selectorSankeySeries, series => series?.linkOptions?.highlight ?? DEFAULT_LINK_HIGHLIGHT);
26
26
 
27
27
  /**
28
28
  * Get the link fade configuration from the Sankey series.
29
29
  * Defaults to 'none' if not specified.
30
30
  */
31
- export const selectorLinkFadeConfig = createSelector([selectorSankeySeries], series => series?.linkOptions?.fade ?? DEFAULT_FADE);
31
+ export const selectorLinkFadeConfig = createSelector(selectorSankeySeries, series => series?.linkOptions?.fade ?? DEFAULT_FADE);
32
32
 
33
33
  /**
34
34
  * Get the currently highlighted item in the Sankey chart.
35
35
  * @param {UseSankeyHighlightSignature['state']} state The state of the chart.
36
36
  * @returns {SankeyItemIdentifier | null} The highlighted item identifier or null.
37
37
  */
38
- export const selectorSankeyHighlightedItem = createSelector([selectorSankeyHighlight], highlight => highlight.item);
38
+ export const selectorSankeyHighlightedItem = createSelector(selectorSankeyHighlight, highlight => highlight.item);
39
39
 
40
40
  /**
41
41
  * Determines if a specific node should be highlighted.
@@ -43,7 +43,7 @@ export const selectorSankeyHighlightedItem = createSelector([selectorSankeyHighl
43
43
  * - It's the highlighted node (unless highlight mode is 'none')
44
44
  * - It's connected to a highlighted link (based on linkOptions.highlight)
45
45
  */
46
- export const selectorIsNodeHighlighted = createSelector([selectorSankeyHighlightedItem, selectorNodeHighlightConfig, selectorLinkHighlightConfig], (highlightedItem, nodeHighlight, linkHighlight, nodeId) => {
46
+ export const selectorIsNodeHighlighted = createSelector(selectorSankeyHighlightedItem, selectorNodeHighlightConfig, selectorLinkHighlightConfig, (highlightedItem, nodeHighlight, linkHighlight, nodeId) => {
47
47
  if (!highlightedItem) {
48
48
  return false;
49
49
  }
@@ -78,7 +78,7 @@ export const selectorIsNodeHighlighted = createSelector([selectorSankeyHighlight
78
78
  * - It's the highlighted link (unless highlight mode is 'none')
79
79
  * - It's connected to a highlighted node (based on nodeOptions.highlight)
80
80
  */
81
- export const selectorIsLinkHighlighted = createSelector([selectorSankeyHighlightedItem, selectorNodeHighlightConfig, selectorLinkHighlightConfig], (highlightedItem, nodeHighlight, linkHighlight, link) => {
81
+ export const selectorIsLinkHighlighted = createSelector(selectorSankeyHighlightedItem, selectorNodeHighlightConfig, selectorLinkHighlightConfig, (highlightedItem, nodeHighlight, linkHighlight, link) => {
82
82
  if (!highlightedItem) {
83
83
  return false;
84
84
  }
@@ -111,7 +111,7 @@ export const selectorIsLinkHighlighted = createSelector([selectorSankeyHighlight
111
111
  * - This item is not highlighted
112
112
  * - The fade mode is 'global' for the highlighted element type
113
113
  */
114
- export const selectorIsSankeyItemFaded = createSelector([selectorSankeyHighlightedItem, selectorNodeFadeConfig, selectorLinkFadeConfig], (highlightedItem, nodeFade, linkFade, isHighlighted) => {
114
+ export const selectorIsSankeyItemFaded = createSelector(selectorSankeyHighlightedItem, selectorNodeFadeConfig, selectorLinkFadeConfig, (highlightedItem, nodeFade, linkFade, isHighlighted) => {
115
115
  if (!highlightedItem || isHighlighted) {
116
116
  return false;
117
117
  }
@@ -1423,7 +1423,7 @@ process.env.NODE_ENV !== "production" ? ScatterChartPro.propTypes = {
1423
1423
  * Configuration for zoom interactions.
1424
1424
  */
1425
1425
  zoomInteractionConfig: PropTypes.shape({
1426
- pan: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf(['drag', 'pressAndDrag']), PropTypes.shape({
1426
+ pan: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf(['drag', 'pressAndDrag', 'wheel']), PropTypes.shape({
1427
1427
  pointerMode: PropTypes.oneOf(['mouse', 'touch']),
1428
1428
  requiredKeys: PropTypes.arrayOf(PropTypes.string),
1429
1429
  type: PropTypes.oneOf(['drag']).isRequired
@@ -1431,6 +1431,11 @@ process.env.NODE_ENV !== "production" ? ScatterChartPro.propTypes = {
1431
1431
  pointerMode: PropTypes.oneOf(['mouse', 'touch']),
1432
1432
  requiredKeys: PropTypes.arrayOf(PropTypes.string),
1433
1433
  type: PropTypes.oneOf(['pressAndDrag']).isRequired
1434
+ }), PropTypes.shape({
1435
+ allowedDirection: PropTypes.oneOf(['x', 'xy', 'y']),
1436
+ pointerMode: PropTypes.any,
1437
+ requiredKeys: PropTypes.arrayOf(PropTypes.string),
1438
+ type: PropTypes.oneOf(['wheel']).isRequired
1434
1439
  })]).isRequired),
1435
1440
  zoom: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf(['brush', 'doubleTapReset', 'pinch', 'tapAndDrag', 'wheel']), PropTypes.shape({
1436
1441
  pointerMode: PropTypes.any,
@@ -1,8 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createAllSeriesSelectorOfType, createSeriesSelectorsOfType } from '@mui/x-charts/internals';
4
- const useSelectorSeries = createSeriesSelectorsOfType('funnel');
5
- const useSelectorSeriesContext = createAllSeriesSelectorOfType('funnel');
3
+ import { useSeriesOfType, useAllSeriesOfType } from '@mui/x-charts/internals';
6
4
 
7
5
  /**
8
6
  * Get access to the internal state of funnel series.
@@ -27,7 +25,7 @@ const useSelectorSeriesContext = createAllSeriesSelectorOfType('funnel');
27
25
  */
28
26
 
29
27
  export function useFunnelSeries(seriesIds) {
30
- return useSelectorSeries(seriesIds);
28
+ return useSeriesOfType('funnel', seriesIds);
31
29
  }
32
30
 
33
31
  /**
@@ -38,5 +36,5 @@ export function useFunnelSeries(seriesIds) {
38
36
  * @returns the funnel series
39
37
  */
40
38
  export function useFunnelSeriesContext() {
41
- return useSelectorSeriesContext();
39
+ return useAllSeriesOfType('funnel');
42
40
  }
@@ -1,8 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createAllSeriesSelectorOfType, createSeriesSelectorsOfType } from '@mui/x-charts/internals';
4
- const useSelectorSeries = createSeriesSelectorsOfType('heatmap');
5
- const useSelectorSeriesContext = createAllSeriesSelectorOfType('heatmap');
3
+ import { useSeriesOfType, useAllSeriesOfType } from '@mui/x-charts/internals';
6
4
 
7
5
  /**
8
6
  * Get access to the internal state of heatmap series.
@@ -27,7 +25,7 @@ const useSelectorSeriesContext = createAllSeriesSelectorOfType('heatmap');
27
25
  */
28
26
 
29
27
  export function useHeatmapSeries(seriesIds) {
30
- return useSelectorSeries(seriesIds);
28
+ return useSeriesOfType('heatmap', seriesIds);
31
29
  }
32
30
 
33
31
  /**
@@ -38,5 +36,5 @@ export function useHeatmapSeries(seriesIds) {
38
36
  * @returns the heatmap series
39
37
  */
40
38
  export function useHeatmapSeriesContext() {
41
- return useSelectorSeriesContext();
39
+ return useAllSeriesOfType('heatmap');
42
40
  }
@@ -1,8 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createAllSeriesSelectorOfType, createSeriesSelectorsOfType } from '@mui/x-charts/internals';
4
- const useSelectorSeries = createSeriesSelectorsOfType('sankey');
5
- const useSelectorSeriesContext = createAllSeriesSelectorOfType('sankey');
3
+ import { useSeriesOfType, useAllSeriesOfType } from '@mui/x-charts/internals';
6
4
 
7
5
  /**
8
6
  * Get access to the internal state of sankey series.
@@ -27,7 +25,7 @@ const useSelectorSeriesContext = createAllSeriesSelectorOfType('sankey');
27
25
  */
28
26
 
29
27
  export function useSankeySeries(seriesIds) {
30
- return useSelectorSeries(seriesIds);
28
+ return useSeriesOfType('sankey', seriesIds);
31
29
  }
32
30
 
33
31
  /**
@@ -38,5 +36,5 @@ export function useSankeySeries(seriesIds) {
38
36
  * @returns the sankey series
39
37
  */
40
38
  export function useSankeySeriesContext() {
41
- return useSelectorSeriesContext();
39
+ return useAllSeriesOfType('sankey');
42
40
  }
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts-pro v8.18.0
2
+ * @mui/x-charts-pro v8.20.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
@@ -8,10 +8,11 @@ export declare const selectorZoomInteractionConfig: (args_0: import("@mui/x-char
8
8
  requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
9
9
  };
10
10
  pointerMode?: import("@mui/x-internal-gestures/core").PointerMode[];
11
+ allowedDirection?: "x" | "y" | "xy";
11
12
  }) | null;
12
13
  export declare const selectorPanInteractionConfig: (args_0: import("@mui/x-charts/internals/plugins/corePlugins/useChartId/useChartId.types").UseChartIdState & import("@mui/x-charts/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types").UseChartExperimentalFeaturesState & import("@mui/x-charts/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.types").UseChartDimensionsState & import("@mui/x-charts/internals/plugins/corePlugins/useChartSeries/useChartSeries.types").UseChartSeriesState<keyof import("@mui/x-charts/internals").ChartsSeriesConfig> & import("@mui/x-charts/internals/plugins/corePlugins/useChartAnimation/useChartAnimation.types").UseChartAnimationState & import("@mui/x-charts/internals").UseChartInteractionListenerState & import("./useChartProZoom.types.js").UseChartProZoomState & Partial<{}> & {
13
14
  cacheKey: import("@mui/x-charts/internals").ChartStateCacheKey;
14
- }, interactionName: "drag" | "pressAndDrag") => (Omit<import("./ZoomInteractionConfig.types.js").PanInteraction, "pointerMode"> & {
15
+ }, interactionName: "wheel" | "drag" | "pressAndDrag") => (Omit<import("./ZoomInteractionConfig.types.js").PanInteraction, "pointerMode"> & {
15
16
  mouse: {
16
17
  requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
17
18
  };
@@ -19,15 +20,6 @@ export declare const selectorPanInteractionConfig: (args_0: import("@mui/x-chart
19
20
  requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
20
21
  };
21
22
  pointerMode?: import("@mui/x-internal-gestures/core").PointerMode[];
23
+ allowedDirection?: "x" | "y" | "xy";
22
24
  }) | null;
23
- export declare const selectorIsZoomBrushEnabled: (args_0: import("@mui/x-charts/internals/plugins/corePlugins/useChartId/useChartId.types").UseChartIdState & import("@mui/x-charts/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types").UseChartExperimentalFeaturesState & import("@mui/x-charts/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.types").UseChartDimensionsState & import("@mui/x-charts/internals/plugins/corePlugins/useChartSeries/useChartSeries.types").UseChartSeriesState<keyof import("@mui/x-charts/internals").ChartsSeriesConfig> & import("@mui/x-charts/internals/plugins/corePlugins/useChartAnimation/useChartAnimation.types").UseChartAnimationState & import("@mui/x-charts/internals").UseChartInteractionListenerState & Partial<import("@mui/x-charts/internals").UseChartCartesianAxisState> & {
24
- cacheKey: import("@mui/x-charts/internals").ChartStateCacheKey;
25
- }) => false | (Omit<import("./ZoomInteractionConfig.types.js").ZoomInteraction, "pointerMode"> & {
26
- mouse: {
27
- requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
28
- };
29
- touch: {
30
- requiredKeys?: import("@mui/x-internal-gestures/core").KeyboardKey[];
31
- };
32
- pointerMode?: import("@mui/x-internal-gestures/core").PointerMode[];
33
- });
25
+ export declare const selectorIsZoomBrushEnabled: (args_0: {}, zoomInteractionConfig: any) => any;
@@ -1,5 +1,6 @@
1
- import { createSelector, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
1
+ import { createSelector } from '@mui/x-internals/store';
2
+ import { selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
2
3
  import { selectorChartZoomState } from "./useChartProZoom.selectors.js";
3
- export const selectorZoomInteractionConfig = createSelector([selectorChartZoomState], (zoomState, interactionName) => zoomState.zoomInteractionConfig.zoom[interactionName] ?? null);
4
- export const selectorPanInteractionConfig = createSelector([selectorChartZoomState], (zoomState, interactionName) => zoomState.zoomInteractionConfig.pan[interactionName] ?? null);
5
- export const selectorIsZoomBrushEnabled = createSelector([selectorChartZoomOptionsLookup, state => selectorZoomInteractionConfig(state, 'brush')], (zoomOptions, zoomInteractionConfig) => Object.keys(zoomOptions).length > 0 && zoomInteractionConfig || false);
4
+ export const selectorZoomInteractionConfig = createSelector(selectorChartZoomState, (zoomState, interactionName) => zoomState.zoomInteractionConfig.zoom[interactionName] ?? null);
5
+ export const selectorPanInteractionConfig = createSelector(selectorChartZoomState, (zoomState, interactionName) => zoomState.zoomInteractionConfig.pan[interactionName] ?? null);
6
+ export const selectorIsZoomBrushEnabled = createSelector(selectorChartZoomOptionsLookup, state => selectorZoomInteractionConfig(state, 'brush'), (zoomOptions, zoomInteractionConfig) => Object.keys(zoomOptions).length > 0 && zoomInteractionConfig || false);
@@ -15,8 +15,9 @@ export type ZoomInteractionConfig = {
15
15
  * Defines the interactions that trigger panning.
16
16
  * - `drag`: Pans the chart when dragged with the mouse.
17
17
  * - `pressAndDrag`: Pans the chart by pressing and holding, then dragging. Useful for avoiding conflicts with selection gestures.
18
+ * - `wheel`: Pans the chart when the mouse wheel is scrolled (horizontal by default).
18
19
  *
19
- * @default ['drag']
20
+ * @default ['drag', 'wheel']
20
21
  */
21
22
  pan?: readonly (PanInteraction | PanInteraction['type'])[];
22
23
  };
@@ -28,13 +29,14 @@ type Entry<T extends AnyInteraction> = { [K in T['type']]?: Omit<T, 'pointerMode
28
29
  requiredKeys?: KeyboardKey[];
29
30
  };
30
31
  pointerMode?: PointerMode[];
32
+ allowedDirection?: 'x' | 'y' | 'xy';
31
33
  } };
32
34
  export type DefaultizedZoomInteractionConfig = {
33
35
  zoom: Entry<ZoomInteraction>;
34
36
  pan: Entry<PanInteraction>;
35
37
  };
36
38
  export type ZoomInteraction = WheelInteraction | PinchInteraction | TapAndDragInteraction | DoubleTapResetInteraction | BrushInteraction;
37
- export type PanInteraction = DragInteraction | PressAndDragInteraction;
39
+ export type PanInteraction = DragInteraction | PressAndDragInteraction | WheelPanInteraction;
38
40
  export type ZoomInteractionName = ZoomInteraction['type'];
39
41
  export type PanInteractionName = PanInteraction['type'];
40
42
  export type InteractionMode = Exclude<PointerMode, 'pen'>;
@@ -81,6 +83,17 @@ export type TapAndDragInteraction = Unpack<{
81
83
  export type PressAndDragInteraction = Unpack<{
82
84
  type: 'pressAndDrag';
83
85
  } & AllModeProp & AllKeysProp>;
86
+ export type WheelPanInteraction = Unpack<{
87
+ type: 'wheel';
88
+ /**
89
+ * Defines which axes are affected by pan on wheel.
90
+ * - `'x'`: Only pan horizontally
91
+ * - `'y'`: Only pan vertically
92
+ * - `'xy'`: Pan both axes
93
+ * @default 'x'
94
+ */
95
+ allowedDirection?: 'x' | 'y' | 'xy';
96
+ } & NoModeProp & AllKeysProp>;
84
97
  export type DoubleTapResetInteraction = Unpack<{
85
98
  type: 'doubleTapReset';
86
99
  } & AllModeProp & AllKeysProp>;
@@ -91,6 +104,7 @@ export type AnyInteraction = {
91
104
  type: string;
92
105
  pointerMode?: InteractionMode;
93
106
  requiredKeys?: KeyboardKey[];
107
+ allowedDirection?: 'x' | 'y' | 'xy';
94
108
  };
95
109
  export type AnyEntry = Omit<AnyInteraction, 'pointerMode'> & {
96
110
  mouse: {
@@ -100,5 +114,6 @@ export type AnyEntry = Omit<AnyInteraction, 'pointerMode'> & {
100
114
  requiredKeys?: KeyboardKey[];
101
115
  };
102
116
  pointerMode?: PointerMode[];
117
+ allowedDirection?: 'x' | 'y' | 'xy';
103
118
  };
104
119
  export {};
@@ -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 usePanOnWheel: ({
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,98 @@
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 { translateZoom } from "./useZoom.utils.js";
7
+ import { selectorPanInteractionConfig } from "../ZoomInteractionConfig.selectors.js";
8
+ export const usePanOnWheel = ({
9
+ store,
10
+ instance,
11
+ svgRef
12
+ }, setZoomDataCallback) => {
13
+ const drawingArea = useSelector(store, selectorChartDrawingArea);
14
+ const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
15
+ const startedOutsideRef = React.useRef(false);
16
+ const startedOutsideTimeoutRef = React.useRef(null);
17
+ const config = useSelector(store, selectorPanInteractionConfig, 'wheel');
18
+ const isPanOnWheelEnabled = Object.keys(optionsLookup).length > 0 && Boolean(config);
19
+ React.useEffect(() => {
20
+ if (!isPanOnWheelEnabled) {
21
+ return;
22
+ }
23
+ instance.updateZoomInteractionListeners('panTurnWheel', {
24
+ requiredKeys: config.requiredKeys
25
+ });
26
+ }, [config, isPanOnWheelEnabled, instance]);
27
+
28
+ // Add event for chart pan on wheel
29
+ React.useEffect(() => {
30
+ const element = svgRef.current;
31
+ const accumulatedChange = {
32
+ x: 0,
33
+ y: 0
34
+ };
35
+ if (element === null || !isPanOnWheelEnabled) {
36
+ return () => {};
37
+ }
38
+ const rafThrottledSetZoomData = rafThrottle(setZoomDataCallback);
39
+ const wheelHandler = instance.addInteractionListener('panTurnWheel', event => {
40
+ const point = getSVGPoint(element, {
41
+ clientX: event.detail.centroid.x,
42
+ clientY: event.detail.centroid.y
43
+ });
44
+
45
+ // This prevents a pan event from being triggered when the mouse is outside the chart area.
46
+ // The timeout is used to prevent an weird behavior where if the mouse is outside but enters due to
47
+ // scrolling, then the pan event is triggered.
48
+ if (startedOutsideRef.current || !instance.isPointInside(point.x, point.y)) {
49
+ startedOutsideRef.current = true;
50
+ if (startedOutsideTimeoutRef.current) {
51
+ clearTimeout(startedOutsideTimeoutRef.current);
52
+ }
53
+ startedOutsideTimeoutRef.current = setTimeout(() => {
54
+ startedOutsideRef.current = false;
55
+ startedOutsideTimeoutRef.current = null;
56
+ }, 100);
57
+ return;
58
+ }
59
+ event.detail.srcEvent.preventDefault();
60
+ const allowedDirection = config?.allowedDirection ?? 'x';
61
+ if (event.detail.deltaX === 0 && event.detail.deltaY === 0) {
62
+ return;
63
+ }
64
+ accumulatedChange.x += event.detail.deltaX;
65
+ accumulatedChange.y += event.detail.deltaY;
66
+ rafThrottledSetZoomData(prev => {
67
+ const x = accumulatedChange.x;
68
+ const y = accumulatedChange.y;
69
+ accumulatedChange.x = 0;
70
+ accumulatedChange.y = 0;
71
+ let movementX = 0;
72
+ let movementY = 0;
73
+ if (allowedDirection === 'x' || allowedDirection === 'xy') {
74
+ movementX = -x;
75
+ }
76
+ if (allowedDirection === 'y' || allowedDirection === 'xy') {
77
+ movementY = y;
78
+ }
79
+ if (movementX === 0 && movementY === 0) {
80
+ return prev;
81
+ }
82
+ return translateZoom(prev, {
83
+ x: movementX,
84
+ y: movementY
85
+ }, drawingArea, optionsLookup, allowedDirection);
86
+ });
87
+ });
88
+ return () => {
89
+ wheelHandler.cleanup();
90
+ if (startedOutsideTimeoutRef.current) {
91
+ clearTimeout(startedOutsideTimeoutRef.current);
92
+ startedOutsideTimeoutRef.current = null;
93
+ }
94
+ startedOutsideRef.current = false;
95
+ rafThrottledSetZoomData.clear();
96
+ };
97
+ }, [svgRef, drawingArea, isPanOnWheelEnabled, optionsLookup, instance, setZoomDataCallback, store, config]);
98
+ };
@@ -46,4 +46,4 @@ export declare function translateZoom(initialZoomData: readonly ZoomData[], move
46
46
  }, drawingArea: {
47
47
  width: number;
48
48
  height: number;
49
- }, optionsLookup: Record<string | number, DefaultizedZoomOptions>): ZoomData[];
49
+ }, optionsLookup: Record<string | number, DefaultizedZoomOptions>, filterMode?: 'x' | 'y' | 'xy'): ZoomData[];
@@ -104,10 +104,10 @@ export function getVerticalCenterRatio(point, area, reverse) {
104
104
  /**
105
105
  * Translate the zoom data by a given movement.
106
106
  */
107
- export function translateZoom(initialZoomData, movement, drawingArea, optionsLookup) {
107
+ export function translateZoom(initialZoomData, movement, drawingArea, optionsLookup, filterMode = 'xy') {
108
108
  return initialZoomData.map(zoom => {
109
109
  const options = optionsLookup[zoom.axisId];
110
- if (!options || !options.panning) {
110
+ if (!options || !options.panning || options.axisDirection === 'x' && filterMode === 'y' || options.axisDirection === 'y' && filterMode === 'x') {
111
111
  return zoom;
112
112
  }
113
113
  const min = zoom.start;
@@ -1,2 +1,3 @@
1
+ import type { AxisId, DefaultizedZoomOptions } from '@mui/x-charts/internals';
1
2
  import type { ZoomInteractionConfig, DefaultizedZoomInteractionConfig } from "./ZoomInteractionConfig.types.js";
2
- export declare const initializeZoomInteractionConfig: (zoomInteractionConfig?: ZoomInteractionConfig) => DefaultizedZoomInteractionConfig;
3
+ export declare const initializeZoomInteractionConfig: (zoomInteractionConfig?: ZoomInteractionConfig, optionsLookup?: Record<AxisId, DefaultizedZoomOptions>) => DefaultizedZoomInteractionConfig;
@@ -1,10 +1,12 @@
1
1
  'use client';
2
2
 
3
- export const initializeZoomInteractionConfig = zoomInteractionConfig => {
3
+ export const initializeZoomInteractionConfig = (zoomInteractionConfig, optionsLookup) => {
4
4
  const defaultizedConfig = {
5
5
  zoom: {},
6
6
  pan: {}
7
7
  };
8
+
9
+ // Config for zoom
8
10
  if (!zoomInteractionConfig?.zoom) {
9
11
  defaultizedConfig.zoom = {
10
12
  wheel: {
@@ -21,8 +23,10 @@ export const initializeZoomInteractionConfig = zoomInteractionConfig => {
21
23
  }
22
24
  };
23
25
  } else {
24
- defaultizedConfig.zoom = initializeFor(zoomInteractionConfig.zoom);
26
+ defaultizedConfig.zoom = initializeFor('zoom', zoomInteractionConfig.zoom);
25
27
  }
28
+
29
+ // Config for pan
26
30
  if (!zoomInteractionConfig?.pan) {
27
31
  defaultizedConfig.pan = {
28
32
  drag: {
@@ -32,12 +36,37 @@ export const initializeZoomInteractionConfig = zoomInteractionConfig => {
32
36
  touch: {}
33
37
  }
34
38
  };
39
+ let hasXZoom = false;
40
+ let hasYZoom = false;
41
+ if (optionsLookup) {
42
+ Object.values(optionsLookup).forEach(options => {
43
+ if (options.axisDirection === 'x') {
44
+ hasXZoom = true;
45
+ }
46
+ if (options.axisDirection === 'y') {
47
+ hasYZoom = true;
48
+ }
49
+ });
50
+ }
51
+
52
+ // Only add pan on wheel if the x-axis can pan (has zoom enabled) but the y-axis cannot
53
+ // This provides a consistent horizontal panning experience that aligns with typical scrolling behavior
54
+ // When both axes can pan, we avoid wheel interactions to prevent conflicts with vertical scrolling
55
+ if (hasXZoom && !hasYZoom) {
56
+ defaultizedConfig.pan.wheel = {
57
+ type: 'wheel',
58
+ requiredKeys: [],
59
+ allowedDirection: 'x',
60
+ mouse: {},
61
+ touch: {}
62
+ };
63
+ }
35
64
  } else {
36
- defaultizedConfig.pan = initializeFor(zoomInteractionConfig.pan);
65
+ defaultizedConfig.pan = initializeFor('pan', zoomInteractionConfig.pan);
37
66
  }
38
67
  return defaultizedConfig;
39
68
  };
40
- function initializeFor(zoomInteractionConfig) {
69
+ function initializeFor(interactionType, zoomInteractionConfig) {
41
70
  // We aggregate interactions by type
42
71
  const aggregation = zoomInteractionConfig.reduce((acc, interaction) => {
43
72
  if (typeof interaction === 'string') {
@@ -57,7 +86,8 @@ function initializeFor(zoomInteractionConfig) {
57
86
  acc[type].push({
58
87
  type,
59
88
  pointerMode: interaction.pointerMode,
60
- requiredKeys: interaction.requiredKeys
89
+ requiredKeys: interaction.requiredKeys,
90
+ allowedDirection: interaction.allowedDirection
61
91
  });
62
92
  return acc;
63
93
  }, {});
@@ -81,6 +111,9 @@ function initializeFor(zoomInteractionConfig) {
81
111
  requiredKeys: lastTouch?.requiredKeys ?? []
82
112
  } : {}
83
113
  };
114
+ if (type === 'wheel' && interactionType === 'pan') {
115
+ acc[type].allowedDirection = lastEmpty?.allowedDirection ?? 'x';
116
+ }
84
117
  }
85
118
  return acc;
86
119
  }
@@ -6,10 +6,12 @@ import { useSelector, selectorChartZoomOptionsLookup, createZoomLookup, selector
6
6
  import debounce from '@mui/utils/debounce';
7
7
  import { useEffectAfterFirstRender } from '@mui/x-internals/useEffectAfterFirstRender';
8
8
  import { useEventCallback } from '@mui/material/utils';
9
+ import { isDeepEqual } from '@mui/x-internals/isDeepEqual';
9
10
  import { calculateZoom } from "./calculateZoom.js";
10
11
  import { useZoomOnWheel } from "./gestureHooks/useZoomOnWheel.js";
11
12
  import { useZoomOnPinch } from "./gestureHooks/useZoomOnPinch.js";
12
13
  import { usePanOnDrag } from "./gestureHooks/usePanOnDrag.js";
14
+ import { usePanOnWheel } from "./gestureHooks/usePanOnWheel.js";
13
15
  import { useZoomOnTapAndDrag } from "./gestureHooks/useZoomOnTapAndDrag.js";
14
16
  import { usePanOnPressAndDrag } from "./gestureHooks/usePanOnPressAndDrag.js";
15
17
  import { useZoomOnBrush } from "./gestureHooks/useZoomOnBrush.js";
@@ -30,9 +32,9 @@ export const useChartProZoom = pluginData => {
30
32
  const optionsLookup = useSelector(store, selectorChartZoomOptionsLookup);
31
33
  useEffectAfterFirstRender(() => {
32
34
  store.set('zoom', _extends({}, store.state.zoom, {
33
- zoomInteractionConfig: initializeZoomInteractionConfig(zoomInteractionConfig)
35
+ zoomInteractionConfig: initializeZoomInteractionConfig(zoomInteractionConfig, optionsLookup)
34
36
  }));
35
- }, [store, zoomInteractionConfig]);
37
+ }, [store, zoomInteractionConfig, optionsLookup]);
36
38
 
37
39
  // This is debounced. We want to run it only once after the interaction ends.
38
40
  const removeIsInteracting = React.useMemo(() => debounce(() => store.set('zoom', _extends({}, store.state.zoom, {
@@ -55,6 +57,9 @@ export const useChartProZoom = pluginData => {
55
57
  }, [store, paramsZoomData, removeIsInteracting]);
56
58
  const setZoomDataCallback = React.useCallback(zoomData => {
57
59
  const newZoomData = typeof zoomData === 'function' ? zoomData([...store.state.zoom.zoomData]) : zoomData;
60
+ if (isDeepEqual(store.state.zoom.zoomData, newZoomData)) {
61
+ return;
62
+ }
58
63
  onZoomChange(newZoomData);
59
64
  if (store.state.zoom.isControlled) {
60
65
  store.set('zoom', _extends({}, store.state.zoom, {
@@ -113,6 +118,7 @@ export const useChartProZoom = pluginData => {
113
118
  // Add events
114
119
  usePanOnDrag(pluginData, setZoomDataCallback);
115
120
  usePanOnPressAndDrag(pluginData, setZoomDataCallback);
121
+ usePanOnWheel(pluginData, setZoomDataCallback);
116
122
  useZoomOnWheel(pluginData, setZoomDataCallback);
117
123
  useZoomOnPinch(pluginData, setZoomDataCallback);
118
124
  useZoomOnTapAndDrag(pluginData, setZoomDataCallback);
@@ -164,7 +170,7 @@ useChartProZoom.getInitialState = params => {
164
170
  zoomData: initializeZoomData(optionsLookup, userZoomData),
165
171
  isInteracting: false,
166
172
  isControlled: zoomData !== undefined,
167
- zoomInteractionConfig: initializeZoomInteractionConfig(params.zoomInteractionConfig)
173
+ zoomInteractionConfig: initializeZoomInteractionConfig(params.zoomInteractionConfig, optionsLookup)
168
174
  }
169
175
  };
170
176
  };
@@ -1,16 +1,17 @@
1
- import { createSelector, selectorChartZoomMap, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
1
+ import { createSelector } from '@mui/x-internals/store';
2
+ import { selectorChartZoomMap, selectorChartZoomOptionsLookup } from '@mui/x-charts/internals';
2
3
  export const selectorChartZoomState = state => state.zoom;
3
- export const selectorChartZoomIsInteracting = createSelector([selectorChartZoomState], zoom => zoom.isInteracting);
4
- export const selectorChartZoomIsEnabled = createSelector([selectorChartZoomOptionsLookup], optionsLookup => Object.keys(optionsLookup).length > 0);
5
- export const selectorChartAxisZoomData = createSelector([selectorChartZoomMap], (zoomMap, axisId) => zoomMap?.get(axisId));
6
- export const selectorChartCanZoomOut = createSelector([selectorChartZoomState, selectorChartZoomOptionsLookup], (zoomState, zoomOptions) => {
4
+ export const selectorChartZoomIsInteracting = createSelector(selectorChartZoomState, zoom => zoom.isInteracting);
5
+ export const selectorChartZoomIsEnabled = createSelector(selectorChartZoomOptionsLookup, optionsLookup => Object.keys(optionsLookup).length > 0);
6
+ export const selectorChartAxisZoomData = createSelector(selectorChartZoomMap, (zoomMap, axisId) => zoomMap?.get(axisId));
7
+ export const selectorChartCanZoomOut = createSelector(selectorChartZoomState, selectorChartZoomOptionsLookup, (zoomState, zoomOptions) => {
7
8
  return zoomState.zoomData.every(zoomData => {
8
9
  const span = zoomData.end - zoomData.start;
9
10
  const options = zoomOptions[zoomData.axisId];
10
11
  return zoomData.start === options.minStart && zoomData.end === options.maxEnd || span === options.maxSpan;
11
12
  });
12
13
  });
13
- export const selectorChartCanZoomIn = createSelector([selectorChartZoomState, selectorChartZoomOptionsLookup], (zoomState, zoomOptions) => {
14
+ export const selectorChartCanZoomIn = createSelector(selectorChartZoomState, selectorChartZoomOptionsLookup, (zoomState, zoomOptions) => {
14
15
  return zoomState.zoomData.every(zoomData => {
15
16
  const span = zoomData.end - zoomData.start;
16
17
  const options = zoomOptions[zoomData.axisId];
@@ -0,0 +1 @@
1
+ export * from '@mui/x-charts/utils';
@@ -0,0 +1,2 @@
1
+ // Re-export automatically generated, to customize, simply remove this line.
2
+ export * from '@mui/x-charts/utils';
@@ -7,9 +7,6 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.useFunnelSeries = useFunnelSeries;
8
8
  exports.useFunnelSeriesContext = useFunnelSeriesContext;
9
9
  var _internals = require("@mui/x-charts/internals");
10
- const useSelectorSeries = (0, _internals.createSeriesSelectorsOfType)('funnel');
11
- const useSelectorSeriesContext = (0, _internals.createAllSeriesSelectorOfType)('funnel');
12
-
13
10
  /**
14
11
  * Get access to the internal state of funnel series.
15
12
  *
@@ -33,7 +30,7 @@ const useSelectorSeriesContext = (0, _internals.createAllSeriesSelectorOfType)('
33
30
  */
34
31
 
35
32
  function useFunnelSeries(seriesIds) {
36
- return useSelectorSeries(seriesIds);
33
+ return (0, _internals.useSeriesOfType)('funnel', seriesIds);
37
34
  }
38
35
 
39
36
  /**
@@ -44,5 +41,5 @@ function useFunnelSeries(seriesIds) {
44
41
  * @returns the funnel series
45
42
  */
46
43
  function useFunnelSeriesContext() {
47
- return useSelectorSeriesContext();
44
+ return (0, _internals.useAllSeriesOfType)('funnel');
48
45
  }
@@ -7,9 +7,6 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.useHeatmapSeries = useHeatmapSeries;
8
8
  exports.useHeatmapSeriesContext = useHeatmapSeriesContext;
9
9
  var _internals = require("@mui/x-charts/internals");
10
- const useSelectorSeries = (0, _internals.createSeriesSelectorsOfType)('heatmap');
11
- const useSelectorSeriesContext = (0, _internals.createAllSeriesSelectorOfType)('heatmap');
12
-
13
10
  /**
14
11
  * Get access to the internal state of heatmap series.
15
12
  *
@@ -33,7 +30,7 @@ const useSelectorSeriesContext = (0, _internals.createAllSeriesSelectorOfType)('
33
30
  */
34
31
 
35
32
  function useHeatmapSeries(seriesIds) {
36
- return useSelectorSeries(seriesIds);
33
+ return (0, _internals.useSeriesOfType)('heatmap', seriesIds);
37
34
  }
38
35
 
39
36
  /**
@@ -44,5 +41,5 @@ function useHeatmapSeries(seriesIds) {
44
41
  * @returns the heatmap series
45
42
  */
46
43
  function useHeatmapSeriesContext() {
47
- return useSelectorSeriesContext();
44
+ return (0, _internals.useAllSeriesOfType)('heatmap');
48
45
  }