@mui/x-charts-pro 9.0.0-beta.0 → 9.0.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 (56) hide show
  1. package/BarChartPro/BarChartPro.js +18 -18
  2. package/BarChartPro/BarChartPro.mjs +18 -18
  3. package/CHANGELOG.md +267 -1
  4. package/ChartsDataProviderPro/ChartsDataProviderPro.js +2 -2
  5. package/ChartsDataProviderPro/ChartsDataProviderPro.mjs +2 -2
  6. package/ChartsZoomSlider/internals/ChartsAxisZoomSlider.js +59 -8
  7. package/ChartsZoomSlider/internals/ChartsAxisZoomSlider.mjs +60 -9
  8. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderActiveTrack.d.mts +5 -1
  9. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderActiveTrack.d.ts +5 -1
  10. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderActiveTrack.js +66 -22
  11. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderActiveTrack.mjs +69 -23
  12. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreview.d.mts +6 -1
  13. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreview.d.ts +6 -1
  14. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreview.js +6 -4
  15. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreview.mjs +6 -4
  16. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreviewContent.d.mts +5 -1
  17. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderPreviewContent.d.ts +5 -1
  18. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderThumb.d.mts +4 -1
  19. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderThumb.d.ts +4 -1
  20. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderThumb.js +93 -47
  21. package/ChartsZoomSlider/internals/ChartsAxisZoomSliderThumb.mjs +94 -48
  22. package/ChartsZoomSlider/internals/constants.d.mts +5 -0
  23. package/ChartsZoomSlider/internals/constants.d.ts +5 -0
  24. package/ChartsZoomSlider/internals/constants.js +6 -1
  25. package/ChartsZoomSlider/internals/constants.mjs +5 -0
  26. package/ChartsZoomSlider/internals/previews/AreaPreviewPlot.d.mts +3 -2
  27. package/ChartsZoomSlider/internals/previews/AreaPreviewPlot.d.ts +3 -2
  28. package/ChartsZoomSlider/internals/previews/AreaPreviewPlot.js +4 -2
  29. package/ChartsZoomSlider/internals/previews/AreaPreviewPlot.mjs +4 -2
  30. package/ChartsZoomSlider/internals/previews/BarPreviewPlot.js +14 -9
  31. package/ChartsZoomSlider/internals/previews/BarPreviewPlot.mjs +13 -9
  32. package/ChartsZoomSlider/internals/previews/LineAreaPreviewPlot.d.mts +2 -1
  33. package/ChartsZoomSlider/internals/previews/LineAreaPreviewPlot.d.ts +2 -1
  34. package/ChartsZoomSlider/internals/previews/LineAreaPreviewPlot.js +6 -3
  35. package/ChartsZoomSlider/internals/previews/LineAreaPreviewPlot.mjs +6 -3
  36. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.d.mts +3 -2
  37. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.d.ts +3 -2
  38. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.js +6 -1
  39. package/ChartsZoomSlider/internals/previews/LinePreviewPlot.mjs +6 -1
  40. package/ChartsZoomSlider/internals/previews/PreviewPlot.types.d.mts +5 -1
  41. package/ChartsZoomSlider/internals/previews/PreviewPlot.types.d.ts +5 -1
  42. package/ChartsZoomSlider/internals/previews/ScatterPreviewPlot.d.mts +2 -1
  43. package/ChartsZoomSlider/internals/previews/ScatterPreviewPlot.d.ts +2 -1
  44. package/ChartsZoomSlider/internals/previews/ScatterPreviewPlot.js +7 -1
  45. package/ChartsZoomSlider/internals/previews/ScatterPreviewPlot.mjs +7 -1
  46. package/Heatmap/Heatmap.js +2 -2
  47. package/Heatmap/Heatmap.mjs +2 -2
  48. package/LineChartPro/LineChartPro.js +18 -18
  49. package/LineChartPro/LineChartPro.mjs +18 -18
  50. package/ScatterChartPro/ScatterChartPro.js +18 -18
  51. package/ScatterChartPro/ScatterChartPro.mjs +18 -18
  52. package/index.d.mts +1 -0
  53. package/index.d.ts +1 -0
  54. package/index.js +13 -1
  55. package/index.mjs +2 -1
  56. package/package.json +121 -121
