Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
95fe3d7
add keys and mode handling
JCQuintas Jul 1, 2025
261a655
simplify proptypes
JCQuintas Jul 1, 2025
602ad37
add tests
JCQuintas Jul 1, 2025
dfa7f42
docs
JCQuintas Jul 1, 2025
2934bdf
fix types
JCQuintas Jul 2, 2025
96966fa
Merge commit 'fac25a0892692bb0e3e1e25274d85a20ff9cee5b' into zoom-con…
JCQuintas Aug 11, 2025
f2b29a2
split zoom interactions
JCQuintas Aug 12, 2025
b0226d0
improve configuration and rename props
JCQuintas Aug 12, 2025
07cbeee
Merge commit 'dbd436ed10bee05e2decc6578587ffca527c33b5' into zoom-con…
JCQuintas Aug 20, 2025
c64ef57
export pmode
JCQuintas Aug 25, 2025
36bf78f
impl
JCQuintas Aug 25, 2025
1e6ca49
add logic
JCQuintas Aug 25, 2025
d97d83f
export
JCQuintas Aug 25, 2025
15f27ce
Merge commit 'd97d83f47baed0f19eff1da0cd2c853e2ce0dfee' into zoom-con…
JCQuintas Aug 25, 2025
669695f
allow multiconfig
JCQuintas Aug 25, 2025
9840ae7
improve docs
JCQuintas Aug 25, 2025
d05d8f7
fix behaviour to align with expectation
JCQuintas Aug 25, 2025
dff7a90
Merge branch 'master' into zoom-configuration-simplified
JCQuintas Aug 25, 2025
667a268
Update docs/data/charts/zoom-and-pan/zoom-and-pan.md
JCQuintas Sep 15, 2025
cbeb4e1
Merge commit '7f540b1d48d7facac5dcf5b53ddb404185e9449c' into zoom-con…
JCQuintas Sep 15, 2025
62f40a0
fix type
JCQuintas Sep 15, 2025
4a4326f
fix lint
JCQuintas Sep 15, 2025
59f0491
fix types
JCQuintas Sep 15, 2025
9b20308
sugg
JCQuintas Sep 15, 2025
2300dc3
simplify types
JCQuintas Sep 15, 2025
a681945
retro
JCQuintas Sep 15, 2025
f701ba7
fix type issues
JCQuintas Sep 16, 2025
0cbcfd3
fix requiredkeys not working in some instances
JCQuintas Sep 16, 2025
dc758f2
scripts
JCQuintas Sep 16, 2025
db2944a
ts
JCQuintas Sep 16, 2025
90546a5
fix ts error using anyentry
JCQuintas Sep 16, 2025
8da2f5d
update params when they change
JCQuintas Sep 17, 2025
31ba823
Merge branch 'master' into zoom-configuration-simplified
JCQuintas Sep 17, 2025
53a48d0
try revert
JCQuintas Sep 17, 2025
b8bd625
Revert "try revert"
JCQuintas Sep 18, 2025
1490d65
Revert "update params when they change"
JCQuintas Sep 18, 2025
08e4f42
use layout effect
JCQuintas Sep 18, 2025
1271e0c
eff
JCQuintas Sep 18, 2025
3cfe653
ZoomConfig > ZoomInteractionConfig
JCQuintas Sep 18, 2025
6e8f7d5
scripts
JCQuintas Sep 18, 2025
ae305e9
remove comment
JCQuintas Sep 18, 2025
8ccde7f
Merge commit '112c08f2efbeb0480c764e2946d4168c921a2a2a' into zoom-con…
JCQuintas Sep 18, 2025
aa84499
fix missing refs
JCQuintas Sep 18, 2025
b4be924
Merge branch 'master' into zoom-configuration-simplified
JCQuintas Sep 18, 2025
627ff1f
Merge branch 'master' into zoom-configuration-simplified
JCQuintas Sep 18, 2025
3411313
JCQuintas Sep 18, 2025
522c115
fix config clarity and memoization on all interactions
JCQuintas Sep 18, 2025
2292df4
Merge branch 'master' into zoom-configuration-simplified
JCQuintas Sep 18, 2025
40d8f9c
test
JCQuintas Sep 18, 2025
8be2e5b
memo obj
JCQuintas Sep 18, 2025
6165ef7
Revert "memo obj"
JCQuintas Sep 18, 2025
f78ae17
test change in place
JCQuintas Sep 18, 2025
14126c0
add hook and logic
JCQuintas Sep 19, 2025
af02063
custom hooks
JCQuintas Sep 19, 2025
44133e5
fixes
JCQuintas Sep 22, 2025
408df54
remove on
JCQuintas Sep 22, 2025
14c24e9
Merge commit 'ba2428ddee2c58391609580c2c4effdcaa316d88' into zoom-con…
JCQuintas Sep 22, 2025
1f330b1
scripts
JCQuintas Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions docs/data/charts/zoom-and-pan/zoom-and-pan.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,93 @@ The `zoom` state is an array of objects that define the zoom state for each axis
To synchronize zoom between multiple charts, you can control the zoom state.

