@mui/x-charts 9.12.0 → 9.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 (78) hide show
  1. package/BarChart/BarChart.js +1 -1
  2. package/BarChart/BarChart.mjs +1 -1
  3. package/CHANGELOG.md +152 -0
  4. package/ChartsContainer/ChartsContainer.js +1 -1
  5. package/ChartsContainer/ChartsContainer.mjs +1 -1
  6. package/ChartsXAxis/useAxisTicksProps.js +2 -1
  7. package/ChartsXAxis/useAxisTicksProps.mjs +2 -1
  8. package/ChartsYAxis/shortenLabels.js +16 -26
  9. package/ChartsYAxis/shortenLabels.mjs +17 -27
  10. package/ChartsYAxis/useAxisTicksProps.js +2 -1
  11. package/ChartsYAxis/useAxisTicksProps.mjs +2 -1
  12. package/LineChart/LineChart.js +1 -1
  13. package/LineChart/LineChart.mjs +1 -1
  14. package/LineChart/MarkElement.js +13 -6
  15. package/LineChart/MarkElement.mjs +13 -6
  16. package/RadarChart/RadarAxis/RadarAxis.utils.d.mts +1 -1
  17. package/RadarChart/RadarAxis/RadarAxis.utils.d.ts +1 -1
  18. package/RadarChart/RadarChart.js +1 -1
  19. package/RadarChart/RadarChart.mjs +1 -1
  20. package/RadarChart/RadarSeriesPlot/RadarSeriesMarks.js +6 -2
  21. package/RadarChart/RadarSeriesPlot/RadarSeriesMarks.mjs +6 -2
  22. package/RadarChart/RadarSeriesPlot/RadarSeriesPlot.js +2 -1
  23. package/RadarChart/RadarSeriesPlot/RadarSeriesPlot.mjs +2 -1
  24. package/ScatterChart/ScatterChart.js +1 -1
  25. package/ScatterChart/ScatterChart.mjs +1 -1
  26. package/SparkLineChart/SparkLineChart.js +1 -1
  27. package/SparkLineChart/SparkLineChart.mjs +1 -1
  28. package/constants/index.d.mts +3 -1
  29. package/constants/index.d.ts +3 -1
  30. package/constants/index.js +5 -2
  31. package/constants/index.mjs +4 -1
  32. package/hooks/animation/index.d.mts +1 -0
  33. package/hooks/animation/index.d.ts +1 -0
  34. package/hooks/animation/index.js +11 -0
  35. package/hooks/animation/index.mjs +1 -0
  36. package/hooks/animation/useAnimateMark.d.mts +25 -0
  37. package/hooks/animation/useAnimateMark.d.ts +25 -0
  38. package/hooks/animation/useAnimateMark.js +45 -0
  39. package/hooks/animation/useAnimateMark.mjs +39 -0
  40. package/index.js +1 -1
  41. package/index.mjs +1 -1
  42. package/internals/domUtils.js +17 -16
  43. package/internals/domUtils.mjs +17 -15
  44. package/internals/ellipsize.d.mts +30 -0
  45. package/internals/ellipsize.d.ts +30 -0
  46. package/internals/ellipsize.js +60 -0
  47. package/internals/ellipsize.mjs +59 -0
  48. package/internals/index.d.mts +1 -0
  49. package/internals/index.d.ts +1 -0
  50. package/internals/index.js +8 -0
  51. package/internals/index.mjs +1 -0
  52. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.d.mts +3 -0
  53. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.d.ts +3 -0
  54. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.js +9 -5
  55. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.mjs +9 -5
  56. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisAutoSize.selectors.js +9 -4
  57. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisAutoSize.selectors.mjs +9 -4
  58. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +75 -3
  59. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.mjs +76 -3
  60. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.types.d.mts +6 -2
  61. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.types.d.ts +6 -2
  62. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisLayout.selectors.d.mts +1 -0
  63. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisLayout.selectors.d.ts +1 -0
  64. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.js +44 -11
  65. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.mjs +44 -11
  66. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.d.mts +18 -0
  67. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.d.ts +18 -0
  68. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.js +65 -0
  69. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.mjs +60 -0
  70. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.js +43 -1
  71. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.mjs +43 -1
  72. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.types.d.mts +3 -2
  73. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.types.d.ts +3 -2
  74. package/internals/useDefaultTickLabelStyle.d.mts +7 -0
  75. package/internals/useDefaultTickLabelStyle.d.ts +7 -0
  76. package/internals/useDefaultTickLabelStyle.js +34 -0
  77. package/internals/useDefaultTickLabelStyle.mjs +28 -0
  78. package/package.json +6 -6
@@ -30,6 +30,7 @@ var _exportNames = {
30
30
  getLineLikeTooltip: true,
31
31
  checkBarChartScaleErrors: true,
32
32
  getBandSize: true,
33
+ useDefaultTickLabelStyle: true,
33
34
  EPSILON: true,
34
35
  useChartsTooltipUtilityClasses: true,
35
36
  getChartsTooltipUtilityClass: true,
@@ -157,6 +158,12 @@ Object.defineProperty(exports, "useChartsTooltipUtilityClasses", {
157
158
  return _chartsTooltipClasses.useUtilityClasses;
158
159
  }
159
160
  });
