@mui/x-charts 9.11.0 → 9.12.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 (68) hide show
  1. package/BarChart/BarChart.js +7 -0
  2. package/BarChart/BarChart.mjs +7 -0
  3. package/CHANGELOG.md +165 -0
  4. package/ChartsContainer/ChartsContainer.js +7 -0
  5. package/ChartsContainer/ChartsContainer.mjs +7 -0
  6. package/ChartsContainer/useChartsContainerProps.js +3 -1
  7. package/ChartsContainer/useChartsContainerProps.mjs +3 -1
  8. package/ChartsTooltip/ChartsAxisTooltipContent.js +2 -9
  9. package/ChartsTooltip/ChartsAxisTooltipContent.mjs +2 -9
  10. package/ChartsTooltip/ChartsItemTooltipContent.js +2 -2
  11. package/ChartsTooltip/ChartsItemTooltipContent.mjs +2 -2
  12. package/ChartsTooltip/ChartsTooltipContainer.js +6 -2
  13. package/ChartsTooltip/ChartsTooltipContainer.mjs +6 -2
  14. package/LineChart/CircleMarkElement.js +0 -4
  15. package/LineChart/CircleMarkElement.mjs +0 -4
  16. package/LineChart/LineChart.js +7 -0
  17. package/LineChart/LineChart.mjs +7 -0
  18. package/PieChart/PieChart.js +7 -0
  19. package/PieChart/PieChart.mjs +7 -0
  20. package/RadarChart/RadarAxis/RadarAxis.utils.d.mts +1 -1
  21. package/RadarChart/RadarAxis/RadarAxis.utils.d.ts +1 -1
  22. package/RadarChart/RadarChart.js +7 -0
  23. package/RadarChart/RadarChart.mjs +7 -0
  24. package/RadarChart/useRadarChartProps.js +3 -1
  25. package/RadarChart/useRadarChartProps.mjs +3 -1
  26. package/ScatterChart/ScatterChart.js +7 -0
  27. package/ScatterChart/ScatterChart.mjs +7 -0
  28. package/SparkLineChart/SparkLineChart.js +7 -0
  29. package/SparkLineChart/SparkLineChart.mjs +7 -0
  30. package/hooks/useInteractionItemProps.js +4 -0
  31. package/hooks/useInteractionItemProps.mjs +4 -0
  32. package/index.js +1 -1
  33. package/index.mjs +1 -1
  34. package/internals/components/ChartsAccessibilityProxy/ChartsAccessibilityProxy.js +13 -3
  35. package/internals/components/ChartsAccessibilityProxy/ChartsAccessibilityProxy.mjs +12 -3
  36. package/internals/components/ChartsAccessibilityProxy/focusAccessibilityProxy.d.mts +8 -0
  37. package/internals/components/ChartsAccessibilityProxy/focusAccessibilityProxy.d.ts +8 -0
  38. package/internals/components/ChartsAccessibilityProxy/focusAccessibilityProxy.js +25 -0
  39. package/internals/components/ChartsAccessibilityProxy/focusAccessibilityProxy.mjs +19 -0
  40. package/internals/components/ChartsAccessibilityProxy/index.d.mts +2 -1
  41. package/internals/components/ChartsAccessibilityProxy/index.d.ts +2 -1
  42. package/internals/components/ChartsAccessibilityProxy/index.js +11 -0
  43. package/internals/components/ChartsAccessibilityProxy/index.mjs +2 -1
  44. package/internals/plugins/corePlugins/useChartElementRef/useChartElementRef.js +3 -1
  45. package/internals/plugins/corePlugins/useChartElementRef/useChartElementRef.mjs +3 -1
  46. package/internals/plugins/corePlugins/useChartElementRef/useChartElementRef.types.d.mts +5 -0
  47. package/internals/plugins/corePlugins/useChartElementRef/useChartElementRef.types.d.ts +5 -0
  48. package/internals/plugins/featurePlugins/shared/useRegisterPointerInteractions.js +10 -0
  49. package/internals/plugins/featurePlugins/shared/useRegisterPointerInteractions.mjs +10 -0
  50. package/internals/plugins/featurePlugins/useChartClosestPoint/useChartClosestPoint.js +22 -0
  51. package/internals/plugins/featurePlugins/useChartClosestPoint/useChartClosestPoint.mjs +22 -0
  52. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.js +27 -1
  53. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.mjs +27 -1
  54. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.d.mts +20 -0
  55. package/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.d.ts +20 -0
  56. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.js +209 -35
  57. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.mjs +211 -36
  58. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.d.mts +3 -0
  59. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.d.ts +3 -0
  60. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.js +12 -5
  61. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.mjs +11 -4
  62. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.d.mts +29 -0
  63. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.d.ts +29 -0
  64. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtAxisPosition.d.mts +28 -0
  65. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtAxisPosition.d.ts +28 -0
  66. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtAxisPosition.js +81 -0
  67. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtAxisPosition.mjs +76 -0
  68. package/package.json +4 -4
