@mui/x-charts 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 (52) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/ChartsSurface/ChartsSurface.js +4 -3
  3. package/PieChart/PieArc.d.ts +3 -0
  4. package/PieChart/PieArc.js +13 -12
  5. package/PieChart/PieArcPlot.js +27 -3
  6. package/esm/ChartsSurface/ChartsSurface.js +4 -3
  7. package/esm/PieChart/PieArc.d.ts +3 -0
  8. package/esm/PieChart/PieArc.js +14 -13
  9. package/esm/PieChart/PieArcPlot.js +29 -5
  10. package/esm/index.js +1 -1
  11. package/esm/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.js +60 -32
  12. package/esm/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.types.d.ts +19 -5
  13. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisScale.d.ts +5 -2
  14. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisScale.js +8 -10
  15. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisPreview.selectors.js +7 -5
  16. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisRendering.selectors.d.ts +10 -0
  17. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisRendering.selectors.js +13 -4
  18. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianHighlight.selectors.d.ts +2 -2
  19. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianHighlight.selectors.js +30 -7
  20. package/esm/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.js +6 -4
  21. package/esm/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.selectors.d.ts +6 -1
  22. package/esm/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.selectors.js +2 -1
  23. package/esm/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.d.ts +6 -0
  24. package/esm/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.js +115 -155
  25. package/esm/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.d.ts +14 -2
  26. package/esm/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.js +27 -1
  27. package/esm/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.d.ts +2 -0
  28. package/esm/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.js +2 -5
  29. package/esm/internals/ticks.d.ts +3 -5
  30. package/esm/internals/ticks.js +6 -5
  31. package/index.js +1 -1
  32. package/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.js +59 -31
  33. package/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.types.d.ts +19 -5
  34. package/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisScale.d.ts +5 -2
  35. package/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisScale.js +8 -11
  36. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisPreview.selectors.js +6 -4
  37. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisRendering.selectors.d.ts +10 -0
  38. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisRendering.selectors.js +14 -5
  39. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianHighlight.selectors.d.ts +2 -2
  40. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianHighlight.selectors.js +30 -7
  41. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.js +6 -4
  42. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.selectors.d.ts +6 -1
  43. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.selectors.js +3 -2
  44. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.d.ts +6 -0
  45. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.js +115 -155
  46. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.d.ts +14 -2
  47. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.js +28 -2
  48. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.d.ts +2 -0
  49. package/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.js +1 -4
  50. package/internals/ticks.d.ts +3 -5
  51. package/internals/ticks.js +7 -5
  52. package/package.json +4 -4
@@ -5,137 +5,91 @@ import * as React from 'react';
5
5
  import useEventCallback from '@mui/utils/useEventCallback';
6
6
  import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
7
7
  import { getNextSeriesWithData, getPreviousSeriesWithData, seriesHasData } from "./useChartKeyboardNavigation.helpers.js";
