Skip to content

Commit 47cd28c

Browse files
authored
[charts-pro] Add brush zoom interaction (#19899)
1 parent f718403 commit 47cd28c

31 files changed

+587
-34
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react';
2+
import { LineChartPro } from '@mui/x-charts-pro/LineChartPro';
3+
import Box from '@mui/material/Box';
4+
import Typography from '@mui/material/Typography';
5+
6+
export default function BrushZoom() {
7+
return (
8+
<Box sx={{ width: '100%' }}>
9+
<Typography variant="body2" sx={{ mb: 2 }}>
10+
Click and drag on the chart to select an area to zoom into. Double-tap to
11+
reset the zoom.
12+
</Typography>
13+
<LineChartPro
14+
height={300}
15+
series={[
16+
{
17+
data: yData,
18+
label: 'Temperature (°C)',
19+
area: true,
20+
},
21+
]}
22+
xAxis={[
23+
{
24+
data: xData,
25+
scaleType: 'point',
26+
zoom: true,
27+
},
28+
]}
29+
zoomInteractionConfig={{
30+
// Enable brush zoom and double-tap reset
31+
zoom: ['brush', 'doubleTapReset'],
32+
// Disable default interactions for this demo
33+
pan: [],
34+
}}
35+
/>
36+
</Box>
37+
);
38+
}
39+
40+
const xData = [
41+
'Jan',
42+
'Feb',
43+
'Mar',
44+
'Apr',
45+
'May',
46+
'Jun',
47+
'Jul',
48+
'Aug',
49+
'Sep',
50+
'Oct',
51+
'Nov',
52+
'Dec',
53+
];
54+
55+
const yData = [2, 5.5, 9, 13.5, 18, 21, 23.5, 23, 20, 15, 9, 4];
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react';
2+
import { LineChartPro } from '@mui/x-charts-pro/LineChartPro';
3+
import Box from '@mui/material/Box';
4+
import Typography from '@mui/material/Typography';
5+
6+
export default function BrushZoom() {
7+
return (
8+
<Box sx={{ width: '100%' }}>
9+
<Typography variant="body2" sx={{ mb: 2 }}>
10+
Click and drag on the chart to select an area to zoom into. Double-tap to
11+
reset the zoom.
12+
</Typography>
13+
<LineChartPro
14+
height={300}
15+
series={[
16+
{
17+
data: yData,
18+
label: 'Temperature (°C)',
19+
area: true,
20+
},
21+
]}
22+
xAxis={[
23+
{
24+
data: xData,
25+
scaleType: 'point',
26+
zoom: true,
27+
},
28+
]}
29+
zoomInteractionConfig={{
30+
// Enable brush zoom and double-tap reset
31+
zoom: ['brush', 'doubleTapReset'],
32+
// Disable default interactions for this demo
33+
pan: [],
34+
}}
35+
/>
36+
</Box>
37+
);
38+
}
39+
40+
const xData = [
41+
'Jan',
42+
'Feb',
43+
'Mar',
44+
'Apr',
45+
'May',
46+
'Jun',
47+
'Jul',
48+
'Aug',
49+
'Sep',
50+
'Oct',
51+
'Nov',
52+
'Dec',
53+
];
54+
55+
const yData = [2, 5.5, 9, 13.5, 18, 21, 23.5, 23, 20, 15, 9, 4];

docs/data/charts/zoom-and-pan/ZoomAndPanInteractions.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ const knobs = {
2323
knob: 'switch',
2424
defaultValue: false,
2525
},
26+
brush: {
27+
displayName: 'Brush',
28+
knob: 'switch',
29+
defaultValue: false,
30+
},
31+
doubleTapReset: {
32+
displayName: 'Double tap reset',
33+
knob: 'switch',
34+
defaultValue: false,
35+
},
2636
// Pan interactions
2737
pan: {
2838
knob: 'title',
@@ -57,6 +67,12 @@ export default function ZoomAndPanInteractions() {
5767
if (props.tapAndDrag) {
5868
zoomInteractions.push('tapAndDrag');
5969
}
70+
if (props.brush) {
71+
zoomInteractions.push('brush');
72+
}
73+
if (props.doubleTapReset) {
74+
zoomInteractions.push('doubleTapReset');
75+
}
6076

6177
// Build pan interactions array
6278
const panInteractions = [];
@@ -106,6 +122,12 @@ export default function ZoomAndPanInteractions() {
106122
if (props.tapAndDrag) {
107123
zoomInteractions.push('tapAndDrag');
108124
}
125+
if (props.brush) {
126+
zoomInteractions.push('brush');
127+
}
128+
if (props.doubleTapReset) {
129+
zoomInteractions.push('doubleTapReset');
130+
}
109131

110132
// Build pan interactions array
111133
const panInteractions = [];

docs/data/charts/zoom-and-pan/ZoomAndPanInteractions.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ const knobs = {
2323
knob: 'switch',
2424
defaultValue: false,
2525
},
26+
brush: {
27+
displayName: 'Brush',
28+
knob: 'switch',
29+
defaultValue: false,
30+
},
31+
doubleTapReset: {
32+
displayName: 'Double tap reset',
33+
knob: 'switch',
34+
defaultValue: false,
35+
},
2636

2737
// Pan interactions
2838
pan: {
@@ -58,6 +68,12 @@ export default function ZoomAndPanInteractions() {
5868
if (props.tapAndDrag) {
5969
zoomInteractions.push('tapAndDrag');
6070
}
71+
if (props.brush) {
72+
zoomInteractions.push('brush');
73+
}
74+
if (props.doubleTapReset) {
75+
zoomInteractions.push('doubleTapReset');
76+
}
6177

6278
// Build pan interactions array
6379
const panInteractions: any[] = [];
@@ -107,6 +123,12 @@ export default function ZoomAndPanInteractions() {
107123
if (props.tapAndDrag) {
108124
zoomInteractions.push('tapAndDrag');
109125
}
126+
if (props.brush) {
127+
zoomInteractions.push('brush');
128+
}
129+
if (props.doubleTapReset) {
130+
zoomInteractions.push('doubleTapReset');
131+
}
110132

111133
// Build pan interactions array
112134
const panInteractions: any[] = [];

docs/data/charts/zoom-and-pan/zoom-and-pan.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Charts - Zoom and pan
33
productId: x-charts
4-
components: ScatterChartPro, BarChartPro, LineChartPro, ChartZoomSlider
4+
components: ScatterChartPro, BarChartPro, LineChartPro, ChartZoomSlider, ChartsBrushOverlay
55
---
66

77
# Charts - Zoom and pan [<span class="plan-pro"></span>](/x/introduction/licensing/#pro-plan 'Pro plan')
@@ -157,16 +157,35 @@ The `zoomInteractionConfig` prop allows you to specify which interactions are en
157157

158158
**Zoom** interactions:
159159

160-
- **`wheel`**: Zoom in/out by scrolling the mouse wheel (default)
161-
- **`pinch`**: Zoom in/out by pinching on touch devices (default)
162-
- **`tapAndDrag`**: Zoom in/out by tapping twice and then dragging vertically. Dragging up zooms in, dragging down zooms out.
160+
- `wheel` (default): Zoom in/out by scrolling the mouse wheel
161+
- `pinch` (default): Zoom in/out by pinching on touch devices
162+
- `tapAndDrag`: Zoom in/out by tapping twice and then dragging vertically. Dragging up zooms in, dragging down zooms out.
163+
- `brush`: Zoom into a selected area by clicking and dragging to create a selection rectangle.
164+
- `doubleTapReset`: Reset the zoom level to the original state when double-tapping.
163165

164166
**Pan** interactions:
165167

166-
- **`drag`**: Pan the chart by dragging with the mouse or touch (default)
168+
- `drag` (default): Pan the chart by dragging with the mouse or touch
169+
- `pressAndDrag`: Pan the chart by pressing and holding, then dragging. Useful for avoiding conflicts with selection gestures.
170+
171+
:::info
172+
When modifying the zoom interaction configuration, care should be taken as to not create a bad user experience.
173+
174+
For example, the "drag" and "brush" interactions do not work well together.
175+
176+
If both are needed, the `pointerMode` and `requiredKeys` options described in the next sections can help.
177+
178+
:::
167179

168180
{{"demo": "ZoomAndPanInteractions.js"}}
169181

182+
### Brush zoom
183+
184+
The brush zoom interaction allows users to select a specific area to zoom into by clicking and dragging to create a selection rectangle.
185+
This provides an intuitive way to focus on a particular region of interest in the chart.
186+
187+
{{"demo": "BrushZoom.js"}}
188+
170189
### Key modifiers
171190

172191
Some interactions allow setting up required keys to be pressed to enable the interaction.

docs/pages/x/api/charts/bar-chart-pro.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
"zoomInteractionConfig": {
136136
"type": {
137137
"name": "shape",
138-
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;'pressAndDrag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'pressAndDrag' }&gt;, zoom?: Array&lt;'pinch'<br>&#124;&nbsp;'tapAndDrag'<br>&#124;&nbsp;'wheel'<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: Array&lt;string&gt;, type: 'wheel' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'pinch' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'tapAndDrag' }&gt; }"
138+
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;'pressAndDrag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'pressAndDrag' }&gt;, zoom?: Array&lt;'brush'<br>&#124;&nbsp;'doubleTapReset'<br>&#124;&nbsp;'pinch'<br>&#124;&nbsp;'tapAndDrag'<br>&#124;&nbsp;'wheel'<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: Array&lt;string&gt;, type: 'wheel' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'pinch' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'tapAndDrag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'doubleTapReset' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'brush' }&gt; }"
139139
}
140140
}
141141
},

docs/pages/x/api/charts/charts-brush-overlay.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
"muiName": "MuiChartsBrushOverlay",
1414
"filename": "/packages/x-charts/src/ChartsBrushOverlay/ChartsBrushOverlay.tsx",
1515
"inheritance": null,
16-
"demos": "<ul><li><a href=\"/x/react-charts/brush/\">Charts - Brush</a></li></ul>",
16+
"demos": "<ul><li><a href=\"/x/react-charts/brush/\">Charts - Brush</a></li>\n<li><a href=\"/x/react-charts/zoom-and-pan/\">Charts - Zoom and pan <a href=\"/x/introduction/licensing/#pro-plan\" title=\"Pro plan\"><span class=\"plan-pro\"></span></a></a></li></ul>",
1717
"cssComponent": false
1818
}

docs/pages/x/api/charts/line-chart-pro.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
"zoomInteractionConfig": {
131131
"type": {
132132
"name": "shape",
133-
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;'pressAndDrag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'pressAndDrag' }&gt;, zoom?: Array&lt;'pinch'<br>&#124;&nbsp;'tapAndDrag'<br>&#124;&nbsp;'wheel'<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: Array&lt;string&gt;, type: 'wheel' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'pinch' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'tapAndDrag' }&gt; }"
133+
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;'pressAndDrag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'pressAndDrag' }&gt;, zoom?: Array&lt;'brush'<br>&#124;&nbsp;'doubleTapReset'<br>&#124;&nbsp;'pinch'<br>&#124;&nbsp;'tapAndDrag'<br>&#124;&nbsp;'wheel'<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: Array&lt;string&gt;, type: 'wheel' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'pinch' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'tapAndDrag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'doubleTapReset' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'brush' }&gt; }"
134134
}
135135
}
136136
},