@@ -145,6 +145,13 @@ process.env.NODE_ENV !== "production" ? ScatterChart.propTypes /* remove-proptyp
145
145
  keyboardActivation: PropTypes.bool,
146
146
  progressiveRendering: PropTypes.bool
147
147
  }),
148
+ /**
149
+ * If `true`, clicking an item immediately shows the keyboard focus indicator on it.
150
+ * By default, clicking sets the item that keyboard navigation starts from, but the focus
151
+ * indicator stays hidden until the user presses a key.
152
+ * @default false
153
+ */
154
+ focusItemOnClick: PropTypes.bool,
148
155
  /**
149
156
  * Option to display a cartesian grid in the background.
150
157
  */
@@ -259,6 +259,13 @@ process.env.NODE_ENV !== "production" ? SparkLineChart.propTypes /* remove-propt
259
259
  keyboardActivation: _propTypes.default.bool,
260
260
  progressiveRendering: _propTypes.default.bool
261
261
  }),
262
+ /**
263
+ * If `true`, clicking an item immediately shows the keyboard focus indicator on it.
264
+ * By default, clicking sets the item that keyboard navigation starts from, but the focus
265
+ * indicator stays hidden until the user presses a key.
266
+ * @default false
267
+ */
268
+ focusItemOnClick: _propTypes.default.bool,
262
269
  /**
263
270
  * The height of the chart in px. If not defined, it takes the height of the parent element.
264
271
  */
@@ -252,6 +252,13 @@ process.env.NODE_ENV !== "production" ? SparkLineChart.propTypes /* remove-propt
252
252
  keyboardActivation: PropTypes.bool,
253
253
  progressiveRendering: PropTypes.bool
254
254
  }),
255
+ /**
256
+ * If `true`, clicking an item immediately shows the keyboard focus indicator on it.
257
+ * By default, clicking sets the item that keyboard navigation starts from, but the focus
258
+ * indicator stays hidden until the user presses a key.
259
+ * @default false
260
+ */
261
+ focusItemOnClick: PropTypes.bool,
255
262
  /**
256
263
  * The height of the chart in px. If not defined, it takes the height of the parent element.
257
264
  */
@@ -24,11 +24,13 @@ const useInteractionItemProps = data => {
24
24
  const onPointerEnter = (0, _useEventCallback.default)(() => {
25
25
  interactionActive.current = true;
26
26
  instance.setLastUpdateSource('pointer');
27
+ instance.setHoveredItem(data);
27
28
  instance.setTooltipItem(data);
28
29
  instance.setHighlight(data);
29
30
  });
30
31
  const onPointerLeave = (0, _useEventCallback.default)(() => {
31
32
  interactionActive.current = false;
33
+ instance.clearHoveredItem(data);
32
34
  instance.removeTooltipItem(data);
33
35
  instance.clearHighlight();
34
36
  });
@@ -53,6 +55,7 @@ function getInteractionItemProps(instance, item) {
53
55
  return;
54
56
  }
55
57
  instance.setLastUpdateSource('pointer');
58
+ instance.setHoveredItem(item);
56
59
  instance.setTooltipItem(item);
57
60
  instance.setHighlight(item);
58
61
  }
@@ -60,6 +63,7 @@ function getInteractionItemProps(instance, item) {
60
63
  if (!item) {
61
64
  return;
62
65
  }
66
+ instance.clearHoveredItem(item);
63
67
  instance.removeTooltipItem(item);
64
68
  instance.clearHighlight();
65
69
  }