8
+ function getNextIndexFocusedItem(state) {
9
+ let {
10
+ type,
11
+ seriesId
12
+ } = state.keyboardNavigation.item ?? {};
13
+ if (type === undefined ||
14
+ // @ts-ignore sankey is not in MIT version
15
+ type === 'sankey' || seriesId === undefined || !seriesHasData(state.series.processedSeries, type, seriesId)) {
16
+ const nextSeries = getNextSeriesWithData(state.series.processedSeries, type, seriesId);
17
+ if (nextSeries === null) {
18
+ return null;
19
+ }
20
+ type = nextSeries.type;
21
+ seriesId = nextSeries.seriesId;
22
+ }
23
+ const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
24
+ return {
25
+ type,
26
+ seriesId,
27
+ dataIndex: ((state.keyboardNavigation.item?.dataIndex ?? -1) + 1) % dataLength
28
+ };
29
+ }
30
+ function getPreviousIndexFocusedItem(state) {
31
+ let {
32
+ type,
33
+ seriesId
34
+ } = state.keyboardNavigation.item ?? {};
35
+ if (type === undefined ||
36
+ // @ts-ignore sankey is not in MIT version
37
+ type === 'sankey' || seriesId === undefined || !seriesHasData(state.series.processedSeries, type, seriesId)) {
38
+ const previousSeries = getPreviousSeriesWithData(state.series.processedSeries, type, seriesId);
39
+ if (previousSeries === null) {
40
+ return null;
41
+ }
42
+ type = previousSeries.type;
43
+ seriesId = previousSeries.seriesId;
44
+ }
45
+ const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
46
+ return {
47
+ type,
48
+ seriesId,
49
+ dataIndex: (dataLength + (state.keyboardNavigation.item?.dataIndex ?? 1) - 1) % dataLength
50
+ };
51
+ }
52
+ function getNextSeriesFocusedItem(state) {
53
+ let {
54
+ type,
55
+ seriesId
56
+ } = state.keyboardNavigation.item ?? {};
57
+ const nextSeries = getNextSeriesWithData(state.series.processedSeries, type, seriesId);
58
+ if (nextSeries === null) {
59
+ return null; // No series to move the focus to.
60
+ }
61
+ type = nextSeries.type;
62
+ seriesId = nextSeries.seriesId;
63
+ const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
64
+ return {
65
+ type,
66
+ seriesId,
67
+ dataIndex: Math.min(dataLength - 1, state.keyboardNavigation.item?.dataIndex ?? 0)
68
+ };
69
+ }
70
+ function getPreviousSeriesFocusedItem(state) {
71
+ let {
72
+ type,
73
+ seriesId
74
+ } = state.keyboardNavigation.item ?? {};
75
+ const previousSeries = getPreviousSeriesWithData(state.series.processedSeries, type, seriesId);
76
+ if (previousSeries === null) {
77
+ return null; // No series to move the focus to.
78
+ }
79
+ type = previousSeries.type;
80
+ seriesId = previousSeries.seriesId;
81
+ const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
82
+ return {
83
+ type,
84
+ seriesId,
85
+ dataIndex: Math.min(dataLength - 1, state.keyboardNavigation.item?.dataIndex ?? 0)
86
+ };
87
+ }
8
88
  export const useChartKeyboardNavigation = ({
9
89
  params,
10
90
  store,
11
91
  svgRef
12
92
  }) => {
13
- const focusNextItem = useEventCallback(function focusNextItem() {
14
- store.update(state => {
15
- let {
16
- type,
17
- seriesId
18
- } = state.keyboardNavigation.item ?? {};
19
- if (type === undefined ||
20
- // @ts-ignore sankey is not in MIT version
21
- type === 'sankey' || seriesId === undefined || !seriesHasData(state.series.processedSeries, type, seriesId)) {
22
- const nextSeries = getNextSeriesWithData(state.series.processedSeries, type, seriesId);
23
- if (nextSeries === null) {
24
- return _extends({}, state, {
25
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
26
- item: null // No series to move the focus too.
27
- })
28
- });
29
- }
30
- type = nextSeries.type;
31
- seriesId = nextSeries.seriesId;
32
- }
33
- const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
34
- return _extends({}, state, {
35
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
36
- item: {
37
- type,
38
- seriesId,
39
- dataIndex: ((state.keyboardNavigation.item?.dataIndex ?? -1) + 1) % dataLength
40
- }
41
- })
42
- });
43
- });
44
- });
45
- const focusPreviousItem = useEventCallback(function focusPreviousItem() {
46
- store.update(state => {
47
- let {
48
- type,
49
- seriesId
50
- } = state.keyboardNavigation.item ?? {};
51
- if (type === undefined ||
52
- // @ts-ignore sankey is not in MIT version
53
- type === 'sankey' || seriesId === undefined || !seriesHasData(state.series.processedSeries, type, seriesId)) {
54
- const previousSeries = getPreviousSeriesWithData(state.series.processedSeries, type, seriesId);
55
- if (previousSeries === null) {
56
- return _extends({}, state, {
57
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
58
- item: null // No series to move the focus too.} };
59
- })
60
- });
61
- }
62
- type = previousSeries.type;
63
- seriesId = previousSeries.seriesId;
64
- }
65
- const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
66
- return _extends({}, state, {
67
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
68
- item: {
69
- type,
70
- seriesId,
71
- dataIndex: (dataLength + (state.keyboardNavigation.item?.dataIndex ?? 1) - 1) % dataLength
72
- }
73
- })
74
- });
75
- });
76
- });
77
- const focusPreviousSeries = useEventCallback(function focusPreviousSeries() {
78
- let setNewSeries = false;
79
- store.update(state => {
80
- let {
81
- type,
82
- seriesId
83
- } = state.keyboardNavigation.item ?? {};
84
- const previousSeries = getPreviousSeriesWithData(state.series.processedSeries, type, seriesId);
85
- if (previousSeries === null) {
86
- return _extends({}, state, {
87
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
88
- item: null // No series to move the focus too.
89
- })
90
- });
91
- }
92
- type = previousSeries.type;
93
- seriesId = previousSeries.seriesId;
94
- const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
95
- setNewSeries = true;
96
- return _extends({}, state, {
97
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
98
- item: {
99
- type,
100
- seriesId,
101
- dataIndex: Math.min(dataLength - 1, state.keyboardNavigation.item?.dataIndex ?? 0)
102
- }
103
- })
104
- });
105
- });
106
- return setNewSeries;
107
- });
108
- const focusNextSeries = useEventCallback(function focusNextSeries() {
109
- let setNewSeries = false;
110
- store.update(state => {
111
- let {
112
- type,
113
- seriesId
114
- } = state.keyboardNavigation.item ?? {};
115
- const nextSeries = getNextSeriesWithData(state.series.processedSeries, type, seriesId);
116
- if (nextSeries === null) {
117
- return _extends({}, state, {
118
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
119
- item: null // No series to move the focus too.
120
- })
121
- });
122
- }
123
- type = nextSeries.type;
124
- seriesId = nextSeries.seriesId;
125
- const dataLength = state.series.processedSeries[type].series[seriesId].data.length;
126
- setNewSeries = true;
127
- return _extends({}, state, {
128
- keyboardNavigation: _extends({}, state.keyboardNavigation, {
129
- item: {
130
- type,
131
- seriesId,
132
- dataIndex: Math.min(dataLength - 1, state.keyboardNavigation.item?.dataIndex ?? 0)
133
- }
134
- })
135
- });
136
- });
137
- return setNewSeries;
138
- });
139
93
  const removeFocus = useEventCallback(function removeFocus() {
140
94
  store.update(state => {
141
95
  if (state.keyboardNavigation.item === null) {
@@ -154,34 +108,42 @@ export const useChartKeyboardNavigation = ({
154
108
  return undefined;
155
109
  }
156
110
  function keyboardHandler(event) {
157
- switch (event.key) {
158
- case 'ArrowRight':
159
- focusNextItem();
160
- break;
161
- case 'ArrowLeft':
162
- focusPreviousItem();
163
- break;
164
- case 'ArrowDown':
165
- {
166
- const updatedStore = focusPreviousSeries();
167
- if (updatedStore) {
168
- // prevents scrolling
169
- event.preventDefault();
170
- }
111
+ store.update(prevState => {
112
+ let newFocusedItem = prevState.keyboardNavigation.item;
113
+ switch (event.key) {
114
+ case 'ArrowRight':
115
+ newFocusedItem = getNextIndexFocusedItem(prevState);
116
+ break;
117
+ case 'ArrowLeft':
118
+ newFocusedItem = getPreviousIndexFocusedItem(prevState);
171
119
  break;
172
- }
173
- case 'ArrowUp':
174
- {
175
- const updatedStore = focusNextSeries();
176
- if (updatedStore) {
177
- // prevents scrolling
178
- event.preventDefault();
120
+ case 'ArrowDown':
121
+ {
122
+ newFocusedItem = getPreviousSeriesFocusedItem(prevState);
123
+ break;
179
124
  }
125
+ case 'ArrowUp':
126
+ {
127
+ newFocusedItem = getNextSeriesFocusedItem(prevState);
128
+ break;
129
+ }
130
+ default:
180
131
  break;
181
- }
182
- default:
183
- break;
184
- }
132
+ }
133
+ if (newFocusedItem !== prevState.keyboardNavigation.item) {
134
+ event.preventDefault();
135
+ return _extends({}, prevState, prevState.interaction && {
136
+ interaction: _extends({}, prevState.interaction, {
137
+ lastUpdate: 'keyboard'
138
+ })
139
+ }, {
140
+ keyboardNavigation: _extends({}, prevState.keyboardNavigation, {
141
+ item: newFocusedItem
142
+ })
143
+ });
144
+ }
145
+ return prevState;
146
+ });
185
147
  }
186
148
  element.addEventListener('keydown', keyboardHandler);
187
149
  element.addEventListener('blur', removeFocus);
@@ -189,15 +151,13 @@ export const useChartKeyboardNavigation = ({
189
151
  element.removeEventListener('keydown', keyboardHandler);
190
152
  element.removeEventListener('blur', removeFocus);
191
153
  };
192
- }, [svgRef, focusNextItem, focusPreviousItem, removeFocus, focusPreviousSeries, focusNextSeries, params.enableKeyboardNavigation]);
154
+ }, [svgRef, removeFocus, params.enableKeyboardNavigation, store]);
193
155
  useEnhancedEffect(() => store.update(prev => prev.keyboardNavigation.enableKeyboardNavigation === params.enableKeyboardNavigation ? prev : _extends({}, prev, {
194
156
  keyboardNavigation: _extends({}, prev.keyboardNavigation, {
195
157
  enableKeyboardNavigation: !!params.enableKeyboardNavigation
196
158
  })
197
159
  })), [store, params.enableKeyboardNavigation]);
198
- return {
199
- instance: {}
200
- };
160
+ return {};
201
161
  };
202
162
  useChartKeyboardNavigation.getInitialState = params => ({
203
163
  keyboardNavigation: {
@@ -1,3 +1,5 @@
1
+ import { SeriesId } from "../../../../models/seriesType/common.js";
2
+ import { AxisItemIdentifier } from "../../../../models/axis.js";
1
3
  export declare const selectorChartsHasFocusedItem: import("reselect").Selector<import("../../corePlugins/useChartId/useChartId.types.js").UseChartIdState & import("../../corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types.js").UseChartExperimentalFeaturesState & import("../../corePlugins/useChartDimensions/useChartDimensions.types.js").UseChartDimensionsState & import("../../corePlugins/useChartSeries/useChartSeries.types.js").UseChartSeriesState<keyof import("../../../index.js").ChartsSeriesConfig> & import("../../corePlugins/useChartAnimation/useChartAnimation.types.js").UseChartAnimationState & import("../../../index.js").UseChartInteractionListenerState & Partial<import("./useChartKeyboardNavigation.types.js").UseChartKeyboardNavigationState> & {
2
4
  cacheKey: import("../../models/index.js").ChartStateCacheKey;
3
5
  } & {
@@ -12,7 +14,7 @@ export declare const selectorChartsFocusedSeriesId: import("reselect").Selector<
12
14
  cacheKey: import("../../models/index.js").ChartStateCacheKey;
13
15
  } & {
14
16
  cacheKey: import("../../models/index.js").ChartStateCacheKey;
15
- }, import("../../../index.js").SeriesId | undefined, any[]>;
17
+ }, SeriesId | undefined, any[]>;
16
18
  export declare const selectorChartsFocusedDataIndex: import("reselect").Selector<import("../../corePlugins/useChartId/useChartId.types.js").UseChartIdState & import("../../corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types.js").UseChartExperimentalFeaturesState & import("../../corePlugins/useChartDimensions/useChartDimensions.types.js").UseChartDimensionsState & import("../../corePlugins/useChartSeries/useChartSeries.types.js").UseChartSeriesState<keyof import("../../../index.js").ChartsSeriesConfig> & import("../../corePlugins/useChartAnimation/useChartAnimation.types.js").UseChartAnimationState & import("../../../index.js").UseChartInteractionListenerState & Partial<import("./useChartKeyboardNavigation.types.js").UseChartKeyboardNavigationState> & {
17
19
  cacheKey: import("../../models/index.js").ChartStateCacheKey;
18
20
  } & {
@@ -22,4 +24,14 @@ export declare const selectorChartsIsKeyboardNavigationEnabled: import("reselect
22
24
  cacheKey: import("../../models/index.js").ChartStateCacheKey;
23
25
  } & {
24
26
  cacheKey: import("../../models/index.js").ChartStateCacheKey;
25
- }, boolean, any[]>;
27
+ }, boolean, any[]>;
28
+ export declare const selectorChartsKeyboardXAxisIndex: import("reselect").Selector<import("../../corePlugins/useChartId/useChartId.types.js").UseChartIdState & import("../../corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types.js").UseChartExperimentalFeaturesState & import("../../corePlugins/useChartDimensions/useChartDimensions.types.js").UseChartDimensionsState & import("../../corePlugins/useChartSeries/useChartSeries.types.js").UseChartSeriesState<keyof import("../../../index.js").ChartsSeriesConfig> & import("../../corePlugins/useChartAnimation/useChartAnimation.types.js").UseChartAnimationState & import("../../../index.js").UseChartInteractionListenerState & Partial<import("./useChartKeyboardNavigation.types.js").UseChartKeyboardNavigationState> & {
29
+ cacheKey: import("../../models/index.js").ChartStateCacheKey;
30
+ } & {
31
+ cacheKey: import("../../models/index.js").ChartStateCacheKey;
32
+ } & Partial<import("../useChartCartesianAxis/index.js").UseChartCartesianAxisState> & Partial<{}>, AxisItemIdentifier | undefined, any[]>;
33
+ export declare const selectorChartsKeyboardYAxisIndex: import("reselect").Selector<import("../../corePlugins/useChartId/useChartId.types.js").UseChartIdState & import("../../corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types.js").UseChartExperimentalFeaturesState & import("../../corePlugins/useChartDimensions/useChartDimensions.types.js").UseChartDimensionsState & import("../../corePlugins/useChartSeries/useChartSeries.types.js").UseChartSeriesState<keyof import("../../../index.js").ChartsSeriesConfig> & import("../../corePlugins/useChartAnimation/useChartAnimation.types.js").UseChartAnimationState & import("../../../index.js").UseChartInteractionListenerState & Partial<import("./useChartKeyboardNavigation.types.js").UseChartKeyboardNavigationState> & {
34
+ cacheKey: import("../../models/index.js").ChartStateCacheKey;
35
+ } & {
36
+ cacheKey: import("../../models/index.js").ChartStateCacheKey;
37
+ } & Partial<import("../useChartCartesianAxis/index.js").UseChartCartesianAxisState> & Partial<{}>, AxisItemIdentifier | undefined, any[]>;
@@ -1,7 +1,33 @@
1
1
  import { createSelector } from "../../utils/selectors.js";
2
+ import { selectorChartSeriesProcessed } from "../../corePlugins/useChartSeries/index.js";
3
+ import { selectorChartXAxis, selectorChartYAxis } from "../useChartCartesianAxis/useChartCartesianAxisRendering.selectors.js";
2
4
  const selectKeyboardNavigation = state => state.keyboardNavigation;
3
5
  export const selectorChartsHasFocusedItem = createSelector([selectKeyboardNavigation], keyboardNavigationState => keyboardNavigationState?.item != null);
4
6
  export const selectorChartsFocusedSeriesType = createSelector([selectKeyboardNavigation], keyboardNavigationState => keyboardNavigationState?.item?.type);
5
7
  export const selectorChartsFocusedSeriesId = createSelector([selectKeyboardNavigation], keyboardNavigationState => keyboardNavigationState?.item?.seriesId);
6
8
  export const selectorChartsFocusedDataIndex = createSelector([selectKeyboardNavigation], keyboardNavigationState => keyboardNavigationState?.item?.dataIndex);
7
- export const selectorChartsIsKeyboardNavigationEnabled = createSelector([selectKeyboardNavigation], keyboardNavigationState => !!keyboardNavigationState?.enableKeyboardNavigation);
9
+ export const selectorChartsIsKeyboardNavigationEnabled = createSelector([selectKeyboardNavigation], keyboardNavigationState => !!keyboardNavigationState?.enableKeyboardNavigation);
10
+
11
+ /**
12
+ * Selectors to override highlight behavior.
13
+ */
14
+
15
+ const createSelectAxisHighlight = direction => (type, seriesId, dataIndex, axis, series) => {
16
+ if (type === undefined || seriesId === undefined || dataIndex === undefined) {
17
+ return undefined;
18
+ }
19
+ const seriesConfig = series[type]?.series[seriesId];
20
+ if (!seriesConfig) {
21
+ return undefined;
22
+ }
23
+ let axisId = direction === 'x' ? 'xAxisId' in seriesConfig && seriesConfig.xAxisId : 'yAxisId' in seriesConfig && seriesConfig.yAxisId;
24
+ if (axisId === undefined || axisId === false) {
25
+ axisId = axis.axisIds[0];
26
+ }
27
+ return {
28
+ axisId,
29
+ dataIndex
30
+ };
31
+ };
32
+ export const selectorChartsKeyboardXAxisIndex = createSelector([selectorChartsFocusedSeriesType, selectorChartsFocusedSeriesId, selectorChartsFocusedDataIndex, selectorChartXAxis, selectorChartSeriesProcessed], createSelectAxisHighlight('x'));
33
+ export const selectorChartsKeyboardYAxisIndex = createSelector([selectorChartsFocusedSeriesType, selectorChartsFocusedSeriesId, selectorChartsFocusedDataIndex, selectorChartYAxis, selectorChartSeriesProcessed], createSelectAxisHighlight('y'));
@@ -1,6 +1,7 @@
1
1
  import { ChartPluginSignature } from "../../models/index.js";
2
2
  import { ChartSeriesType } from "../../../../models/seriesType/config.js";
3
3
  import { SeriesId } from "../../../../models/seriesType/common.js";
4
+ import { UseChartInteractionSignature } from "../useChartInteraction/index.js";
4
5
  export interface UseChartKeyboardNavigationInstance {}
5
6
  type SeriesItemIdentifier = {
6
7
  /**
@@ -30,5 +31,6 @@ export type UseChartKeyboardNavigationSignature = ChartPluginSignature<{
30
31
  defaultizedParams: UseChartKeyboardNavigationParameters;
31
32
  instance: UseChartKeyboardNavigationInstance;
32
33
  state: UseChartKeyboardNavigationState;
34
+ optionalDependencies: [UseChartInteractionSignature];
33
35
  }>;
34
36
  export {};
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { scaleBand, scalePoint } from '@mui/x-charts-vendor/d3-scale';
3
3
  import { isBandScaleConfig, isPointScaleConfig } from "../../../../models/axis.js";
4
4
  import { getColorScale, getOrdinalColorScale } from "../../../colorScale.js";
5
- import { getTickNumber, scaleTickNumberByRange } from "../../../ticks.js";
5
+ import { getDefaultTickNumber, getTickNumber, scaleTickNumberByRange } from "../../../ticks.js";
6
6
  import { getScale } from "../../../getScale.js";
7
7
  import { isDateData, createDateFormatter } from "../../../dateHelpers.js";
8
8
  import { getAxisExtremum } from "./getAxisExtremum.js";
@@ -99,10 +99,7 @@ export function computeAxisValue({
99
99
  axisExtremums[0] = min;
100
100
  axisExtremums[1] = max;
101
101
  }
102
- const rawTickNumber = getTickNumber(_extends({}, axis, {
103
- range,
104
- domain: axisExtremums
105
- }));
102
+ const rawTickNumber = getTickNumber(axis, axisExtremums, getDefaultTickNumber(Math.abs(range[1] - range[0])));
106
103
  const tickNumber = scaleTickNumberByRange(rawTickNumber, range);
107
104
  const scale = getScale(scaleType, axisExtremums, range);
108
105
  const finalScale = domainLimit === 'nice' ? scale.nice(rawTickNumber) : scale;
@@ -1,6 +1,4 @@
1
1
  import type { TickParams } from "../hooks/useTicks.js";
2
- export declare function getTickNumber(params: TickParams & {
3
- range: number[];
4
- domain: any[];
5
- }): number;
6
- export declare function scaleTickNumberByRange(tickNumber: number, range: number[]): number;
2
+ export declare function getTickNumber(params: TickParams, domain: any[], defaultTickNumber: number): number;
3
+ export declare function scaleTickNumberByRange(tickNumber: number, range: number[]): number;
4
+ export declare function getDefaultTickNumber(dimension: number): number;
@@ -1,14 +1,12 @@
1
- export function getTickNumber(params) {
1
+ export function getTickNumber(params, domain, defaultTickNumber) {
2
2
  const {
3
3
  tickMaxStep,
4
4
  tickMinStep,
5
- tickNumber,
6
- range,
7
- domain
5
+ tickNumber
8
6
  } = params;
9
7
  const maxTicks = tickMinStep === undefined ? 999 : Math.floor(Math.abs(domain[1] - domain[0]) / tickMinStep);
10
8
  const minTicks = tickMaxStep === undefined ? 2 : Math.ceil(Math.abs(domain[1] - domain[0]) / tickMaxStep);
11
- const defaultizedTickNumber = tickNumber ?? Math.floor(Math.abs(range[1] - range[0]) / 50);
9
+ const defaultizedTickNumber = tickNumber ?? defaultTickNumber;
12
10
  return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber));
13
11
  }
14
12
  export function scaleTickNumberByRange(tickNumber, range) {
@@ -19,4 +17,7 @@ export function scaleTickNumberByRange(tickNumber, range) {
19
17
  return 1;
20
18
  }
21
19
  return tickNumber / ((range[1] - range[0]) / 100);
20
+ }
21
+ export function getDefaultTickNumber(dimension) {
22
+ return Math.floor(Math.abs(dimension) / 50);
22
23
  }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts v8.12.0
2
+ * @mui/x-charts v8.13.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -12,43 +12,62 @@ const preventDefault = event => event.preventDefault();
12
12
  const useChartInteractionListener = ({
13
13
  svgRef
14
14
  }) => {
15
+ const gestureManagerRef = React.useRef(null);
15
16
  React.useEffect(() => {
16
17
  const svg = svgRef.current;
17
- if (!svg) {
18
+ if (!gestureManagerRef.current) {
19
+ gestureManagerRef.current = new _core.GestureManager({
20
+ gestures: [
21
+ // We separate the zoom gestures from the gestures that are not zoom related
22
+ // This allows us to configure the zoom gestures based on the zoom configuration.
23
+ new _core.PanGesture({
24
+ name: 'pan',
25
+ threshold: 0,
26
+ maxPointers: 1
27
+ }), new _core.PanGesture({
28
+ name: 'zoomPan',
29
+ threshold: 0,
30
+ maxPointers: 1,
31
+ preventIf: ['zoomTapAndDrag']
32
+ }), new _core.MoveGesture({
33
+ name: 'move',
34
+ preventIf: ['pan', 'zoomPinch', 'zoomPan'] // Prevent move gesture when pan is active
35
+ }), new _core.PinchGesture({
36
+ name: 'zoomPinch',
37
+ threshold: 5,
38
+ preventIf: ['pan', 'zoomPan']
39
+ }), new _core.TurnWheelGesture({
40
+ name: 'zoomTurnWheel',
41
+ sensitivity: 0.01,
42
+ initialDelta: 1
43
+ }), new _core.TapGesture({
44
+ name: 'tap',
45
+ maxDistance: 10,
46
+ preventIf: ['pan', 'zoomPan', 'zoomPinch']
47
+ }), new _core.PressGesture({
48
+ name: 'quickPress',
49
+ duration: 50,
50
+ maxDistance: 10
51
+ }), new _core.TapAndDragGesture({
52
+ name: 'zoomTapAndDrag',
53
+ tapMaxDistance: 10,
54
+ dragThreshold: 10,
55
+ dragTimeout: 1000
56
+ })]
57
+ });
58
+ }
59
+
60
+ // Assign gesture manager after initialization
61
+ const gestureManager = gestureManagerRef.current;
62
+ if (!svg || !gestureManager) {
18
63
  return undefined;
19
64
  }
20
- const gestureManager = new _core.GestureManager({
21
- gestures: [new _core.PanGesture({
22
- name: 'pan',
23
- threshold: 0,
24
- maxPointers: 1
25
- }), new _core.MoveGesture({
26
- name: 'move',
27
- preventIf: ['pan', 'pinch'] // Prevent move gesture when pan is active
28
- }), new _core.PinchGesture({
29
- name: 'pinch',
30
- threshold: 5,
31
- preventIf: ['pan']
32
- }), new _core.TurnWheelGesture({
33
- name: 'turnWheel',
34
- sensitivity: 0.01,
35
- initialDelta: 1
36
- }), new _core.TapGesture({
37
- name: 'tap',
38
- maxDistance: 10,
39
- preventIf: ['pan', 'pinch']
40
- }), new _core.PressGesture({
41
- name: 'quickPress',
42
- duration: 50,
43
- maxDistance: 10
44
- })]
45
- });
46
- gestureManager.registerElement(['pan', 'move', 'pinch', 'turnWheel', 'tap', 'quickPress'], svg);
65
+ gestureManager.registerElement(['pan', 'move', 'zoomPinch', 'zoomPan', 'zoomTurnWheel', 'tap', 'quickPress', 'zoomTapAndDrag'], svg);
47
66
  return () => {
48
67
  // Cleanup gesture manager
49
- gestureManager.destroy();
68
+ gestureManager.unregisterAllGestures(svg);
50
69
  };
51
- }, [svgRef]);
70
+ }, [svgRef, gestureManagerRef]);
52
71
  const addInteractionListener = React.useCallback((interaction, callback, options) => {
53
72
  // Forcefully cast the svgRef to any, it is annoying to fix the types.
54
73
  const svg = svgRef.current;
@@ -57,6 +76,14 @@ const useChartInteractionListener = ({
57
76
  cleanup: () => svg?.removeEventListener(interaction, callback)
58
77
  };
59
78
  }, [svgRef]);
79
+ const updateZoomInteractionListeners = React.useCallback((interaction, options) => {
80
+ const svg = svgRef.current;
81
+ const gestureManager = gestureManagerRef.current;
82
+ if (!gestureManager || !svg) {
83
+ return;
84
+ }
85
+ gestureManager.setGestureOptions(interaction, svg, options ?? {});
86
+ }, [svgRef, gestureManagerRef]);
60
87
  React.useEffect(() => {
61
88
  const svg = svgRef.current;
62
89
 
@@ -73,7 +100,8 @@ const useChartInteractionListener = ({
73
100
  }, [svgRef]);
74
101
  return {
75
102
  instance: {
76
- addInteractionListener
103
+ addInteractionListener,
104
+ updateZoomInteractionListeners
77
105
  }
78
106
  };
79
107
  };
@@ -1,16 +1,23 @@
1
- import { MoveEvent, PanEvent, PinchEvent, PressEvent, TapEvent, TurnWheelEvent } from '@mui/x-internal-gestures/core';
1
+ import { MoveEvent, PanEvent, PanGestureOptions, PinchEvent, PinchGestureOptions, PressEvent, TapEvent, TurnWheelEvent, type TapAndDragEvent, type TapAndDragGestureOptions, type TurnWheelGestureOptions } from '@mui/x-internal-gestures/core';
2
2
  import { ChartPluginSignature } from "../../models/index.js";
3
- export type ChartInteraction = 'pan' | 'panStart' | 'panEnd' | 'pinch' | 'pinchStart' | 'pinchEnd' | 'move' | 'moveStart' | 'moveEnd' | 'quickPress' | 'quickPressEnd' | 'turnWheel' | 'tap';
3
+ export type ChartInteraction = 'pan' | 'panStart' | 'panEnd' | 'zoomPan' | 'zoomPanStart' | 'zoomPanEnd' | 'zoomPinch' | 'zoomPinchStart' | 'zoomPinchEnd' | 'move' | 'moveStart' | 'moveEnd' | 'quickPress' | 'quickPressEnd' | 'zoomTurnWheel' | 'tap';
4
4
  export type InteractionListenerResult = {
5
5
  cleanup: () => void;
6
6
  };
7
7
  export type AddInteractionListener = {
8
- <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'pan' | 'panStart' | 'panEnd', callback: (event: PanEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
9
- <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'pinch' | 'pinchStart' | 'pinchEnd', callback: (event: PinchEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
10
- <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'turnWheel', callback: (event: TurnWheelEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
8
+ <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'pan' | 'panStart' | 'panEnd' | 'zoomPan' | 'zoomPanStart' | 'zoomPanEnd', callback: (event: PanEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
9
+ <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'zoomPinch' | 'zoomPinchStart' | 'zoomPinchEnd', callback: (event: PinchEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
10
+ <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'zoomTurnWheel', callback: (event: TurnWheelEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
11
11
  <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'move' | 'moveStart' | 'moveEnd', callback: (event: MoveEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
12
12
  <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'tap', callback: (event: TapEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
13
13
  <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'quickPress' | 'quickPressEnd', callback: (event: PressEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
14
+ <CustomData extends Record<string, unknown> = Record<string, unknown>>(interaction: 'zoomTapAndDrag' | 'zoomTapAndDragStart' | 'zoomTapAndDragEnd', callback: (event: TapAndDragEvent<CustomData>) => void, options?: boolean | AddEventListenerOptions): InteractionListenerResult;
15
+ };
16
+ export type UpdateZoomInteractionListeners = {
17
+ (interaction: 'zoomPan', options?: Omit<PanGestureOptions<'zoomPan'>, 'name'>): void;
18
+ (interaction: 'zoomPinch', options?: Omit<PinchGestureOptions<'zoomPinch'>, 'name'>): void;
19
+ (interaction: 'zoomTurnWheel', options?: Omit<TurnWheelGestureOptions<'zoomTurnWheel'>, 'name'>): void;
20
+ (interaction: 'zoomTapAndDrag', options?: Omit<TapAndDragGestureOptions<'zoomTapAndDrag'>, 'name'>): void;
14
21
  };
15
22
  export interface UseChartInteractionListenerParameters {}
16
23
  export interface UseChartInteractionListenerState {}
@@ -22,6 +29,13 @@ export interface UseChartInteractionListenerInstance {
22
29
  * @param callback The callback to call when the interaction occurs.
23
30
  */
24
31
  addInteractionListener: AddInteractionListener;
32
+ /**
33
+ * Updates the zoom interaction listeners with the provided options.
34
+ *
35
+ * @param interaction The interaction to update.
36
+ * @param options The options to apply to the interaction.
37
+ */
38
+ updateZoomInteractionListeners: UpdateZoomInteractionListeners;
25
39
  }
26
40
  export type UseChartInteractionListenerSignature = ChartPluginSignature<{
27
41
  params: UseChartInteractionListenerParameters;
@@ -13,6 +13,7 @@ type GetAxesScalesParams<T extends ChartSeriesType = ChartSeriesType> = {
13
13
  * @deprecated To remove in v9. This is an experimental feature to avoid breaking change.
14
14
  */
15
15
  preferStrictDomainInLineCharts?: boolean;
16
+ defaultTickNumber: number;
16
17
  };
17
18
  export declare function getXAxesScales<T extends ChartSeriesType>({
18
19
  drawingArea,
@@ -20,7 +21,8 @@ export declare function getXAxesScales<T extends ChartSeriesType>({
20
21
  axis: axes,
21
22
  seriesConfig,
22
23
  zoomMap,
23
- preferStrictDomainInLineCharts
24
+ preferStrictDomainInLineCharts,
25
+ defaultTickNumber
24
26
  }: GetAxesScalesParams<T> & {
25
27
  axis?: DefaultedAxis[];
26
28
  }): Record<AxisId, ScaleDefinition>;
@@ -30,7 +32,8 @@ export declare function getYAxesScales<T extends ChartSeriesType>({
30
32
  axis: axes,
31
33
  seriesConfig,
32
34
  zoomMap,
33
- preferStrictDomainInLineCharts
35
+ preferStrictDomainInLineCharts,
36
+ defaultTickNumber
34
37
  }: GetAxesScalesParams<T> & {
35
38
  axis?: DefaultedAxis[];
36
39
  }): Record<AxisId, ScaleDefinition>;