docs/pages/x/api/charts/scatter-chart-pro.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
"zoomInteractionConfig": {
126126
"type": {
127127
"name": "shape",
128-
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;'pressAndDrag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'pressAndDrag' }&gt;, zoom?: Array&lt;'pinch'<br>&#124;&nbsp;'tapAndDrag'<br>&#124;&nbsp;'wheel'<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: Array&lt;string&gt;, type: 'wheel' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'pinch' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'tapAndDrag' }&gt; }"
128+
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;'pressAndDrag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'pressAndDrag' }&gt;, zoom?: Array&lt;'brush'<br>&#124;&nbsp;'doubleTapReset'<br>&#124;&nbsp;'pinch'<br>&#124;&nbsp;'tapAndDrag'<br>&#124;&nbsp;'wheel'<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: Array&lt;string&gt;, type: 'wheel' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'pinch' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'tapAndDrag' }<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'doubleTapReset' }<br>&#124;&nbsp;{ pointerMode?: any, requiredKeys?: array, type: 'brush' }&gt; }"
129129
}
130130
}
131131
},

packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ChartsClipPath } from '@mui/x-charts/ChartsClipPath';
1313
import { useBarChartProps } from '@mui/x-charts/internals';
1414
import { ChartsSurface } from '@mui/x-charts/ChartsSurface';
1515
import { ChartsWrapper } from '@mui/x-charts/ChartsWrapper';
16+
import { ChartsBrushOverlay } from '@mui/x-charts/ChartsBrushOverlay';
1617
import {
1718
ChartsToolbarProSlotProps,
1819
ChartsToolbarProSlots,
@@ -115,6 +116,7 @@ const BarChartPro = React.forwardRef(function BarChartPro(
115116
</g>
116117
<ChartsAxis {...chartsAxisProps} />
117118
<ChartZoomSlider />
119+
<ChartsBrushOverlay />
118120
<ChartsClipPath {...clipPathProps} />
119121
{children}
120122
</ChartsSurface>
@@ -1972,7 +1974,7 @@ BarChartPro.propTypes = {
19721974
),
19731975
zoom: PropTypes.arrayOf(
19741976
PropTypes.oneOfType([
1975-
PropTypes.oneOf(['pinch', 'tapAndDrag', 'wheel']),
1977+
PropTypes.oneOf(['brush', 'doubleTapReset', 'pinch', 'tapAndDrag', 'wheel']),
19761978
PropTypes.shape({
19771979
pointerMode: PropTypes.any,
19781980
requiredKeys: PropTypes.arrayOf(PropTypes.string),
@@ -1988,6 +1990,16 @@ BarChartPro.propTypes = {
19881990
requiredKeys: PropTypes.arrayOf(PropTypes.string),
19891991
type: PropTypes.oneOf(['tapAndDrag']).isRequired,
19901992
}),
1993+
PropTypes.shape({
1994+
pointerMode: PropTypes.oneOf(['mouse', 'touch']),
1995+
requiredKeys: PropTypes.arrayOf(PropTypes.string),
1996+
type: PropTypes.oneOf(['doubleTapReset']).isRequired,
1997+
}),
1998+
PropTypes.shape({
1999+
pointerMode: PropTypes.any,
2000+
requiredKeys: PropTypes.array,
2001+
type: PropTypes.oneOf(['brush']).isRequired,
2002+
}),
19912003
]).isRequired,
19922004
),
19932005
}),

0 commit comments

Comments
 (0)