@@ -16,11 +16,13 @@ export const useInteractionItemProps = data => {
16
16
  const onPointerEnter = useEventCallback(() => {
17
17
  interactionActive.current = true;
18
18
  instance.setLastUpdateSource('pointer');
19
+ instance.setHoveredItem(data);
19
20
  instance.setTooltipItem(data);
20
21
  instance.setHighlight(data);
21
22
  });
22
23
  const onPointerLeave = useEventCallback(() => {
23
24
  interactionActive.current = false;
25
+ instance.clearHoveredItem(data);
24
26
  instance.removeTooltipItem(data);
25
27
  instance.clearHighlight();
26
28
  });
@@ -44,6 +46,7 @@ export function getInteractionItemProps(instance, item) {
44
46
  return;
45
47
  }
46
48
  instance.setLastUpdateSource('pointer');
49
+ instance.setHoveredItem(item);
47
50
  instance.setTooltipItem(item);
48
51
  instance.setHighlight(item);
49
52
  }
@@ -51,6 +54,7 @@ export function getInteractionItemProps(instance, item) {
51
54
  if (!item) {
52
55
  return;
53
56
  }
57
+ instance.clearHoveredItem(item);
54
58
  instance.removeTooltipItem(item);
55
59
  instance.clearHighlight();
56
60
  }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts v9.11.0
2
+ * @mui/x-charts v9.12.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts v9.11.0
2
+ * @mui/x-charts v9.12.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -1,13 +1,16 @@
1
1
  "use strict";
2
2
  'use client';
3
3
 
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
5
  var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
5
6
  Object.defineProperty(exports, "__esModule", {
6
7
  value: true
7
8
  });
8
9
  exports.ChartsAccessibilityProxy = ChartsAccessibilityProxy;
9
10
  var React = _interopRequireWildcard(require("react"));
11
+ var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef"));
10
12
  var _useChartId = require("../../../hooks/useChartId");
13
+ var _ChartsProvider = require("../../../context/ChartsProvider");
11
14
  var _useDescription = require("./useDescription");
12
15
  var _jsxRuntime = require("react/jsx-runtime");
13
16
  /**
@@ -35,9 +38,13 @@ var _jsxRuntime = require("react/jsx-runtime");
35
38
  function ChartsAccessibilityProxy() {
36
39
  const message = (0, _useDescription.useDescription)();
37
40
  const chartId = (0, _useChartId.useChartId)();
41
+ const {
42
+ instance
43
+ } = (0, _ChartsProvider.useChartsContext)();
38
44
  const currentFormatRef = React.useRef(null);
39
45
  const currentIndexRef = React.useRef(0);
40
46
  const containerRef = React.useRef(null);
47
+ const handleRef = (0, _useForkRef.default)(containerRef, instance.chartsAccessibilityProxyRef);
41
48
  React.useEffect(() => {
42
49
  const container = containerRef.current;
43
50
  if (!container) {
@@ -93,9 +100,12 @@ function ChartsAccessibilityProxy() {
93
100
  }
94
101
  }, [message, chartId]);
95
102
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
96
- role: "none",
97
- tabIndex: message ? undefined : 0,
98
- ref: containerRef,
103
+ role: "none"
104
+ // Stays focusable once an announcer child owns the tab stop, otherwise the browser would
105
+ // blur the root mid hand-off and the chart would look like it lost the focus.
106
+ ,
107
+ tabIndex: message ? -1 : 0,
108
+ ref: handleRef,
99
109
  style: fullSizeLayerStyle
100
110
  });
101
111
  }
@@ -1,7 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import * as React from 'react';
4
+ import useForkRef from '@mui/utils/useForkRef';
4
5
  import { useChartId } from "../../../hooks/useChartId.mjs";
6
+ import { useChartsContext } from "../../../context/ChartsProvider/index.mjs";
5
7
  import { useDescription } from "./useDescription.mjs";
6
8
 
7
9
  /**
@@ -31,9 +33,13 @@ const fullSizeLayerStyle = {
31
33
  export function ChartsAccessibilityProxy() {
32
34
  const message = useDescription();
33
35
  const chartId = useChartId();
36
+ const {
37
+ instance
38
+ } = useChartsContext();
34
39
  const currentFormatRef = React.useRef(null);
35
40
  const currentIndexRef = React.useRef(0);
36
41
  const containerRef = React.useRef(null);
42
+ const handleRef = useForkRef(containerRef, instance.chartsAccessibilityProxyRef);
37
43
  React.useEffect(() => {
38
44
  const container = containerRef.current;
39
45
  if (!container) {
@@ -89,9 +95,12 @@ export function ChartsAccessibilityProxy() {
89
95
  }
90
96
  }, [message, chartId]);
91
97
  return /*#__PURE__*/_jsx("div", {
92
- role: "none",
93
- tabIndex: message ? undefined : 0,
94
- ref: containerRef,
98
+ role: "none"
99
+ // Stays focusable once an announcer child owns the tab stop, otherwise the browser would
100
+ // blur the root mid hand-off and the chart would look like it lost the focus.
101
+ ,
102
+ tabIndex: message ? -1 : 0,
103
+ ref: handleRef,
95
104
  style: fullSizeLayerStyle
96
105
  });