{{"demo": "ZoomControlled.js"}}

## Zoom interactions configuration

You can have fine-grained control over which interactions are enabled and under which conditions by using the `zoomInteractionConfig` prop.

### Interactions

The `zoomInteractionConfig` prop allows you to specify which interactions are enabled for zooming and panning:

```jsx
<BarChartPro
zoomInteractionConfig={{
// Enable both wheel and pinch zoom
zoom: ['wheel', 'pinch'],
// Enable drag panning
pan: ['drag'],
}}
/>
```

### Key modifiers

Some interactions allow setting up required keys to be pressed to enable the interaction.
This can be set up using the `requiredKeys` property in the interaction configuration.

```jsx
<BarChartPro
zoomInteractionConfig={{
// Only zoom when Control key is pressed
zoom: [{ type: 'wheel', requiredKeys: ['Control'] }],
// Only pan when Shift key is pressed
pan: [{ type: 'drag', requiredKeys: ['Shift'] }],
}}
/>
```

Available keys include:

- Modifier keys: `'Shift'`, `'Control'`, `'Alt'`, `'Meta'`
- `'ControlOrMeta'` which resolves to `Control` on Windows and Linux and to `Meta` on macOS.
- Any other key can be used as well, such as `'Space'` and `'Enter'` based on [`event.key` values](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).

It is also possible to require multiple keys to be pressed simultaneously:

```jsx
<BarChartPro
zoomInteractionConfig={{
// Only pan when both Shift and Control are pressed
pan: [{ type: 'drag', requiredKeys: ['Shift', 'Control'] }],
}}
/>
```

### Pointer Modes

Interactions can also be restricted to specific pointer types by using the `mode` property:

```jsx
<BarChartPro
zoomInteractionConfig={{
// Only pan with touch, not mouse
pan: [{ type: 'drag', pointerMode: 'touch' }],
}}
// other props
/>
```

Available pointer modes:

- `undefined`: Allow both mouse and touch interactions (default)
- `'mouse'`: Only allow mouse interactions
- `'touch'`: Only allow touch interactions

### Multiple interactions of the same type

It is possible to define multiple interactions of the same type with different configurations.

In the example below, the pan `drag` interaction is configured to require a specific key combination for mouse, while touch interactions don't require any key to be pressed:

```jsx
<BarChartPro
zoomInteractionConfig={{
pan: [
{ type: 'drag', pointerMode: 'mouse', requiredKeys: ['ControlOrMeta'] },
{ type: 'drag', pointerMode: 'touch', requiredKeys: [] },
],
}}
// other props
/>
```
6 changes: 6 additions & 0 deletions docs/pages/x/api/charts/bar-chart-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@
"name": "arrayOf",
"description": "Array&lt;{ axisId: number<br>&#124;&nbsp;string, end: number, start: number }&gt;"
}
},
"zoomInteractionConfig": {
"type": {
"name": "shape",
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }&gt;, zoom?: Array&lt;'pinch'<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' }&gt; }"
}
}
},
"name": "BarChartPro",
Expand Down
6 changes: 6 additions & 0 deletions docs/pages/x/api/charts/line-chart-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@
"name": "arrayOf",
"description": "Array&lt;{ axisId: number<br>&#124;&nbsp;string, end: number, start: number }&gt;"
}
},
"zoomInteractionConfig": {
"type": {
"name": "shape",
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }&gt;, zoom?: Array&lt;'pinch'<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' }&gt; }"
}
}
},
"name": "LineChartPro",
Expand Down
6 changes: 6 additions & 0 deletions docs/pages/x/api/charts/scatter-chart-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@
"name": "arrayOf",
"description": "Array&lt;{ axisId: number<br>&#124;&nbsp;string, end: number, start: number }&gt;"
}
},
"zoomInteractionConfig": {
"type": {
"name": "shape",
"description": "{ pan?: Array&lt;'drag'<br>&#124;&nbsp;{ pointerMode?: 'mouse'<br>&#124;&nbsp;'touch', requiredKeys?: Array&lt;string&gt;, type: 'drag' }&gt;, zoom?: Array&lt;'pinch'<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' }&gt; }"
}
}
},
"name": "ScatterChartPro",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@
"description": "The configuration of the y-axes. If not provided, a default axis config is used. An array of <a href='/x/api/charts/axis-config/'>AxisConfig</a> objects."
},
"zAxis": { "description": "The configuration of the z-axes." },
"zoomData": { "description": "The list of zoom data related to each axis." }
"zoomData": { "description": "The list of zoom data related to each axis." },
"zoomInteractionConfig": { "description": "Configuration for zoom interactions." }
},
"classDescriptions": {},
"slotDescriptions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
"description": "The configuration of the y-axes. If not provided, a default axis config is used. An array of <a href='/x/api/charts/axis-config/'>AxisConfig</a> objects."
},
"zAxis": { "description": "The configuration of the z-axes." },
"zoomData": { "description": "The list of zoom data related to each axis." }
"zoomData": { "description": "The list of zoom data related to each axis." },
"zoomInteractionConfig": { "description": "Configuration for zoom interactions." }
},
"classDescriptions": {},
"slotDescriptions": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
"description": "The configuration of the y-axes. If not provided, a default axis config is used. An array of <a href='/x/api/charts/axis-config/'>AxisConfig</a> objects."
},
"zAxis": { "description": "The configuration of the z-axes." },
"zoomData": { "description": "The list of zoom data related to each axis." }
"zoomData": { "description": "The list of zoom data related to each axis." },
"zoomInteractionConfig": { "description": "Configuration for zoom interactions." }
},
"classDescriptions": {},
"slotDescriptions": {
Expand Down
6 changes: 6 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ export default defineConfig(
// migration rules
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-require-imports': 'off',
'react-hooks/exhaustive-deps': [
'error',
{
additionalHooks: '(useEnhancedEffect|useIsoLayoutEffect|useEffectAfterFirstRender)',
},
],
},
},
// Test start
Expand Down
30 changes: 30 additions & 0 deletions packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,36 @@ BarChartPro.propTypes = {
start: PropTypes.number.isRequired,
}),
),
/**
* Configuration for zoom interactions.
*/
zoomInteractionConfig: PropTypes.shape({
pan: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.oneOf(['drag']),
PropTypes.shape({
pointerMode: PropTypes.oneOf(['mouse', 'touch']),
requiredKeys: PropTypes.arrayOf(PropTypes.string),
type: PropTypes.oneOf(['drag']).isRequired,
}),
]).isRequired,
),
zoom: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.oneOf(['pinch', 'wheel']),
PropTypes.shape({
pointerMode: PropTypes.any,
requiredKeys: PropTypes.arrayOf(PropTypes.string),
type: PropTypes.oneOf(['wheel']).isRequired,
}),
PropTypes.shape({
pointerMode: PropTypes.any,
requiredKeys: PropTypes.array,
type: PropTypes.oneOf(['pinch']).isRequired,
}),
]).isRequired,
),
}),
} as any;