161
+ Object.defineProperty(exports, "useDefaultTickLabelStyle", {
162
+ enumerable: true,
163
+ get: function () {
164
+ return _useDefaultTickLabelStyle.useDefaultTickLabelStyle;
165
+ }
166
+ });
160
167
  Object.defineProperty(exports, "useDrawingArea", {
161
168
  enumerable: true,
162
169
  get: function () {
@@ -967,6 +974,7 @@ var _processLineLikeSeries = require("./processLineLikeSeries");
967
974
  var _getLineLikeTooltip = require("./getLineLikeTooltip");
968
975
  var _checkBarChartScaleErrors = require("../BarChart/checkBarChartScaleErrors");
969
976
  var _getBandSize = require("./getBandSize");
977
+ var _useDefaultTickLabelStyle = require("./useDefaultTickLabelStyle");
970
978
  var _defaultSeriesConfig = require("./plugins/utils/defaultSeriesConfig");
971
979
  Object.keys(_defaultSeriesConfig).forEach(function (key) {
972
980
  if (key === "default" || key === "__esModule") return;
@@ -91,6 +91,7 @@ export { processLineLikeSeries } from "./processLineLikeSeries.mjs";
91
91
  export { getLineLikeTooltip } from "./getLineLikeTooltip.mjs";
92
92
  export { checkBarChartScaleErrors } from "../BarChart/checkBarChartScaleErrors.mjs";
93
93
  export { getBandSize } from "./getBandSize.mjs";
94
+ export { useDefaultTickLabelStyle } from "./useDefaultTickLabelStyle.mjs";
94
95
  export * from "./plugins/utils/defaultSeriesConfig.mjs";
95
96
  export { EPSILON } from "../utils/epsilon.mjs";
96
97
  export { useUtilityClasses as useChartsTooltipUtilityClasses, getChartsTooltipUtilityClass } from "../ChartsTooltip/chartsTooltipClasses.mjs";
@@ -1,4 +1,5 @@
1
1
  import type { DefaultedXAxis, DefaultedYAxis } from "../../../../models/axis.mjs";
2
+ import type { ChartsTextStyle } from "../../../getWordsByLines.mjs";
2
3
  import type { DomainDefinition } from "./domain.mjs";
3
4
  interface ComputeAxisAutoSizeOptions {
4
5
  axis: DefaultedXAxis | DefaultedYAxis;
@@ -9,6 +10,8 @@ interface ComputeAxisAutoSizeOptions {
9
10
  * so tick labels measured from them match what the chart renders.
10
11
  */
11
12
  domain?: DomainDefinition;
13
+ /** Font the ticks render with, used only for measuring. */
14
+ defaultTickLabelStyle?: ChartsTextStyle;
12
15
  }
13
16
  /**
14
17
  * Result of auto-size computation for axes.
@@ -1,4 +1,5 @@
1
1
  import type { DefaultedXAxis, DefaultedYAxis } from "../../../../models/axis.js";
2
+ import type { ChartsTextStyle } from "../../../getWordsByLines.js";
2
3
  import type { DomainDefinition } from "./domain.js";
3
4
  interface ComputeAxisAutoSizeOptions {
4
5
  axis: DefaultedXAxis | DefaultedYAxis;
@@ -9,6 +10,8 @@ interface ComputeAxisAutoSizeOptions {
9
10
  * so tick labels measured from them match what the chart renders.
10
11
  */
11
12
  domain?: DomainDefinition;
13
+ /** Font the ticks render with, used only for measuring. */
14
+ defaultTickLabelStyle?: ChartsTextStyle;
12
15
  }
13
16
  /**
14
17
  * Result of auto-size computation for axes.
@@ -219,7 +219,7 @@ function getRotatedDimension(width, height, angle, direction) {
219
219
  *
220
220
  * Returns both the total size and the computed tick sizes for each group level.
221
221
  */
222
- function computeGroupedAxisAutoSize(axis, direction) {
222
+ function computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle) {
223
223
  const {
224
224
  groups,
225
225
  data,
@@ -238,7 +238,7 @@ function computeGroupedAxisAutoSize(axis, direction) {
238
238
  for (let groupIndex = 0; groupIndex < groups.length; groupIndex += 1) {
239
239
  const group = groups[groupIndex];
240
240
  const groupLabels = selectLargestCandidates(getGroupLabels(data, group));
241
- const groupTickLabelStyle = (0, _extends2.default)({}, axisTickLabelStyle, group.tickLabelStyle);
241
+ const groupTickLabelStyle = (0, _extends2.default)({}, defaultTickLabelStyle, axisTickLabelStyle, group.tickLabelStyle);
242
242
  const angle = groupTickLabelStyle?.angle;
243
243
  const {
244
244
  maxWidth,
@@ -284,12 +284,16 @@ function computeAxisAutoSize(options) {
284
284
  const {
285
285
  axis,
286
286
  direction,
287
- domain
287
+ domain,
288
+ defaultTickLabelStyle
288
289
  } = options;
289
290
  if (hasGroups(axis)) {
290
- return computeGroupedAxisAutoSize(axis, direction);
291
+ return computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle);
291
292
  }
292
- const tickLabelStyle = axis.tickLabelStyle;
293
+
294
+ // Measure with the font the ticks render with. `axis.tickLabelStyle` only holds what the user set,
295
+ // so on its own the measurement element would inherit the document font instead.
296
+ const tickLabelStyle = (0, _extends2.default)({}, defaultTickLabelStyle, axis.tickLabelStyle);
293
297
  const tickSize = axis.tickSize ?? _autoSizeConstants.AXIS_AUTO_SIZE_TICK_SIZE;
294
298
  const hasLabel = Boolean(axis.label);
295
299
  const angle = tickLabelStyle?.angle;
@@ -212,7 +212,7 @@ function getRotatedDimension(width, height, angle, direction) {
212
212
  *
213
213
  * Returns both the total size and the computed tick sizes for each group level.
214
214
  */
215
- function computeGroupedAxisAutoSize(axis, direction) {
215
+ function computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle) {
216
216
  const {
217
217
  groups,
218
218
  data,
@@ -231,7 +231,7 @@ function computeGroupedAxisAutoSize(axis, direction) {
231
231
  for (let groupIndex = 0; groupIndex < groups.length; groupIndex += 1) {
232
232
  const group = groups[groupIndex];
233
233
  const groupLabels = selectLargestCandidates(getGroupLabels(data, group));
234
- const groupTickLabelStyle = _extends({}, axisTickLabelStyle, group.tickLabelStyle);
234
+ const groupTickLabelStyle = _extends({}, defaultTickLabelStyle, axisTickLabelStyle, group.tickLabelStyle);
235
235
  const angle = groupTickLabelStyle?.angle;
236
236
  const {
237
237
  maxWidth,
@@ -277,12 +277,16 @@ export function computeAxisAutoSize(options) {
277
277
  const {
278
278
  axis,
279
279
  direction,
280
- domain
280
+ domain,
281
+ defaultTickLabelStyle
281
282
  } = options;
282
283
  if (hasGroups(axis)) {
283
- return computeGroupedAxisAutoSize(axis, direction);
284
+ return computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle);
284
285
  }
285
- const tickLabelStyle = axis.tickLabelStyle;
286
+
287
+ // Measure with the font the ticks render with. `axis.tickLabelStyle` only holds what the user set,
288
+ // so on its own the measurement element would inherit the document font instead.
289
+ const tickLabelStyle = _extends({}, defaultTickLabelStyle, axis.tickLabelStyle);
286
290
  const tickSize = axis.tickSize ?? AXIS_AUTO_SIZE_TICK_SIZE;
287
291
  const hasLabel = Boolean(axis.label);
288
292
  const angle = tickLabelStyle?.angle;
@@ -10,6 +10,9 @@ var _computeAxisAutoSize = require("./computeAxisAutoSize");
10
10
  var _useChartAxisDomains = require("./useChartAxisDomains.selectors");
11
11
  // Direct state access to avoid circular dependency
12
12
  const selectorIsHydrated = state => state.dimensions.width && state.dimensions.height;
13
+
14
+ // Direct state access to avoid circular dependency
15
+ const selectorDefaultTickLabelStyle = state => state.cartesianAxis?.defaultTickLabelStyle;
13
16
  const EMPTY_SIZES = {};
14
17
  const EMPTY_RESULTS = {};
15
18
 
@@ -17,7 +20,7 @@ const EMPTY_RESULTS = {};
17
20
  * Selector that computes full auto-size results for X axes that have `height: 'auto'`.
18
21
  * Returns a map of axis ID to full result (including group tick sizes for grouped axes).
19
22
  */
20
- const selectorChartXAxisAutoSizeResults = exports.selectorChartXAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawXAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartXAxisDomainsForAutoSize, function selectorChartXAxisAutoSizeResults(xAxes, isHydrated, domainsMap) {
23
+ const selectorChartXAxisAutoSizeResults = exports.selectorChartXAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawXAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartXAxisDomainsForAutoSize, selectorDefaultTickLabelStyle, function selectorChartXAxisAutoSizeResults(xAxes, isHydrated, domainsMap, defaultTickLabelStyle) {
21
24
  const hasAutoAxis = xAxes?.some(axis => axis.height === 'auto');
22
25
  if (!hasAutoAxis || !isHydrated) {
23
26
  return EMPTY_RESULTS;
@@ -29,7 +32,8 @@ const selectorChartXAxisAutoSizeResults = exports.selectorChartXAxisAutoSizeResu
29
32
  const computed = (0, _computeAxisAutoSize.computeAxisAutoSize)({
30
33
  axis,
31
34
  direction: 'x',
32
- domain: domainsMap[axis.id]
35
+ domain: domainsMap[axis.id],
36
+ defaultTickLabelStyle
33
37
  });
34
38
  if (computed !== undefined) {
35
39
  results[axis.id] = computed;
@@ -58,7 +62,7 @@ const selectorChartXAxisAutoSizes = exports.selectorChartXAxisAutoSizes = (0, _s
58
62
  * Selector that computes full auto-size results for Y axes that have `width: 'auto'`.
59
63
  * Returns a map of axis ID to full result (including group tick sizes for grouped axes).
60
64
  */
61
- const selectorChartYAxisAutoSizeResults = exports.selectorChartYAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawYAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartYAxisDomainsForAutoSize, function selectorChartYAxisAutoSizeResults(yAxes, isHydrated, domainsMap) {
65
+ const selectorChartYAxisAutoSizeResults = exports.selectorChartYAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawYAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartYAxisDomainsForAutoSize, selectorDefaultTickLabelStyle, function selectorChartYAxisAutoSizeResults(yAxes, isHydrated, domainsMap, defaultTickLabelStyle) {
62
66
  const hasAutoAxis = yAxes?.some(axis => axis.width === 'auto');
63
67
  if (!hasAutoAxis || !isHydrated) {
64
68
  return EMPTY_RESULTS;
@@ -70,7 +74,8 @@ const selectorChartYAxisAutoSizeResults = exports.selectorChartYAxisAutoSizeResu
70
74
  const computed = (0, _computeAxisAutoSize.computeAxisAutoSize)({
71
75
  axis,
72
76
  direction: 'y',
73
- domain: domainsMap[axis.id]
77
+ domain: domainsMap[axis.id],
78
+ defaultTickLabelStyle
74
79
  });
75
80
  if (computed !== undefined) {
76
81
  results[axis.id] = computed;
@@ -5,6 +5,9 @@ import { selectorChartXAxisDomainsForAutoSize, selectorChartYAxisDomainsForAutoS
5
5
 
6
6
  // Direct state access to avoid circular dependency
7
7
  const selectorIsHydrated = state => state.dimensions.width && state.dimensions.height;
8
+
9
+ // Direct state access to avoid circular dependency
10
+ const selectorDefaultTickLabelStyle = state => state.cartesianAxis?.defaultTickLabelStyle;
8
11
  const EMPTY_SIZES = {};
9
12
  const EMPTY_RESULTS = {};
10
13
 
@@ -12,7 +15,7 @@ const EMPTY_RESULTS = {};
12
15
  * Selector that computes full auto-size results for X axes that have `height: 'auto'`.
13
16
  * Returns a map of axis ID to full result (including group tick sizes for grouped axes).
14
17
  */
15
- export const selectorChartXAxisAutoSizeResults = createSelectorMemoized(selectorChartRawXAxis, selectorIsHydrated, selectorChartXAxisDomainsForAutoSize, function selectorChartXAxisAutoSizeResults(xAxes, isHydrated, domainsMap) {
18
+ export const selectorChartXAxisAutoSizeResults = createSelectorMemoized(selectorChartRawXAxis, selectorIsHydrated, selectorChartXAxisDomainsForAutoSize, selectorDefaultTickLabelStyle, function selectorChartXAxisAutoSizeResults(xAxes, isHydrated, domainsMap, defaultTickLabelStyle) {
16
19
  const hasAutoAxis = xAxes?.some(axis => axis.height === 'auto');
17
20
  if (!hasAutoAxis || !isHydrated) {
18
21
  return EMPTY_RESULTS;
@@ -24,7 +27,8 @@ export const selectorChartXAxisAutoSizeResults = createSelectorMemoized(selector
24
27
  const computed = computeAxisAutoSize({
25
28
  axis,
26
29
  direction: 'x',
27
- domain: domainsMap[axis.id]
30
+ domain: domainsMap[axis.id],
31
+ defaultTickLabelStyle
28
32
  });
29
33
  if (computed !== undefined) {
30
34
  results[axis.id] = computed;
@@ -53,7 +57,7 @@ export const selectorChartXAxisAutoSizes = createSelectorMemoized(selectorChartX
53
57
  * Selector that computes full auto-size results for Y axes that have `width: 'auto'`.
54
58
  * Returns a map of axis ID to full result (including group tick sizes for grouped axes).
55
59
  */
56
- export const selectorChartYAxisAutoSizeResults = createSelectorMemoized(selectorChartRawYAxis, selectorIsHydrated, selectorChartYAxisDomainsForAutoSize, function selectorChartYAxisAutoSizeResults(yAxes, isHydrated, domainsMap) {
60
+ export const selectorChartYAxisAutoSizeResults = createSelectorMemoized(selectorChartRawYAxis, selectorIsHydrated, selectorChartYAxisDomainsForAutoSize, selectorDefaultTickLabelStyle, function selectorChartYAxisAutoSizeResults(yAxes, isHydrated, domainsMap, defaultTickLabelStyle) {
57
61
  const hasAutoAxis = yAxes?.some(axis => axis.width === 'auto');
58
62
  if (!hasAutoAxis || !isHydrated) {
59
63
  return EMPTY_RESULTS;
@@ -65,7 +69,8 @@ export const selectorChartYAxisAutoSizeResults = createSelectorMemoized(selector
65
69
  const computed = computeAxisAutoSize({
66
70
  axis,
67
71
  direction: 'y',
68
- domain: domainsMap[axis.id]
72
+ domain: domainsMap[axis.id],
73
+ defaultTickLabelStyle
69
74
  });
70
75
  if (computed !== undefined) {
71
76
  results[axis.id] = computed;
@@ -10,6 +10,7 @@ exports.useChartCartesianAxis = void 0;
10
10
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
11
  var React = _interopRequireWildcard(require("react"));
12
12
  var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect"));
13
+ var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
13
14
  var _store = require("@mui/x-internals/store");
14
15
  var _useAssertModelConsistency = require("@mui/x-internals/useAssertModelConsistency");
15
16
  var _warning = require("@mui/x-internals/warning");
@@ -23,6 +24,15 @@ var _useChartInteraction = require("../useChartInteraction");
23
24
  var _useChartCartesianInteraction = require("./useChartCartesianInteraction.selectors");
24
25
  var _checkHasInteractionPlugin = require("../useChartInteraction/checkHasInteractionPlugin");
25
26
  var _getAxisClickPayload = require("./getAxisClickPayload");
27
+ var _useDefaultTickLabelStyle = require("../../../useDefaultTickLabelStyle");
28
+ var _useChartKeyboardNavigation = require("../useChartKeyboardNavigation");
29
+ /**
30
+ * Narrows an axis item to one the axis actually has a value for.
31
+ * Axes unrelated to the focused series can exist with empty data, so the index must resolve.
32
+ */
33
+ function hasAxisValueAt(axes, item) {
34
+ return item !== undefined && item.dataIndex !== undefined && axes[item.axisId]?.data?.[item.dataIndex] !== undefined;
35
+ }
26
36
  const useChartCartesianAxis = ({
27
37
  params,
28
38
  store,
@@ -39,6 +49,12 @@ const useChartCartesianAxis = ({
39
49
  onTooltipAxisChange,
40
50
  axesGap
41
51
  } = params;
52
+ const defaultTickLabelStyle = (0, _useDefaultTickLabelStyle.useDefaultTickLabelStyle)();
53
+ (0, _useEnhancedEffect.default)(() => {
54
+ store.set('cartesianAxis', (0, _extends2.default)({}, store.state.cartesianAxis, {
55
+ defaultTickLabelStyle
56
+ }));
57
+ }, [defaultTickLabelStyle, store]);
42
58
  if (process.env.NODE_ENV !== 'production') {
43
59
  const ids = [...(xAxis ?? []), ...(yAxis ?? [])].filter(axis => axis.id).map(axis => axis.id);
44
60
  const duplicates = new Set(ids.filter((id, index) => ids.indexOf(id) !== index));
@@ -92,9 +108,10 @@ const useChartCartesianAxis = ({
92
108
  store.set('cartesianAxis', {
93
109
  axesGap,
94
110
  x: (0, _defaultizeAxis.defaultizeXAxis)(xAxis, dataset, axesGap),
95
- y: (0, _defaultizeAxis.defaultizeYAxis)(yAxis, dataset, axesGap)
111
+ y: (0, _defaultizeAxis.defaultizeYAxis)(yAxis, dataset, axesGap),
112
+ defaultTickLabelStyle
96
113
  });
97
- }, [xAxis, yAxis, dataset, axesGap, store]);
114
+ }, [xAxis, yAxis, dataset, axesGap, defaultTickLabelStyle, store]);
98
115
  const usedXAxis = xAxisIds[0];
99
116
  const usedYAxis = yAxisIds[0];
100
117
  (0, _store.useStoreEffect)(store, _useChartCartesianInteraction.selectorChartAxisInteraction, (prevAxisInteraction, nextAxisInteraction) => {
@@ -197,7 +214,62 @@ const useChartCartesianAxis = ({
197
214
  return () => {
198
215
  axisClickHandler.cleanup();
199
216
  };
200
- }, [params.onAxisClick, processedSeries, chartsLayerContainerRef, xAxisWithScale, xAxisIds, yAxisWithScale, yAxisIds, usedXAxis, usedYAxis, instance]);
217
+ }, [params.onAxisClick, processedSeries, store, chartsLayerContainerRef, xAxisWithScale, xAxisIds, yAxisWithScale, yAxisIds, usedXAxis, usedYAxis, instance]);
218
+
219
+ /**
220
+ * Kept separate from the pointer effect: this listener must survive re-renders, so it reads the
221
+ * axes and the callback when the key is pressed rather than closing over them. Re-subscribing per
222
+ * render would drop keystrokes landing between a keyboard navigation update and its commit.
223
+ */
224
+ const handleKeyboardActivation = (0, _useEventCallback.default)(event => {
225
+ const onAxisClick = params.onAxisClick;
226
+ if (!onAxisClick ||
227
+ // Ignore the auto-repeat while the key is held: a pointer click does not repeat either.
228
+ event.repeat || !(0, _useChartKeyboardNavigation.isItemActivationKey)(event) || !(0, _useChartKeyboardNavigation.selectorChartsIsKeyboardActivationEnabled)(store.state)) {
229
+ return;
230
+ }
231
+ const {
232
+ axis: xAxes
233
+ } = (0, _useChartCartesianAxisRendering.selectorChartXAxis)(store.state);
234
+ const {
235
+ axis: yAxes
236
+ } = (0, _useChartCartesianAxisRendering.selectorChartYAxis)(store.state);
237
+ const xAxisItem = (0, _useChartKeyboardNavigation.selectorChartsKeyboardXAxisIndex)(store.state);
238
+ const yAxisItem = (0, _useChartKeyboardNavigation.selectorChartsKeyboardYAxisIndex)(store.state);
239
+ const focusedAxisItem = hasAxisValueAt(xAxes, xAxisItem) ? {
240
+ item: xAxisItem,
241
+ isXAxis: true
242
+ } : hasAxisValueAt(yAxes, yAxisItem) && {
243
+ item: yAxisItem,
244
+ isXAxis: false
245
+ };
246
+ if (!focusedAxisItem) {
247
+ return;
248
+ }
249
+ const payload = (0, _getAxisClickPayload.getAxisClickPayload)({
250
+ axisId: focusedAxisItem.item.axisId,
251
+ dataIndex: focusedAxisItem.item.dataIndex,
252
+ isXAxis: focusedAxisItem.isXAxis,
253
+ axes: focusedAxisItem.isXAxis ? xAxes : yAxes,
254
+ processedSeries: (0, _useChartSeries.selectorChartSeriesProcessed)(store.state)
255
+ });
256
+ if (payload === null) {
257
+ return;
258
+ }
259
+ event.preventDefault();
260
+ // The callback only describes the pointer event unless the user augments the types.
261
+ onAxisClick(event, payload);
262
+ });
263
+ React.useEffect(() => {
264
+ const element = chartsLayerContainerRef.current;
265
+ if (element === null) {
266
+ return undefined;
267
+ }
268
+ element.addEventListener('keydown', handleKeyboardActivation);
269
+ return () => {
270
+ element.removeEventListener('keydown', handleKeyboardActivation);
271
+ };
272
+ }, [chartsLayerContainerRef, handleKeyboardActivation]);
201
273
  return {};
202
274
  };
203
275
  exports.useChartCartesianAxis = useChartCartesianAxis;
@@ -3,6 +3,7 @@
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import * as React from 'react';
5
5
  import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
6
+ import useEventCallback from '@mui/utils/useEventCallback';
6
7
  import { useStoreEffect } from '@mui/x-internals/store';
7
8
  import { useAssertModelConsistency } from '@mui/x-internals/useAssertModelConsistency';
8
9
  import { warnOnce } from '@mui/x-internals/warning';
@@ -16,6 +17,16 @@ import { selectorChartsInteractionIsInitialized } from "../useChartInteraction/i
16
17
  import { selectorChartAxisInteraction } from "./useChartCartesianInteraction.selectors.mjs";
17
18
  import { checkHasInteractionPlugin } from "../useChartInteraction/checkHasInteractionPlugin.mjs";
18
19
  import { getAxisClickPayload } from "./getAxisClickPayload.mjs";
20
+ import { useDefaultTickLabelStyle } from "../../../useDefaultTickLabelStyle.mjs";
21
+ import { isItemActivationKey, selectorChartsIsKeyboardActivationEnabled, selectorChartsKeyboardXAxisIndex, selectorChartsKeyboardYAxisIndex } from "../useChartKeyboardNavigation/index.mjs";
22
+
23
+ /**
24
+ * Narrows an axis item to one the axis actually has a value for.
25
+ * Axes unrelated to the focused series can exist with empty data, so the index must resolve.
26
+ */
27
+ function hasAxisValueAt(axes, item) {
28
+ return item !== undefined && item.dataIndex !== undefined && axes[item.axisId]?.data?.[item.dataIndex] !== undefined;
29
+ }
19
30
  export const useChartCartesianAxis = ({
20
31
  params,
21
32
  store,
@@ -32,6 +43,12 @@ export const useChartCartesianAxis = ({
32
43
  onTooltipAxisChange,
33
44
  axesGap
34
45
  } = params;
46
+ const defaultTickLabelStyle = useDefaultTickLabelStyle();
47
+ useEnhancedEffect(() => {
48
+ store.set('cartesianAxis', _extends({}, store.state.cartesianAxis, {
49
+ defaultTickLabelStyle
50
+ }));
51
+ }, [defaultTickLabelStyle, store]);
35
52
  if (process.env.NODE_ENV !== 'production') {
36
53
  const ids = [...(xAxis ?? []), ...(yAxis ?? [])].filter(axis => axis.id).map(axis => axis.id);
37
54
  const duplicates = new Set(ids.filter((id, index) => ids.indexOf(id) !== index));
@@ -85,9 +102,10 @@ export const useChartCartesianAxis = ({
85
102
  store.set('cartesianAxis', {
86
103
  axesGap,
87
104
  x: defaultizeXAxis(xAxis, dataset, axesGap),
88
- y: defaultizeYAxis(yAxis, dataset, axesGap)
105
+ y: defaultizeYAxis(yAxis, dataset, axesGap),
106
+ defaultTickLabelStyle
89
107
  });
90
- }, [xAxis, yAxis, dataset, axesGap, store]);
108
+ }, [xAxis, yAxis, dataset, axesGap, defaultTickLabelStyle, store]);
91
109
  const usedXAxis = xAxisIds[0];
92
110
  const usedYAxis = yAxisIds[0];
93
111
  useStoreEffect(store, selectorChartAxisInteraction, (prevAxisInteraction, nextAxisInteraction) => {
@@ -190,7 +208,62 @@ export const useChartCartesianAxis = ({
190
208
  return () => {
191
209
  axisClickHandler.cleanup();
192
210
  };
193
- }, [params.onAxisClick, processedSeries, chartsLayerContainerRef, xAxisWithScale, xAxisIds, yAxisWithScale, yAxisIds, usedXAxis, usedYAxis, instance]);
211
+ }, [params.onAxisClick, processedSeries, store, chartsLayerContainerRef, xAxisWithScale, xAxisIds, yAxisWithScale, yAxisIds, usedXAxis, usedYAxis, instance]);
212
+
213
+ /**
214
+ * Kept separate from the pointer effect: this listener must survive re-renders, so it reads the
215
+ * axes and the callback when the key is pressed rather than closing over them. Re-subscribing per
216
+ * render would drop keystrokes landing between a keyboard navigation update and its commit.
217
+ */
218
+ const handleKeyboardActivation = useEventCallback(event => {
219
+ const onAxisClick = params.onAxisClick;
220
+ if (!onAxisClick ||
221
+ // Ignore the auto-repeat while the key is held: a pointer click does not repeat either.
222
+ event.repeat || !isItemActivationKey(event) || !selectorChartsIsKeyboardActivationEnabled(store.state)) {
223
+ return;
224
+ }
225
+ const {
226
+ axis: xAxes
227
+ } = selectorChartXAxis(store.state);
228
+ const {
229
+ axis: yAxes
230
+ } = selectorChartYAxis(store.state);
231
+ const xAxisItem = selectorChartsKeyboardXAxisIndex(store.state);
232
+ const yAxisItem = selectorChartsKeyboardYAxisIndex(store.state);
233
+ const focusedAxisItem = hasAxisValueAt(xAxes, xAxisItem) ? {
234
+ item: xAxisItem,
235
+ isXAxis: true
236
+ } : hasAxisValueAt(yAxes, yAxisItem) && {
237
+ item: yAxisItem,
238
+ isXAxis: false
239
+ };
240
+ if (!focusedAxisItem) {
241
+ return;
242
+ }
243
+ const payload = getAxisClickPayload({
244
+ axisId: focusedAxisItem.item.axisId,
245
+ dataIndex: focusedAxisItem.item.dataIndex,
246
+ isXAxis: focusedAxisItem.isXAxis,
247
+ axes: focusedAxisItem.isXAxis ? xAxes : yAxes,
248
+ processedSeries: selectorChartSeriesProcessed(store.state)
249
+ });
250
+ if (payload === null) {
251
+ return;
252
+ }
253
+ event.preventDefault();
254
+ // The callback only describes the pointer event unless the user augments the types.
255
+ onAxisClick(event, payload);
256
+ });
257
+ React.useEffect(() => {
258
+ const element = chartsLayerContainerRef.current;
259
+ if (element === null) {
260
+ return undefined;
261
+ }
262
+ element.addEventListener('keydown', handleKeyboardActivation);
263
+ return () => {
264
+ element.removeEventListener('keydown', handleKeyboardActivation);
265
+ };
266
+ }, [chartsLayerContainerRef, handleKeyboardActivation]);
194
267
  return {};
195
268
  };
196
269
  useChartCartesianAxis.params = {
@@ -7,6 +7,8 @@ import type { UseChartInteractionSignature } from "../useChartInteraction/index.
7
7
  import type { ChartsAxisProps } from "../../../../ChartsAxis/index.mjs";
8
8
  import type { UseChartBrushSignature } from "../useChartBrush/index.mjs";
9
9
  import type { SamplingState } from "./sampling.types.mjs";
10
+ import type { ChartsActivationEvent } from "../../../../models/events.mjs";
11
+ import type { ChartsTextStyle } from "../../../getWordsByLines.mjs";
10
12
  /**
11
13
  * The axes' configuration after computing.
12
14
  * An axis in this state already contains a scale function and all the necessary properties to be rendered.
@@ -34,10 +36,10 @@ export interface UseChartCartesianAxisParameters<S extends ScaleName = ScaleName
34
36
  /**
35
37
  * The function called for onClick events.
36
38
  * The second argument contains information about all line/bar elements at the current mouse position.
37
- * @param {MouseEvent} event The mouse event recorded on the `<svg/>` element.
39
+ * @param {ChartsActivationEvent} event The event recorded on the `<svg/>` element.
38
40
  * @param {null | ChartsAxisData} data The data about the clicked axis and items associated with it.
39
41
  */
40
- onAxisClick?: (event: MouseEvent, data: null | ChartsAxisData) => void;
42
+ onAxisClick?: (event: ChartsActivationEvent, data: null | ChartsAxisData) => void;
41
43
  /**
42
44
  * The function called when the pointer position corresponds to a new axis data item.
43
45
  * This update can either be caused by a pointer movement, or an axis update.
@@ -106,6 +108,8 @@ export interface UseChartCartesianAxisState {
106
108
  axesGap: number;
107
109
  x: DefaultedXAxis[];
108
110
  y: DefaultedYAxis[];
111
+ /** Only used to measure tick labels. Never merged into the rendered tick style. */
112
+ defaultTickLabelStyle?: ChartsTextStyle;
109
113
  };
110
114
  /**
111
115
  * The controlled axis item highlighted.
@@ -7,6 +7,8 @@ import type { UseChartInteractionSignature } from "../useChartInteraction/index.
7
7
  import type { ChartsAxisProps } from "../../../../ChartsAxis/index.js";
8
8
  import type { UseChartBrushSignature } from "../useChartBrush/index.js";
9
9
  import type { SamplingState } from "./sampling.types.js";
10
+ import type { ChartsActivationEvent } from "../../../../models/events.js";
11
+ import type { ChartsTextStyle } from "../../../getWordsByLines.js";
10
12
  /**
11
13
  * The axes' configuration after computing.
12
14
  * An axis in this state already contains a scale function and all the necessary properties to be rendered.
@@ -34,10 +36,10 @@ export interface UseChartCartesianAxisParameters<S extends ScaleName = ScaleName
34
36
  /**
35
37
  * The function called for onClick events.
36
38
  * The second argument contains information about all line/bar elements at the current mouse position.
37
- * @param {MouseEvent} event The mouse event recorded on the `<svg/>` element.
39
+ * @param {ChartsActivationEvent} event The event recorded on the `<svg/>` element.
38
40
  * @param {null | ChartsAxisData} data The data about the clicked axis and items associated with it.
39
41
  */
40
- onAxisClick?: (event: MouseEvent, data: null | ChartsAxisData) => void;
42
+ onAxisClick?: (event: ChartsActivationEvent, data: null | ChartsAxisData) => void;
41
43
  /**
42
44
  * The function called when the pointer position corresponds to a new axis data item.
43
45
  * This update can either be caused by a pointer movement, or an axis update.
@@ -106,6 +108,8 @@ export interface UseChartCartesianAxisState {
106
108
  axesGap: number;
107
109
  x: DefaultedXAxis[];
108
110
  y: DefaultedYAxis[];
111
+ /** Only used to measure tick labels. Never merged into the rendered tick style. */
112
+ defaultTickLabelStyle?: ChartsTextStyle;
109
113
  };
110
114
  /**
111
115
  * The controlled axis item highlighted.
@@ -5,6 +5,7 @@ export declare const selectorChartCartesianAxisState: (state: ChartState<[], [Us
5
5
  axesGap: number;
6
6
  x: DefaultedXAxis[];
7
7
  y: DefaultedYAxis[];
8
+ defaultTickLabelStyle?: import("../../../getWordsByLines.mjs").ChartsTextStyle;
8
9
  } | undefined;
9
10
  export type SelectorChartRawXAxisType = <State extends ChartState<[], [UseChartCartesianAxisSignature]>>(state: State) => DefaultedXAxis[] | undefined;
10
11
  export declare const selectorChartRawXAxis: SelectorChartRawXAxisType;
@@ -5,6 +5,7 @@ export declare const selectorChartCartesianAxisState: (state: ChartState<[], [Us
5
5
  axesGap: number;
6
6
  x: DefaultedXAxis[];
7
7
  y: DefaultedYAxis[];
8
+ defaultTickLabelStyle?: import("../../../getWordsByLines.js").ChartsTextStyle;
8
9
  } | undefined;
9
10
  export type SelectorChartRawXAxisType = <State extends ChartState<[], [UseChartCartesianAxisSignature]>>(state: State) => DefaultedXAxis[] | undefined;
10
11
  export declare const selectorChartRawXAxis: SelectorChartRawXAxisType;
@@ -20,6 +20,9 @@ var _useChartSeries2 = require("../../corePlugins/useChartSeries");
20
20
  var _useChartCartesianAxisRendering = require("../useChartCartesianAxis/useChartCartesianAxisRendering.selectors");
21
21
  var _getChartPoint = require("../../../getChartPoint");
22
22
  var _getItemAtAxisPosition = require("./utils/getItemAtAxisPosition");
23
+ var _getItemAtRotationIndex = require("./utils/getItemAtRotationIndex");
24
+ var _useChartPolarAxis = require("../useChartPolarAxis/useChartPolarAxis.selectors");
25
+ var _useChartPolarInteraction = require("../useChartPolarAxis/useChartPolarInteraction.selectors");
23
26
  var _itemActivation = require("./itemActivation");
24
27
  var _useChartKeyboardNavigation = require("./useChartKeyboardNavigation.selectors");
25
28
  /**
@@ -137,30 +140,55 @@ const useChartKeyboardNavigation = ({
137
140
  });
138
141
 
139
142
  /**
140
- * The item a click landed on, read from the item the pointer is over. Falls back to the axis
141
- * under the pointer, which covers the line and area paths, whose hovered item only knows its
142
- * series.
143
+ * The item a click landed on, read from the item the pointer is over. An area or line path only
144
+ * knows its series, and a radar reports no item at all, so an incomplete one falls back to the
145
+ * axis under the pointer.
143
146
  *
144
147
  * A click that resolves nothing is left alone rather than focusing the chart. The drawing area
145
148
  * is a rectangle, but the data rarely fills it: taking the focus from a click next to a pie,
146
149
  * outside its circle, reads as the chart grabbing clicks that were not meant for it.
147
150
  */
148
151
  const resolveItemAtPointer = (0, _useEventCallback.default)(event => {
149
- // Every series reports the item under the pointer, so the click itself needs no wiring.
152
+ const seriesConfig = (0, _useChartSeriesConfig.selectorChartSeriesConfig)(store.state);
150
153
  const hoveredItem = store.state.interaction?.hoveredItem;
151
- if (hoveredItem != null) {
152
- return hoveredItem;
154
+ // Cleaned before the check, because what a series stores is not always an identifier: the
155
+ // scatter reports its whole data point, whose optional `id` would read as an incomplete one.
156
+ const cleanedHoveredItem = hoveredItem == null ? null : (0, _cleanIdentifier.cleanIdentifier)(seriesConfig, hoveredItem);
157
+
158
+ // Series that report a complete item need no further wiring.
159
+ if (cleanedHoveredItem != null && isCompleteFocusIdentifier(cleanedHoveredItem)) {
160
+ return cleanedHoveredItem;
153
161
  }
154
162
  const element = chartsLayerContainerRef.current;
155
163
  const point = element === null ? null : (0, _getChartPoint.getChartPoint)(element, event);
156
- const item = point && instance.isPointInside?.(point.x, point.y) ? (0, _getItemAtAxisPosition.getItemAtAxisPosition)({
164
+ if (!point || !instance.isPointInside?.(point.x, point.y)) {
165
+ return null;
166
+ }
167
+ const processedSeries = (0, _useChartSeries2.selectorChartSeriesProcessed)(store.state);
168
+ // An incomplete hovered item still names the series under the pointer, which is a better
169
+ // target than the focused one: a click on a series should stay on it.
170
+ const focusedItem = cleanedHoveredItem ?? store.state.keyboardNavigation.item;
171
+ const item = (0, _getItemAtAxisPosition.getItemAtAxisPosition)({
157
172
  point,
158
173
  xAxis: (0, _useChartCartesianAxisRendering.selectorChartXAxis)(store.state),
159
174
  yAxis: (0, _useChartCartesianAxisRendering.selectorChartYAxis)(store.state),
160
- processedSeries: (0, _useChartSeries2.selectorChartSeriesProcessed)(store.state),
161
- focusedItem: store.state.keyboardNavigation.item
162
- }) : null;
163
- return item;
175
+ processedSeries,
176
+ focusedItem
177
+ });
178
+ if (item !== null) {
179
+ return item;
180
+ }
181
+
182
+ // A radar area only covers the polygon the data draws, so a click inside the chart but outside
183
+ // it resolves nothing above. Fall back to the rotation axis, the same one the highlight uses.
184
+ if ((0, _useChartPolarAxis.selectorChartRotationAxis)(store.state).axisIds.length === 0) {
185
+ return null;
186
+ }
187
+ return (0, _getItemAtRotationIndex.getItemAtRotationIndex)({
188
+ dataIndex: (0, _useChartPolarInteraction.selectorChartsInteractionRotationAxisIndex)(store.state),
189
+ processedSeries,
190
+ focusedItem
191
+ });
164
192
  });
165
193
  const activationRegistrationsRef = React.useRef(new Map());
166
194
  const nextRegistrationIdRef = React.useRef(0);
@@ -240,6 +268,11 @@ const useChartKeyboardNavigation = ({
240
268
  handler(event, focusedItem);
241
269
  }
242
270
  function keyboardHandler(event) {
271
+ // Item navigation only uses unmodified keys.
272
+ // Modified ones are left to the browser, and to chart interactions such as keyboard zoom and pan.
273
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
274
+ return;
275
+ }
243
276
  let newFocusedItem = store.state.keyboardNavigation.item;
244
277
  const seriesConfig = (0, _useChartSeriesConfig.selectorChartSeriesConfig)(store.state);
245
278
  let seriesType = newFocusedItem?.type;