97
106
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Moves the DOM focus to the chart tab stop.
3
+ * The proxy root is only tabbable while there is no description, otherwise one of the announcer
4
+ * children holds the tab stop.
5
+ * @param {HTMLDivElement | null} root The accessibility proxy root.
6
+ * @returns {boolean} `true` when an element was focused.
7
+ */
8
+ export declare function focusAccessibilityProxy(root: HTMLDivElement | null): boolean;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Moves the DOM focus to the chart tab stop.
3
+ * The proxy root is only tabbable while there is no description, otherwise one of the announcer
4
+ * children holds the tab stop.
5
+ * @param {HTMLDivElement | null} root The accessibility proxy root.
6
+ * @returns {boolean} `true` when an element was focused.
7
+ */
8
+ export declare function focusAccessibilityProxy(root: HTMLDivElement | null): boolean;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.focusAccessibilityProxy = focusAccessibilityProxy;
7
+ /**
8
+ * Moves the DOM focus to the chart tab stop.
9
+ * The proxy root is only tabbable while there is no description, otherwise one of the announcer
10
+ * children holds the tab stop.
11
+ * @param {HTMLDivElement | null} root The accessibility proxy root.
12
+ * @returns {boolean} `true` when an element was focused.
13
+ */
14
+ function focusAccessibilityProxy(root) {
15
+ if (!root) {
16
+ return false;
17
+ }
18
+ const target = root.querySelector('[tabindex="0"]') ?? (root.tabIndex >= 0 ? root : null);
19
+
20
+ // The proxy covers the whole chart, focusing it would scroll long pages.
21
+ target?.focus({
22
+ preventScroll: true
23
+ });
24
+ return target !== null;
25
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Moves the DOM focus to the chart tab stop.
3
+ * The proxy root is only tabbable while there is no description, otherwise one of the announcer
4
+ * children holds the tab stop.
5
+ * @param {HTMLDivElement | null} root The accessibility proxy root.
6
+ * @returns {boolean} `true` when an element was focused.
7
+ */
8
+ export function focusAccessibilityProxy(root) {
9
+ if (!root) {
10
+ return false;
11
+ }
12
+ const target = root.querySelector('[tabindex="0"]') ?? (root.tabIndex >= 0 ? root : null);
13
+
14
+ // The proxy covers the whole chart, focusing it would scroll long pages.
15
+ target?.focus({
16
+ preventScroll: true
17
+ });
18
+ return target !== null;
19
+ }
@@ -1 +1,2 @@
1
- export * from "./ChartsAccessibilityProxy.mjs";
1
+ export * from "./ChartsAccessibilityProxy.mjs";
2
+ export * from "./focusAccessibilityProxy.mjs";
@@ -1 +1,2 @@
1
- export * from "./ChartsAccessibilityProxy.js";
1
+ export * from "./ChartsAccessibilityProxy.js";
2
+ export * from "./focusAccessibilityProxy.js";
@@ -13,4 +13,15 @@ Object.keys(_ChartsAccessibilityProxy).forEach(function (key) {
13
13
  return _ChartsAccessibilityProxy[key];
14
14
  }
15
15
  });