export { BarChartPro };
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ export const useChartContainerProProps = <
props: ChartContainerProProps<TSeries, TSignatures>,
ref: React.Ref<SVGSVGElement>,
): UseChartContainerProPropsReturnValue<TSeries, TSignatures> => {
const { initialZoom, zoomData, onZoomChange, plugins, apiRef, ...baseProps } =
props as ChartContainerProProps<TSeries, AllPluginSignatures>;
const {
initialZoom,
zoomData,
onZoomChange,
zoomInteractionConfig,
plugins,
apiRef,
...baseProps
} = props as ChartContainerProProps<TSeries, AllPluginSignatures>;

const { chartDataProviderProps, chartsSurfaceProps, children } = useChartContainerProps<TSeries>(
baseProps,
Expand All @@ -40,6 +47,7 @@ export const useChartContainerProProps = <
initialZoom,
zoomData,
onZoomChange,
zoomInteractionConfig,
apiRef,
plugins: plugins ?? DEFAULT_PLUGINS,
} as unknown as ChartDataProviderProps<TSeries, TSignatures>;
Expand Down
30 changes: 30 additions & 0 deletions packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1924,6 +1924,36 @@ LineChartPro.propTypes = {
start: PropTypes.number.isRequired,
}),
),
/**
* Configuration for zoom interactions.
*/
zoomInteractionConfig: PropTypes.shape({
pan: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.oneOf(['drag']),
PropTypes.shape({
pointerMode: PropTypes.oneOf(['mouse', 'touch']),
requiredKeys: PropTypes.arrayOf(PropTypes.string),
type: PropTypes.oneOf(['drag']).isRequired,
}),
]).isRequired,
),
zoom: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.oneOf(['pinch', 'wheel']),
PropTypes.shape({
pointerMode: PropTypes.any,
requiredKeys: PropTypes.arrayOf(PropTypes.string),
type: PropTypes.oneOf(['wheel']).isRequired,
}),
PropTypes.shape({
pointerMode: PropTypes.any,
requiredKeys: PropTypes.array,
type: PropTypes.oneOf(['pinch']).isRequired,
}),
]).isRequired,
),
}),
} as any;

export { LineChartPro };
30 changes: 30 additions & 0 deletions packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1912,6 +1912,36 @@ ScatterChartPro.propTypes = {
start: PropTypes.number.isRequired,
}),
),
/**
* Configuration for zoom interactions.
*/
zoomInteractionConfig: PropTypes.shape({
pan: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.oneOf(['drag']),
PropTypes.shape({
pointerMode: PropTypes.oneOf(['mouse', 'touch']),
requiredKeys: PropTypes.arrayOf(PropTypes.string),
type: PropTypes.oneOf(['drag']).isRequired,
}),
]).isRequired,
),
zoom: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.oneOf(['pinch', 'wheel']),
PropTypes.shape({
pointerMode: PropTypes.any,
requiredKeys: PropTypes.arrayOf(PropTypes.string),
type: PropTypes.oneOf(['wheel']).isRequired,
}),
PropTypes.shape({
pointerMode: PropTypes.any,
requiredKeys: PropTypes.array,
type: PropTypes.oneOf(['pinch']).isRequired,
}),
]).isRequired,
),
}),
} as any;

export { ScatterChartPro };
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createSelector } from '@mui/x-charts/internals';
import { selectorChartZoomState } from './useChartProZoom.selectors';
import type { ZoomInteractionName, PanInteractionName } from './ZoomInteractionConfig.types';

export const selectorZoomInteractionConfig = createSelector(
[selectorChartZoomState, (_state, interactionName: ZoomInteractionName) => interactionName],
(zoomState, interactionName) => zoomState.zoomInteractionConfig.zoom[interactionName] ?? null,
);

export const selectorPanInteractionConfig = createSelector(
[selectorChartZoomState, (_state, interactionName: PanInteractionName) => interactionName],
(zoomState, interactionName) => zoomState.zoomInteractionConfig.pan[interactionName] ?? null,
);
Loading
Loading