@@ -34,7 +34,57 @@ function ChartsAxisZoomSlider({
34
34
  const {
35
35
  yAxis
36
36
  } = (0, _hooks.useYAxes)();
37
- const showPreview = zoomOptions.slider.preview;
37
+ const previewOption = zoomOptions.slider.preview;
38
+ const showPreview = !!previewOption;
39
+ const previewSeriesIds = typeof previewOption === 'object' ? previewOption.seriesIds : undefined;
40
+ const sliderRef = React.useRef(null);
41
+ const layerContainerRef = (0, _hooks.useChartsLayerContainerRef)();
42
+ const isDraggingRef = React.useRef(false);
43
+
44
+ // Prevent scrolling on touch devices when interacting with the zoom slider.
45
+ // Listeners are attached to the parent `<svg>` element because calling
46
+ // `preventDefault` on SVG child elements does not reliably block scrolling —
47
+ // the browser's compositor decides based on the `<svg>` HTML element.
48
+ React.useEffect(() => {
49
+ const slider = sliderRef.current;
50
+ const layerContainer = layerContainerRef.current;
51
+ if (!slider || !layerContainer) {
52
+ return undefined;
53
+ }
54
+ function preventTouchDefault(event) {
55
+ if (slider && slider.contains(event.target)) {
56
+ event.preventDefault();
57
+ }
58
+ }
59
+ layerContainer.addEventListener('touchstart', preventTouchDefault, {
60
+ passive: false
61
+ });
62
+ layerContainer.addEventListener('touchmove', preventTouchDefault, {
63
+ passive: false
64
+ });
65
+ return () => {
66
+ layerContainer.removeEventListener('touchstart', preventTouchDefault);
67
+ layerContainer.removeEventListener('touchmove', preventTouchDefault);
68
+ };
69
+ }, [layerContainerRef]);
70
+ const tooltipOn = React.useCallback(() => {
71
+ setShowTooltip(true);
72
+ }, []);
73
+ const tooltipOff = React.useCallback(() => {
74
+ // Don't hide tooltip while dragging — pointerleave fires when the pointer
75
+ // moves away from the element during drag, but we want to keep the tooltip visible.
76
+ if (!isDraggingRef.current) {
77
+ setShowTooltip(false);
78
+ }
79
+ }, []);
80
+ const interactionStart = React.useCallback(() => {
81
+ isDraggingRef.current = true;
82
+ setShowTooltip(true);
83
+ }, []);
84
+ const interactionEnd = React.useCallback(() => {
85
+ isDraggingRef.current = false;
86
+ setShowTooltip(false);
87
+ }, []);
38
88
  if (!zoomData) {
39
89
  return null;
40
90
  }
@@ -72,6 +122,7 @@ function ChartsAxisZoomSlider({
72
122
  axisId: axisId,
73
123
  axisDirection: axisDirection,
74
124
  reverse: reverse,
125
+ seriesIds: previewSeriesIds,
75
126
  x: 0,
76
127
  y: 0,
77
128
  height: axisDirection === 'x' ? _internals.ZOOM_SLIDER_PREVIEW_SIZE : drawingArea.height,
@@ -86,15 +137,13 @@ function ChartsAxisZoomSlider({
86
137
  axisId: axisId,
87
138
  axisDirection: axisDirection,
88
139
  reverse: reverse,
89
- onSelectStart: tooltipConditions === 'hover' ? () => setShowTooltip(true) : undefined,
90
- onSelectEnd: tooltipConditions === 'hover' ? () => setShowTooltip(false) : undefined
140
+ onSelectStart: tooltipConditions === 'hover' ? interactionStart : undefined,
141
+ onSelectEnd: tooltipConditions === 'hover' ? interactionEnd : undefined
91
142
  });
92
143
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("g", {
144
+ ref: sliderRef,
93
145
  "data-charts-zoom-slider": true,
94
146
  transform: `translate(${x} ${y})`,
95
- style: {
96
- touchAction: 'none'
97
- },
98
147
  children: [track, /*#__PURE__*/(0, _jsxRuntime.jsx)(_ChartsAxisZoomSliderActiveTrack.ChartsAxisZoomSliderActiveTrack, {
99
148
  zoomData: zoomData,
100
149
  axisId: axisId,
@@ -104,8 +153,10 @@ function ChartsAxisZoomSlider({
104
153
  showTooltip: showTooltip && tooltipConditions !== 'never' || tooltipConditions === 'always',
105
154
  size: showPreview ? _internals.ZOOM_SLIDER_PREVIEW_SIZE : _constants.ZOOM_SLIDER_ACTIVE_TRACK_SIZE,
106
155
  preview: showPreview,
107
- onPointerEnter: tooltipConditions === 'hover' ? () => setShowTooltip(true) : undefined,
108
- onPointerLeave: tooltipConditions === 'hover' ? () => setShowTooltip(false) : undefined
156
+ onPointerEnter: tooltipConditions === 'hover' ? tooltipOn : undefined,
157
+ onPointerLeave: tooltipConditions === 'hover' ? tooltipOff : undefined,
158
+ onInteractionStart: tooltipConditions === 'hover' ? interactionStart : undefined,
159
+ onInteractionEnd: tooltipConditions === 'hover' ? interactionEnd : undefined
109
160
  })]
110
161
  });
111
162
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as React from 'react';
4
4
  import { DEFAULT_ZOOM_SLIDER_SHOW_TOOLTIP, selectorChartAxisZoomOptionsLookup, useDrawingArea, useStore, ZOOM_SLIDER_MARGIN, ZOOM_SLIDER_PREVIEW_SIZE } from '@mui/x-charts/internals';
5
- import { useXAxes, useYAxes } from '@mui/x-charts/hooks';
5
+ import { useChartsLayerContainerRef, useXAxes, useYAxes } from '@mui/x-charts/hooks';
6
6
  import { ChartsAxisZoomSliderPreview } from "./ChartsAxisZoomSliderPreview.mjs";
7
7
  import { ZOOM_SLIDER_ACTIVE_TRACK_SIZE, ZOOM_SLIDER_SIZE, ZOOM_SLIDER_TRACK_SIZE } from "./constants.mjs";
8
8
  import { selectorChartAxisZoomData } from "../../internals/plugins/useChartProZoom/index.mjs";
@@ -28,7 +28,57 @@ export function ChartsAxisZoomSlider({
28
28
  const {
29
29
  yAxis
30
30
  } = useYAxes();
31
- const showPreview = zoomOptions.slider.preview;
31
+ const previewOption = zoomOptions.slider.preview;
32
+ const showPreview = !!previewOption;
33
+ const previewSeriesIds = typeof previewOption === 'object' ? previewOption.seriesIds : undefined;
34
+ const sliderRef = React.useRef(null);
35
+ const layerContainerRef = useChartsLayerContainerRef();
36
+ const isDraggingRef = React.useRef(false);
37
+
38
+ // Prevent scrolling on touch devices when interacting with the zoom slider.
39
+ // Listeners are attached to the parent `<svg>` element because calling
40
+ // `preventDefault` on SVG child elements does not reliably block scrolling —
41
+ // the browser's compositor decides based on the `<svg>` HTML element.
42
+ React.useEffect(() => {
43
+ const slider = sliderRef.current;
44
+ const layerContainer = layerContainerRef.current;
45
+ if (!slider || !layerContainer) {
46
+ return undefined;
47
+ }
48
+ function preventTouchDefault(event) {
49
+ if (slider && slider.contains(event.target)) {
50
+ event.preventDefault();
51
+ }
52
+ }
53
+ layerContainer.addEventListener('touchstart', preventTouchDefault, {
54
+ passive: false
55
+ });
56
+ layerContainer.addEventListener('touchmove', preventTouchDefault, {
57
+ passive: false
58
+ });
59
+ return () => {
60
+ layerContainer.removeEventListener('touchstart', preventTouchDefault);
61
+ layerContainer.removeEventListener('touchmove', preventTouchDefault);
62
+ };
63
+ }, [layerContainerRef]);
64
+ const tooltipOn = React.useCallback(() => {
65
+ setShowTooltip(true);
66
+ }, []);
67
+ const tooltipOff = React.useCallback(() => {
68
+ // Don't hide tooltip while dragging — pointerleave fires when the pointer
69
+ // moves away from the element during drag, but we want to keep the tooltip visible.
70
+ if (!isDraggingRef.current) {
71
+ setShowTooltip(false);
72
+ }
73
+ }, []);
74
+ const interactionStart = React.useCallback(() => {
75
+ isDraggingRef.current = true;
76
+ setShowTooltip(true);
77
+ }, []);
78
+ const interactionEnd = React.useCallback(() => {
79
+ isDraggingRef.current = false;
80
+ setShowTooltip(false);
81
+ }, []);
32
82
  if (!zoomData) {
33
83
  return null;
34
84
  }
@@ -66,6 +116,7 @@ export function ChartsAxisZoomSlider({
66
116
  axisId: axisId,
67
117
  axisDirection: axisDirection,
68
118
  reverse: reverse,
119
+ seriesIds: previewSeriesIds,
69
120
  x: 0,
70
121
  y: 0,
71
122
  height: axisDirection === 'x' ? ZOOM_SLIDER_PREVIEW_SIZE : drawingArea.height,
@@ -80,15 +131,13 @@ export function ChartsAxisZoomSlider({
80
131
  axisId: axisId,
81
132
  axisDirection: axisDirection,
82
133
  reverse: reverse,
83
- onSelectStart: tooltipConditions === 'hover' ? () => setShowTooltip(true) : undefined,
84
- onSelectEnd: tooltipConditions === 'hover' ? () => setShowTooltip(false) : undefined
134
+ onSelectStart: tooltipConditions === 'hover' ? interactionStart : undefined,
135
+ onSelectEnd: tooltipConditions === 'hover' ? interactionEnd : undefined
85
136
  });
86
137
  return /*#__PURE__*/_jsxs("g", {
138
+ ref: sliderRef,
87
139
  "data-charts-zoom-slider": true,
88
140
  transform: `translate(${x} ${y})`,
89
- style: {
90
- touchAction: 'none'
91
- },
92
141
  children: [track, /*#__PURE__*/_jsx(ChartsAxisZoomSliderActiveTrack, {
93
142
  zoomData: zoomData,
94
143
  axisId: axisId,
@@ -98,8 +147,10 @@ export function ChartsAxisZoomSlider({
98
147
  showTooltip: showTooltip && tooltipConditions !== 'never' || tooltipConditions === 'always',
99
148
  size: showPreview ? ZOOM_SLIDER_PREVIEW_SIZE : ZOOM_SLIDER_ACTIVE_TRACK_SIZE,
100
149
  preview: showPreview,
101
- onPointerEnter: tooltipConditions === 'hover' ? () => setShowTooltip(true) : undefined,
102
- onPointerLeave: tooltipConditions === 'hover' ? () => setShowTooltip(false) : undefined
150
+ onPointerEnter: tooltipConditions === 'hover' ? tooltipOn : undefined,
151
+ onPointerLeave: tooltipConditions === 'hover' ? tooltipOff : undefined,
152
+ onInteractionStart: tooltipConditions === 'hover' ? interactionStart : undefined,
153
+ onInteractionEnd: tooltipConditions === 'hover' ? interactionEnd : undefined
103
154
  })]
104
155
  });
105
156
  }
@@ -10,6 +10,8 @@ export interface ChartsAxisZoomSliderActiveTrackProps {
10
10
  showTooltip: boolean;
11
11
  onPointerEnter?: () => void;
12
12
  onPointerLeave?: () => void;
13
+ onInteractionStart?: () => void;
14
+ onInteractionEnd?: () => void;
13
15
  }
14
16
  export declare function ChartsAxisZoomSliderActiveTrack({
15
17
  axisId,
@@ -21,5 +23,7 @@ export declare function ChartsAxisZoomSliderActiveTrack({
21
23
  reverse,
22
24
  showTooltip,
23
25
  onPointerEnter,
24
- onPointerLeave
26
+ onPointerLeave,
27
+ onInteractionStart,
28
+ onInteractionEnd
25
29
  }: ChartsAxisZoomSliderActiveTrackProps): import("react/jsx-runtime").JSX.Element;
@@ -10,6 +10,8 @@ export interface ChartsAxisZoomSliderActiveTrackProps {
10
10
  showTooltip: boolean;
11
11
  onPointerEnter?: () => void;
12
12
  onPointerLeave?: () => void;
13
+ onInteractionStart?: () => void;
14
+ onInteractionEnd?: () => void;
13
15
  }
14
16
  export declare function ChartsAxisZoomSliderActiveTrack({
15
17
  axisId,
@@ -21,5 +23,7 @@ export declare function ChartsAxisZoomSliderActiveTrack({
21
23
  reverse,
22
24
  showTooltip,
23
25
  onPointerEnter,
24
- onPointerLeave
26
+ onPointerLeave,
27
+ onInteractionStart,
28
+ onInteractionEnd
25
29
  }: ChartsAxisZoomSliderActiveTrackProps): import("react/jsx-runtime").JSX.Element;
@@ -8,9 +8,9 @@ Object.defineProperty(exports, "__esModule", {
8
8
  });
9
9
  exports.ChartsAxisZoomSliderActiveTrack = ChartsAxisZoomSliderActiveTrack;
10
10
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
+ var React = _interopRequireWildcard(require("react"));
11
12
  var _styles = require("@mui/material/styles");
12
13
  var _internals = require("@mui/x-charts/internals");
13
- var React = _interopRequireWildcard(require("react"));
14
14
  var _rafThrottle = require("@mui/x-internals/rafThrottle");
15
15
  var _system = require("@mui/system");
16
16
  var _useChartProZoom = require("../../internals/plugins/useChartProZoom");
@@ -20,6 +20,15 @@ var _zoomUtils = require("./zoom-utils");
20
20
  var _constants = require("./constants");
21
21
  var _chartsAxisZoomSliderTrackClasses = require("./chartsAxisZoomSliderTrackClasses");
22
22
  var _jsxRuntime = require("react/jsx-runtime");
23
+ /**
24
+ * Invisible touch target that is only active on coarse pointer devices (touch).
25
+ * On fine pointer devices (mouse), it disables pointer events so it doesn't
26
+ * interfere with precise interactions on small zoom ranges.
27
+ */const TouchTarget = (0, _styles.styled)('rect')({
28
+ '@media (pointer: fine)': {
29
+ pointerEvents: 'none'
30
+ }
31
+ });
23
32
  const ZoomSliderActiveTrackRect = (0, _styles.styled)('rect', {
24
33
  slot: 'internal',
25
34
  shouldForwardProp: prop => (0, _system.shouldForwardProp)(prop) && prop !== 'preview'
@@ -42,7 +51,7 @@ const ZoomSliderActiveTrackRect = (0, _styles.styled)('rect', {
42
51
  }), {
43
52
  rx: 4,
44
53
  ry: 4,
45
- stroke: theme.palette.grey[500]
54
+ stroke: (theme.vars || theme).palette.grey[500]
46
55
  })
47
56
  }]
48
57
  }));
@@ -56,7 +65,9 @@ function ChartsAxisZoomSliderActiveTrack({
56
65
  reverse,
57
66
  showTooltip,
58
67
  onPointerEnter,
59
- onPointerLeave
68
+ onPointerLeave,
69
+ onInteractionStart,
70
+ onInteractionEnd
60
71
  }) {
61
72
  const {
62
73
  instance
@@ -67,7 +78,8 @@ function ChartsAxisZoomSliderActiveTrack({
67
78
  const store = (0, _internals.useStore)();
68
79
  const axis = store.use(_internals.selectorChartAxis, axisId);
69
80
  const drawingArea = (0, _internals.useDrawingArea)();
70
- const activePreviewRectRef = React.useRef(null);
81
+ const activeTrackGroupRef = React.useRef(null);
82
+ const touchTargetRef = React.useRef(null);
71
83
  const [startThumbEl, setStartThumbEl] = React.useState(null);
72
84
  const [endThumbEl, setEndThumbEl] = React.useState(null);
73
85
  const {
@@ -80,8 +92,9 @@ function ChartsAxisZoomSliderActiveTrack({
80
92
  const previewThumbWidth = axisDirection === 'x' ? _constants.ZOOM_SLIDER_THUMB_WIDTH : _constants.ZOOM_SLIDER_THUMB_HEIGHT;
81
93
  const previewThumbHeight = axisDirection === 'x' ? _constants.ZOOM_SLIDER_THUMB_HEIGHT : _constants.ZOOM_SLIDER_THUMB_WIDTH;
82
94
  React.useEffect(() => {
83
- const activePreviewRect = activePreviewRectRef.current;
84
- if (!activePreviewRect) {
95
+ const group = activeTrackGroupRef.current;
96
+ const touchTarget = touchTargetRef.current;
97
+ if (!group || !touchTarget) {
85
98
  return;
86
99
  }
87
100
  let prevPointerZoom = 0;
@@ -100,13 +113,17 @@ function ChartsAxisZoomSliderActiveTrack({
100
113
  instance.moveZoomRange(axisId, deltaZoom);
101
114
  });
102
115
  const onPointerUp = () => {
103
- activePreviewRect.removeEventListener('pointermove', onPointerMove);
116
+ group.removeEventListener('pointermove', onPointerMove);
104
117
  document.removeEventListener('pointerup', onPointerUp);
118
+ onInteractionEnd?.();
105
119
  };
106
120
  const onPointerDown = event => {
107
121
  // Prevent text selection when dragging
108
122
  event.preventDefault();
109
- activePreviewRect.setPointerCapture(event.pointerId);
123
+ // Use the touch target rect for pointer capture as setPointerCapture
124
+ // does not work reliably on SVG <g> elements on touch devices.
125
+ touchTarget.setPointerCapture(event.pointerId);
126
+ onInteractionStart?.();
110
127
  const axisZoomData = (0, _useChartProZoom.selectorChartAxisZoomData)(store.state, axisId);
111
128
  const element = chartsLayerContainerRef.current;
112
129
  if (!axisZoomData || !element) {
@@ -119,16 +136,18 @@ function ChartsAxisZoomSliderActiveTrack({
119
136
  }
120
137
  prevPointerZoom = pointerDownZoom;
121
138
  document.addEventListener('pointerup', onPointerUp);
122
- activePreviewRect.addEventListener('pointermove', onPointerMove);
139
+ group.addEventListener('pointermove', onPointerMove);
123
140
  };
124
- activePreviewRect.addEventListener('pointerdown', onPointerDown);
141
+
142
+ // Listen on the group so events from both the visible rect and touch target are captured
143
+ group.addEventListener('pointerdown', onPointerDown);
125
144
 
126
145
  // eslint-disable-next-line consistent-return
127
146
  return () => {
128
- activePreviewRect.removeEventListener('pointerdown', onPointerDown);
147
+ group.removeEventListener('pointerdown', onPointerDown);
129
148
  onPointerMove.clear();
130
149
  };
131
- }, [axisDirection, axisId, instance, reverse, store, chartsLayerContainerRef]);
150
+ }, [axisDirection, axisId, instance, reverse, store, chartsLayerContainerRef, onInteractionStart, onInteractionEnd]);
132
151
  const onStartThumbMove = event => {
133
152
  const element = chartsLayerContainerRef.current;
134
153
  if (!element) {
@@ -222,17 +241,38 @@ function ChartsAxisZoomSliderActiveTrack({
222
241
  endThumbY -= previewThumbHeight / 2;
223
242
  }
224
243
  const previewOffset = _constants.ZOOM_SLIDER_THUMB_HEIGHT > size ? (_constants.ZOOM_SLIDER_THUMB_HEIGHT - size) / 2 : 0;
244
+ const activeTrackX = previewX + (axisDirection === 'x' ? 0 : previewOffset);
245
+ const activeTrackY = previewY + (axisDirection === 'x' ? previewOffset : 0);
246
+
247
+ // Compute a larger invisible touch target centered on the visible active track
248
+ const touchWidth = axisDirection === 'y' ? Math.max(previewWidth, _constants.ZOOM_SLIDER_TOUCH_TARGET) : previewWidth;
249
+ const touchHeight = axisDirection === 'x' ? Math.max(previewHeight, _constants.ZOOM_SLIDER_TOUCH_TARGET) : previewHeight;
250
+ const touchX = activeTrackX - (touchWidth - previewWidth) / 2;
251
+ const touchY = activeTrackY - (touchHeight - previewHeight) / 2;
225
252
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
226
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ZoomSliderActiveTrackRect, {
227
- ref: activePreviewRectRef,
228
- x: previewX + (axisDirection === 'x' ? 0 : previewOffset),
229
- y: previewY + (axisDirection === 'x' ? previewOffset : 0),
230
- preview: preview,
231
- width: previewWidth,
232
- height: previewHeight,
233
- onPointerEnter: onPointerEnter,
234
- onPointerLeave: onPointerLeave,
235
- className: classes.active
253
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("g", {
254
+ ref: activeTrackGroupRef,
255
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(ZoomSliderActiveTrackRect, {
256
+ x: activeTrackX,
257
+ y: activeTrackY,
258
+ preview: preview,
259
+ width: previewWidth,
260
+ height: previewHeight,
261
+ onPointerEnter: onPointerEnter,
262
+ onPointerLeave: onPointerLeave,
263
+ className: classes.active
264
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(TouchTarget, {
265
+ ref: touchTargetRef,
266
+ x: touchX,
267
+ y: touchY,
268
+ width: touchWidth,
269
+ height: touchHeight,
270
+ fill: "transparent",
271
+ stroke: "none",
272
+ cursor: "grab",
273
+ onPointerEnter: onPointerEnter,
274
+ onPointerLeave: onPointerLeave
275
+ })]
236
276
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ChartsAxisZoomSliderThumb.ChartsAxisZoomSliderThumb, {
237
277
  ref: setStartThumbEl,
238
278
  x: startThumbX,
@@ -243,6 +283,8 @@ function ChartsAxisZoomSliderActiveTrack({
243
283
  onMove: onStartThumbMove,
244
284
  onPointerEnter: onPointerEnter,
245
285
  onPointerLeave: onPointerLeave,
286
+ onInteractionStart: onInteractionStart,
287
+ onInteractionEnd: onInteractionEnd,
246
288
  placement: "start"
247
289
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ChartsAxisZoomSliderThumb.ChartsAxisZoomSliderThumb, {
248
290
  ref: setEndThumbEl,
@@ -254,6 +296,8 @@ function ChartsAxisZoomSliderActiveTrack({
254
296
  onMove: onEndThumbMove,
255
297
  onPointerEnter: onPointerEnter,
256
298
  onPointerLeave: onPointerLeave,
299
+ onInteractionStart: onInteractionStart,
300
+ onInteractionEnd: onInteractionEnd,
257
301
  placement: "end"
258
302
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ChartsTooltipZoomSliderValue.ChartsTooltipZoomSliderValue, {
259
303
  anchorEl: startThumbEl,
@@ -1,18 +1,29 @@
1
1
  'use client';
2
2
 
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
+ import * as React from 'react';
4
5
  import { styled } from '@mui/material/styles';
5
6
  import { getChartPoint, invertScale, selectorChartAxis, selectorChartAxisZoomOptionsLookup, useChartsContext, useDrawingArea, useStore } from '@mui/x-charts/internals';
6
- import * as React from 'react';
7
7
  import { rafThrottle } from '@mui/x-internals/rafThrottle';
8
8
  import { shouldForwardProp } from '@mui/system';
9
9
  import { selectorChartAxisZoomData } from "../../internals/plugins/useChartProZoom/index.mjs";
10
10
  import { ChartsAxisZoomSliderThumb } from "./ChartsAxisZoomSliderThumb.mjs";
11
11
  import { ChartsTooltipZoomSliderValue } from "./ChartsTooltipZoomSliderValue.mjs";
12
12
  import { calculateZoomEnd, calculateZoomFromPoint, calculateZoomStart } from "./zoom-utils.mjs";
13
- import { ZOOM_SLIDER_THUMB_HEIGHT, ZOOM_SLIDER_THUMB_WIDTH } from "./constants.mjs";
13
+ import { ZOOM_SLIDER_THUMB_HEIGHT, ZOOM_SLIDER_THUMB_WIDTH, ZOOM_SLIDER_TOUCH_TARGET } from "./constants.mjs";
14
14
  import { useUtilityClasses } from "./chartsAxisZoomSliderTrackClasses.mjs";
15
+
16
+ /**
17
+ * Invisible touch target that is only active on coarse pointer devices (touch).
18
+ * On fine pointer devices (mouse), it disables pointer events so it doesn't
19
+ * interfere with precise interactions on small zoom ranges.
20
+ */
15
21
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
+ const TouchTarget = styled('rect')({
23
+ '@media (pointer: fine)': {
24
+ pointerEvents: 'none'
25
+ }
26
+ });
16
27
  const ZoomSliderActiveTrackRect = styled('rect', {
17
28
  slot: 'internal',
18
29
  shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'preview'
@@ -35,7 +46,7 @@ const ZoomSliderActiveTrackRect = styled('rect', {
35
46
  }), {
36
47
  rx: 4,
37
48
  ry: 4,
38
- stroke: theme.palette.grey[500]
49
+ stroke: (theme.vars || theme).palette.grey[500]
39
50
  })
40
51
  }]
41
52
  }));
@@ -49,7 +60,9 @@ export function ChartsAxisZoomSliderActiveTrack({
49
60
  reverse,
50
61
  showTooltip,
51
62
  onPointerEnter,
52
- onPointerLeave
63
+ onPointerLeave,
64
+ onInteractionStart,
65
+ onInteractionEnd
53
66
  }) {
54
67
  const {
55
68
  instance
@@ -60,7 +73,8 @@ export function ChartsAxisZoomSliderActiveTrack({
60
73
  const store = useStore();
61
74
  const axis = store.use(selectorChartAxis, axisId);
62
75
  const drawingArea = useDrawingArea();
63
- const activePreviewRectRef = React.useRef(null);
76
+ const activeTrackGroupRef = React.useRef(null);
77
+ const touchTargetRef = React.useRef(null);
64
78
  const [startThumbEl, setStartThumbEl] = React.useState(null);
65
79
  const [endThumbEl, setEndThumbEl] = React.useState(null);
66
80
  const {
@@ -73,8 +87,9 @@ export function ChartsAxisZoomSliderActiveTrack({
73
87
  const previewThumbWidth = axisDirection === 'x' ? ZOOM_SLIDER_THUMB_WIDTH : ZOOM_SLIDER_THUMB_HEIGHT;
74
88
  const previewThumbHeight = axisDirection === 'x' ? ZOOM_SLIDER_THUMB_HEIGHT : ZOOM_SLIDER_THUMB_WIDTH;
75
89
  React.useEffect(() => {
76
- const activePreviewRect = activePreviewRectRef.current;
77
- if (!activePreviewRect) {
90
+ const group = activeTrackGroupRef.current;
91
+ const touchTarget = touchTargetRef.current;
92
+ if (!group || !touchTarget) {
78
93
  return;
79
94
  }
80
95
  let prevPointerZoom = 0;
@@ -93,13 +108,17 @@ export function ChartsAxisZoomSliderActiveTrack({
93
108
  instance.moveZoomRange(axisId, deltaZoom);
94
109
  });
95
110
  const onPointerUp = () => {
96
- activePreviewRect.removeEventListener('pointermove', onPointerMove);
111
+ group.removeEventListener('pointermove', onPointerMove);
97
112
  document.removeEventListener('pointerup', onPointerUp);
113
+ onInteractionEnd?.();
98
114
  };
99
115
  const onPointerDown = event => {
100
116
  // Prevent text selection when dragging
101
117
  event.preventDefault();
102
- activePreviewRect.setPointerCapture(event.pointerId);
118
+ // Use the touch target rect for pointer capture as setPointerCapture
119
+ // does not work reliably on SVG <g> elements on touch devices.
120
+ touchTarget.setPointerCapture(event.pointerId);
121
+ onInteractionStart?.();
103
122
  const axisZoomData = selectorChartAxisZoomData(store.state, axisId);
104
123
  const element = chartsLayerContainerRef.current;
105
124
  if (!axisZoomData || !element) {
@@ -112,16 +131,18 @@ export function ChartsAxisZoomSliderActiveTrack({
112
131
  }
113
132
  prevPointerZoom = pointerDownZoom;
114
133
  document.addEventListener('pointerup', onPointerUp);
115
- activePreviewRect.addEventListener('pointermove', onPointerMove);
134
+ group.addEventListener('pointermove', onPointerMove);
116
135
  };
117
- activePreviewRect.addEventListener('pointerdown', onPointerDown);
136
+
137
+ // Listen on the group so events from both the visible rect and touch target are captured
138
+ group.addEventListener('pointerdown', onPointerDown);
118
139
 
119
140
  // eslint-disable-next-line consistent-return
120
141
  return () => {
121
- activePreviewRect.removeEventListener('pointerdown', onPointerDown);
142
+ group.removeEventListener('pointerdown', onPointerDown);
122
143
  onPointerMove.clear();
123
144
  };
124
- }, [axisDirection, axisId, instance, reverse, store, chartsLayerContainerRef]);
145
+ }, [axisDirection, axisId, instance, reverse, store, chartsLayerContainerRef, onInteractionStart, onInteractionEnd]);
125
146
  const onStartThumbMove = event => {
126
147
  const element = chartsLayerContainerRef.current;
127
148
  if (!element) {
@@ -215,17 +236,38 @@ export function ChartsAxisZoomSliderActiveTrack({
215
236
  endThumbY -= previewThumbHeight / 2;
216
237
  }
217
238
  const previewOffset = ZOOM_SLIDER_THUMB_HEIGHT > size ? (ZOOM_SLIDER_THUMB_HEIGHT - size) / 2 : 0;
239
+ const activeTrackX = previewX + (axisDirection === 'x' ? 0 : previewOffset);
240
+ const activeTrackY = previewY + (axisDirection === 'x' ? previewOffset : 0);
241
+
242
+ // Compute a larger invisible touch target centered on the visible active track
243
+ const touchWidth = axisDirection === 'y' ? Math.max(previewWidth, ZOOM_SLIDER_TOUCH_TARGET) : previewWidth;
244
+ const touchHeight = axisDirection === 'x' ? Math.max(previewHeight, ZOOM_SLIDER_TOUCH_TARGET) : previewHeight;
245
+ const touchX = activeTrackX - (touchWidth - previewWidth) / 2;
246
+ const touchY = activeTrackY - (touchHeight - previewHeight) / 2;
218
247
  return /*#__PURE__*/_jsxs(React.Fragment, {
219
- children: [/*#__PURE__*/_jsx(ZoomSliderActiveTrackRect, {
220
- ref: activePreviewRectRef,
221
- x: previewX + (axisDirection === 'x' ? 0 : previewOffset),
222
- y: previewY + (axisDirection === 'x' ? previewOffset : 0),
223
- preview: preview,
224
- width: previewWidth,
225
- height: previewHeight,
226
- onPointerEnter: onPointerEnter,
227
- onPointerLeave: onPointerLeave,
228
- className: classes.active
248
+ children: [/*#__PURE__*/_jsxs("g", {
249
+ ref: activeTrackGroupRef,
250
+ children: [/*#__PURE__*/_jsx(ZoomSliderActiveTrackRect, {
251
+ x: activeTrackX,
252
+ y: activeTrackY,
253
+ preview: preview,
254
+ width: previewWidth,
255
+ height: previewHeight,
256
+ onPointerEnter: onPointerEnter,
257
+ onPointerLeave: onPointerLeave,
258
+ className: classes.active
259
+ }), /*#__PURE__*/_jsx(TouchTarget, {
260
+ ref: touchTargetRef,
261
+ x: touchX,
262
+ y: touchY,
263
+ width: touchWidth,
264
+ height: touchHeight,
265
+ fill: "transparent",
266
+ stroke: "none",
267
+ cursor: "grab",
268
+ onPointerEnter: onPointerEnter,
269
+ onPointerLeave: onPointerLeave
270
+ })]
229
271
  }), /*#__PURE__*/_jsx(ChartsAxisZoomSliderThumb, {
230
272
  ref: setStartThumbEl,
231
273
  x: startThumbX,
@@ -236,6 +278,8 @@ export function ChartsAxisZoomSliderActiveTrack({
236
278
  onMove: onStartThumbMove,
237
279
  onPointerEnter: onPointerEnter,
238
280
  onPointerLeave: onPointerLeave,
281
+ onInteractionStart: onInteractionStart,
282
+ onInteractionEnd: onInteractionEnd,
239
283
  placement: "start"
240
284
  }), /*#__PURE__*/_jsx(ChartsAxisZoomSliderThumb, {
241
285
  ref: setEndThumbEl,
@@ -247,6 +291,8 @@ export function ChartsAxisZoomSliderActiveTrack({
247
291
  onMove: onEndThumbMove,
248
292
  onPointerEnter: onPointerEnter,
249
293
  onPointerLeave: onPointerLeave,
294
+ onInteractionStart: onInteractionStart,
295
+ onInteractionEnd: onInteractionEnd,
250
296
  placement: "end"
251
297
  }), /*#__PURE__*/_jsx(ChartsTooltipZoomSliderValue, {
252
298
  anchorEl: startThumbEl,
@@ -1,4 +1,4 @@
1
- import { type AxisId } from '@mui/x-charts/internals';
1
+ import { type AxisId, type SeriesId } from '@mui/x-charts/internals';
2
2
  interface ChartsAxisZoomSliderPreviewProps {
3
3
  axisId: AxisId;
4
4
  axisDirection: 'x' | 'y';
@@ -7,11 +7,16 @@ interface ChartsAxisZoomSliderPreviewProps {
7
7
  y: number;
8
8
  height: number;
9
9
  width: number;
10
+ /**
11
+ * If provided, only the series with these IDs will be shown in the preview.
12
+ */
13
+ seriesIds?: SeriesId[];
10
14
  }
11
15
  export declare function ChartsAxisZoomSliderPreview({
12
16
  axisId,
13
17
  axisDirection,
14
18
  reverse,
19
+ seriesIds,
15
20
  ...props
16
21
  }: ChartsAxisZoomSliderPreviewProps): import("react/jsx-runtime").JSX.Element;
17
22
  export {};
@@ -1,4 +1,4 @@
1
- import { type AxisId } from '@mui/x-charts/internals';
1
+ import { type AxisId, type SeriesId } from '@mui/x-charts/internals';
2
2
  interface ChartsAxisZoomSliderPreviewProps {
3
3
  axisId: AxisId;
4
4
  axisDirection: 'x' | 'y';
@@ -7,11 +7,16 @@ interface ChartsAxisZoomSliderPreviewProps {
7
7
  y: number;
8
8
  height: number;
9
9
  width: number;
10
+ /**
11
+ * If provided, only the series with these IDs will be shown in the preview.
12
+ */
13
+ seriesIds?: SeriesId[];
10
14
  }
11
15
  export declare function ChartsAxisZoomSliderPreview({
12
16
  axisId,
13
17
  axisDirection,
14
18
  reverse,
19
+ seriesIds,
15
20
  ...props
16
21
  }: ChartsAxisZoomSliderPreviewProps): import("react/jsx-runtime").JSX.Element;
17
22
  export {};