16
+ });
17
+ var _focusAccessibilityProxy = require("./focusAccessibilityProxy");
18
+ Object.keys(_focusAccessibilityProxy).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _focusAccessibilityProxy[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function () {
24
+ return _focusAccessibilityProxy[key];
25
+ }
26
+ });
16
27
  });
@@ -1 +1,2 @@
1
- export * from "./ChartsAccessibilityProxy.mjs";
1
+ export * from "./ChartsAccessibilityProxy.mjs";
2
+ export * from "./focusAccessibilityProxy.mjs";
@@ -10,10 +10,12 @@ var React = _interopRequireWildcard(require("react"));
10
10
  const useChartElementRef = () => {
11
11
  const chartsLayerContainerRef = React.useRef(null);
12
12
  const chartRootRef = React.useRef(null);
13
+ const chartsAccessibilityProxyRef = React.useRef(null);
13
14
  return {
14
15
  instance: {
15
16
  chartsLayerContainerRef,
16
- chartRootRef
17
+ chartRootRef,
18
+ chartsAccessibilityProxyRef
17
19
  }
18
20
  };
19
21
  };
@@ -4,10 +4,12 @@ import * as React from 'react';
4
4
  export const useChartElementRef = () => {
5
5
  const chartsLayerContainerRef = React.useRef(null);
6
6
  const chartRootRef = React.useRef(null);
7
+ const chartsAccessibilityProxyRef = React.useRef(null);
7
8
  return {
8
9
  instance: {
9
10
  chartsLayerContainerRef,
10
- chartRootRef
11
+ chartRootRef,
12
+ chartsAccessibilityProxyRef
11
13
  }
12
14
  };
13
15
  };
@@ -9,6 +9,11 @@ export interface UseChartElementRefInstance {
9
9
  * Reference to the chart root element.
10
10
  */
11
11
  chartRootRef: React.RefObject<Element | null>;
12
+ /**
13
+ * Reference to the root of the accessibility proxy, the element owning the chart tab stop.
14
+ * It is `null` when keyboard navigation is disabled.
15
+ */
16
+ chartsAccessibilityProxyRef: React.RefObject<HTMLDivElement | null>;
12
17
  }
13
18
  export type UseChartElementRefSignature = ChartPluginSignature<{
14
19
  instance: UseChartElementRefInstance;
@@ -9,6 +9,11 @@ export interface UseChartElementRefInstance {
9
9
  * Reference to the chart root element.
10
10
  */
11
11
  chartRootRef: React.RefObject<Element | null>;
12
+ /**
13
+ * Reference to the root of the accessibility proxy, the element owning the chart tab stop.
14
+ * It is `null` when keyboard navigation is disabled.
15
+ */
16
+ chartsAccessibilityProxyRef: React.RefObject<HTMLDivElement | null>;
12
17
  }
13
18
  export type UseChartElementRefSignature = ChartPluginSignature<{
14
19
  instance: UseChartElementRefInstance;
@@ -40,6 +40,7 @@ function useRegisterPointerInteractions() {
40
40
  const lastItem = lastItemRef.current;
41
41
  if (lastItem) {
42
42
  lastItemRef.current = undefined;
43
+ instance.clearHoveredItem(lastItem);
43
44
  instance.removeTooltipItem(lastItem);
44
45
  instance.clearHighlight();
45
46
  }
@@ -66,6 +67,7 @@ function useRegisterPointerInteractions() {
66
67
  }
67
68
  if (item) {
68
69
  instance.setLastUpdateSource('pointer');
70
+ instance.setHoveredItem(item);
69
71
  instance.setTooltipItem(item);
70
72
  instance.setHighlight(item);
71
73
  lastItemRef.current = item;
@@ -73,10 +75,18 @@ function useRegisterPointerInteractions() {
73
75
  reset();
74
76
  }
75
77
  };
78
+
79
+ // Touch has no hover phase, so a tap may never produce a `pointermove`. Resolving on
80
+ // `pointerdown` too is what makes the pointer item known for a stationary tap.
81
+ const onPointerDown = function onPointerDown(event) {
82
+ onPointerMove(event);
83
+ };
84
+ element.addEventListener('pointerdown', onPointerDown);
76
85
  element.addEventListener('pointerleave', onPointerLeave);
77
86
  element.addEventListener('pointermove', onPointerMove);
78
87
  element.addEventListener('pointerenter', onPointerEnter);
79
88
  return () => {
89
+ element.removeEventListener('pointerdown', onPointerDown);
80
90
  element.removeEventListener('pointerenter', onPointerEnter);
81
91
  element.removeEventListener('pointermove', onPointerMove);
82
92
  element.removeEventListener('pointerleave', onPointerLeave);
@@ -34,6 +34,7 @@ export function useRegisterPointerInteractions() {
34
34
  const lastItem = lastItemRef.current;
35
35
  if (lastItem) {
36
36
  lastItemRef.current = undefined;
37
+ instance.clearHoveredItem(lastItem);
37
38
  instance.removeTooltipItem(lastItem);
38
39
  instance.clearHighlight();
39
40
  }
@@ -60,6 +61,7 @@ export function useRegisterPointerInteractions() {
60
61
  }
61
62
  if (item) {
62
63
  instance.setLastUpdateSource('pointer');
64
+ instance.setHoveredItem(item);
63
65
  instance.setTooltipItem(item);
64
66
  instance.setHighlight(item);
65
67
  lastItemRef.current = item;
@@ -67,10 +69,18 @@ export function useRegisterPointerInteractions() {
67
69
  reset();
68
70
  }
69
71
  };
72
+
73
+ // Touch has no hover phase, so a tap may never produce a `pointermove`. Resolving on
74
+ // `pointerdown` too is what makes the pointer item known for a stationary tap.
75
+ const onPointerDown = function onPointerDown(event) {
76
+ onPointerMove(event);
77
+ };
78
+ element.addEventListener('pointerdown', onPointerDown);
70
79
  element.addEventListener('pointerleave', onPointerLeave);
71
80
  element.addEventListener('pointermove', onPointerMove);
72
81
  element.addEventListener('pointerenter', onPointerEnter);
73
82
  return () => {
83
+ element.removeEventListener('pointerdown', onPointerDown);
74
84
  element.removeEventListener('pointerenter', onPointerEnter);
75
85
  element.removeEventListener('pointermove', onPointerMove);
76
86
  element.removeEventListener('pointerleave', onPointerLeave);
@@ -144,6 +144,7 @@ const useChartClosestPoint = ({
144
144
  const moveEndHandler = instance.addInteractionListener('moveEnd', event => {
145
145
  if (!event.detail.activeGestures.pan) {
146
146
  instance.cleanInteraction?.();
147
+ instance.clearHoveredItem?.();
147
148
  instance.clearHighlight?.();
148
149
  instance.removeTooltipItem?.();
149
150
  }
@@ -151,6 +152,7 @@ const useChartClosestPoint = ({
151
152
  const panEndHandler = instance.addInteractionListener('panEnd', event => {
152
153
  if (!event.detail.activeGestures.move) {
153
154
  instance.cleanInteraction?.();
155
+ instance.clearHoveredItem?.();
154
156
  instance.clearHighlight?.();
155
157
  instance.removeTooltipItem?.();
156
158
  }
@@ -158,6 +160,7 @@ const useChartClosestPoint = ({
158
160
  const pressEndHandler = instance.addInteractionListener('quickPressEnd', event => {
159
161
  if (!event.detail.activeGestures.move && !event.detail.activeGestures.pan) {
160
162
  instance.cleanInteraction?.();
163
+ instance.clearHoveredItem?.();
161
164
  instance.clearHighlight?.();
162
165
  instance.removeTooltipItem?.();
163
166
  }
@@ -166,12 +169,14 @@ const useChartClosestPoint = ({
166
169
  const closestPoint = getClosestPoint(event.detail.srcEvent);
167
170
  if (closestPoint === 'outside-chart') {
168
171
  instance.cleanInteraction?.();
172
+ instance.clearHoveredItem?.();
169
173
  instance.clearHighlight?.();
170
174
  instance.removeTooltipItem?.();
171
175
  return;
172
176
  }
173
177
  if (closestPoint === 'outside-voronoi-max-radius' || closestPoint === 'no-point-found') {
174
178
  instance.removeTooltipItem?.();
179
+ instance.clearHoveredItem?.();
175
180
  instance.clearHighlight?.();
176
181
  instance.removeTooltipItem?.();
177
182
  return;
@@ -180,6 +185,11 @@ const useChartClosestPoint = ({
180
185
  seriesId,
181
186
  dataIndex
182
187
  } = closestPoint;
188
+ instance.setHoveredItem?.({
189
+ type: 'scatter',
190
+ seriesId,
191
+ dataIndex
192
+ });
183
193
  instance.setTooltipItem?.({
184
194
  type: 'scatter',
185
195
  seriesId,
@@ -192,6 +202,17 @@ const useChartClosestPoint = ({
192
202
  dataIndex
193
203
  });
194
204
  };
205
+
206
+ // A tap resolves nothing without a preceding move, and touch has no hover phase, so the
207
+ // pointer item is also resolved on `pointerdown`.
208
+ const onPointerDown = function onPointerDown(event) {
209
+ gestureHandler({
210
+ detail: {
211
+ srcEvent: event
212
+ }
213
+ });
214
+ };
215
+ element.addEventListener('pointerdown', onPointerDown);
195
216
  const tapHandler = instance.addInteractionListener('tap', event => {
196
217
  const closestPoint = getClosestPoint(event.detail.srcEvent);
197
218
  if (typeof closestPoint !== 'string' && onItemClick) {
@@ -210,6 +231,7 @@ const useChartClosestPoint = ({
210
231
  const panHandler = instance.addInteractionListener('pan', gestureHandler);
211
232
  const pressHandler = instance.addInteractionListener('quickPress', gestureHandler);
212
233
  return () => {
234
+ element.removeEventListener('pointerdown', onPointerDown);
213
235
  tapHandler.cleanup();
214
236
  moveHandler.cleanup();
215
237
  moveEndHandler.cleanup();
@@ -137,6 +137,7 @@ export const useChartClosestPoint = ({
137
137
  const moveEndHandler = instance.addInteractionListener('moveEnd', event => {
138
138
  if (!event.detail.activeGestures.pan) {
139
139
  instance.cleanInteraction?.();
140
+ instance.clearHoveredItem?.();
140
141
  instance.clearHighlight?.();
141
142
  instance.removeTooltipItem?.();
142
143
  }
@@ -144,6 +145,7 @@ export const useChartClosestPoint = ({
144
145
  const panEndHandler = instance.addInteractionListener('panEnd', event => {
145
146
  if (!event.detail.activeGestures.move) {
146
147
  instance.cleanInteraction?.();
148
+ instance.clearHoveredItem?.();
147
149
  instance.clearHighlight?.();
148
150
  instance.removeTooltipItem?.();
149
151
  }
@@ -151,6 +153,7 @@ export const useChartClosestPoint = ({
151
153
  const pressEndHandler = instance.addInteractionListener('quickPressEnd', event => {
152
154
  if (!event.detail.activeGestures.move && !event.detail.activeGestures.pan) {
153
155
  instance.cleanInteraction?.();
156
+ instance.clearHoveredItem?.();
154
157
  instance.clearHighlight?.();
155
158
  instance.removeTooltipItem?.();
156
159
  }
@@ -159,12 +162,14 @@ export const useChartClosestPoint = ({
159
162
  const closestPoint = getClosestPoint(event.detail.srcEvent);
160
163
  if (closestPoint === 'outside-chart') {
161
164
  instance.cleanInteraction?.();
165
+ instance.clearHoveredItem?.();
162
166
  instance.clearHighlight?.();
163
167
  instance.removeTooltipItem?.();
164
168
  return;
165
169
  }
166
170
  if (closestPoint === 'outside-voronoi-max-radius' || closestPoint === 'no-point-found') {
167
171
  instance.removeTooltipItem?.();
172
+ instance.clearHoveredItem?.();
168
173
  instance.clearHighlight?.();
169
174
  instance.removeTooltipItem?.();
170
175
  return;
@@ -173,6 +178,11 @@ export const useChartClosestPoint = ({
173
178
  seriesId,
174
179
  dataIndex
175
180
  } = closestPoint;
181
+ instance.setHoveredItem?.({
182
+ type: 'scatter',
183
+ seriesId,
184
+ dataIndex
185
+ });
176
186
  instance.setTooltipItem?.({
177
187
  type: 'scatter',
178
188
  seriesId,
@@ -185,6 +195,17 @@ export const useChartClosestPoint = ({
185
195
  dataIndex
186
196
  });
187
197
  };
198
+
199
+ // A tap resolves nothing without a preceding move, and touch has no hover phase, so the
200
+ // pointer item is also resolved on `pointerdown`.
201
+ const onPointerDown = function onPointerDown(event) {
202
+ gestureHandler({
203
+ detail: {
204
+ srcEvent: event
205
+ }
206
+ });
207
+ };
208
+ element.addEventListener('pointerdown', onPointerDown);
188
209
  const tapHandler = instance.addInteractionListener('tap', event => {
189
210
  const closestPoint = getClosestPoint(event.detail.srcEvent);
190
211
  if (typeof closestPoint !== 'string' && onItemClick) {
@@ -203,6 +224,7 @@ export const useChartClosestPoint = ({
203
224
  const panHandler = instance.addInteractionListener('pan', gestureHandler);
204
225
  const pressHandler = instance.addInteractionListener('quickPress', gestureHandler);
205
226
  return () => {
227
+ element.removeEventListener('pointerdown', onPointerDown);
206
228
  tapHandler.cleanup();
207
229
  moveHandler.cleanup();
208
230
  moveEndHandler.cleanup();
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.useChartInteraction = void 0;
8
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
9
  var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
10
+ var _fastObjectShallowCompare = require("@mui/x-internals/fastObjectShallowCompare");
10
11
  const useChartInteraction = ({
11
12
  store
12
13
  }) => {
@@ -24,6 +25,28 @@ const useChartInteraction = ({
24
25
  }));
25
26
  }
26
27
  });
28
+ const setHoveredItem = (0, _useEventCallback.default)(function setHoveredItem(item) {
29
+ const previousItem = store.state.interaction.hoveredItem;
30
+ if (previousItem !== null && (0, _fastObjectShallowCompare.fastObjectShallowCompare)(previousItem, item)) {
31
+ return;
32
+ }
33
+ store.set('interaction', (0, _extends2.default)({}, store.state.interaction, {
34
+ hoveredItem: item
35
+ }));
36
+ });
37
+ const clearHoveredItem = (0, _useEventCallback.default)(function clearHoveredItem(itemToRemove) {
38
+ const previousItem = store.state.interaction.hoveredItem;
39
+ if (previousItem === null) {
40
+ return;
41
+ }
42
+ if (itemToRemove !== undefined && !(0, _fastObjectShallowCompare.fastObjectShallowCompare)(previousItem, itemToRemove)) {
43
+ // Another item took over already.
44
+ return;
45
+ }
46
+ store.set('interaction', (0, _extends2.default)({}, store.state.interaction, {
47
+ hoveredItem: null
48
+ }));
49
+ });
27
50
  const setPointerCoordinate = (0, _useEventCallback.default)(function setPointerCoordinate(coordinate) {
28
51
  store.set('interaction', (0, _extends2.default)({}, store.state.interaction, {
29
52
  pointer: coordinate,
@@ -44,6 +67,8 @@ const useChartInteraction = ({
44
67
  instance: {
45
68
  cleanInteraction,
46
69
  setLastUpdateSource,
70
+ setHoveredItem,
71
+ clearHoveredItem,
47
72
  setPointerCoordinate,
48
73
  handlePointerEnter,
49
74
  handlePointerLeave
@@ -56,7 +81,8 @@ useChartInteraction.getInitialState = () => ({
56
81
  item: null,
57
82
  pointer: null,
58
83
  lastUpdate: 'pointer',
59
- pointerType: null
84
+ pointerType: null,
85
+ hoveredItem: null
60
86
  }
61
87
  });
62
88
  useChartInteraction.params = {};