@mui/x-data-grid 8.25.0 → 8.26.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 (122) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/components/cell/GridActionsCell.d.ts +22 -2
  3. package/components/cell/GridActionsCell.js +15 -5
  4. package/components/toolbar/GridToolbar.d.ts +5 -0
  5. package/components/toolbar/GridToolbar.js +5 -0
  6. package/components/toolbarV8/GridToolbar.d.ts +1 -1
  7. package/components/toolbarV8/GridToolbar.js +26 -24
  8. package/constants/localeTextConstants.js +3 -0
  9. package/esm/components/cell/GridActionsCell.d.ts +22 -2
  10. package/esm/components/cell/GridActionsCell.js +15 -5
  11. package/esm/components/toolbar/GridToolbar.d.ts +5 -0
  12. package/esm/components/toolbar/GridToolbar.js +5 -0
  13. package/esm/components/toolbarV8/GridToolbar.d.ts +1 -1
  14. package/esm/components/toolbarV8/GridToolbar.js +26 -24
  15. package/esm/constants/localeTextConstants.js +3 -0
  16. package/esm/hooks/core/useGridProps.js +5 -3
  17. package/esm/hooks/features/focus/useGridFocus.js +26 -2
  18. package/esm/hooks/features/scroll/useGridScroll.js +3 -3
  19. package/esm/index.js +1 -1
  20. package/esm/internals/index.d.ts +1 -1
  21. package/esm/internals/index.js +1 -1
  22. package/esm/locales/arSD.js +3 -0
  23. package/esm/locales/beBY.js +3 -0
  24. package/esm/locales/bgBG.js +3 -0
  25. package/esm/locales/bnBD.js +3 -0
  26. package/esm/locales/caES.js +3 -0
  27. package/esm/locales/csCZ.js +3 -0
  28. package/esm/locales/daDK.js +3 -0
  29. package/esm/locales/deDE.js +3 -0
  30. package/esm/locales/elGR.js +3 -0
  31. package/esm/locales/esES.js +3 -0
  32. package/esm/locales/faIR.js +3 -0
  33. package/esm/locales/fiFI.js +3 -0
  34. package/esm/locales/frFR.js +3 -0
  35. package/esm/locales/heIL.js +3 -0
  36. package/esm/locales/hrHR.js +3 -0
  37. package/esm/locales/huHU.js +3 -0
  38. package/esm/locales/hyAM.js +3 -0
  39. package/esm/locales/idID.js +3 -0
  40. package/esm/locales/isIS.js +3 -0
  41. package/esm/locales/itIT.js +3 -0
  42. package/esm/locales/jaJP.js +3 -0
  43. package/esm/locales/koKR.js +3 -0
  44. package/esm/locales/nbNO.js +3 -0
  45. package/esm/locales/nlNL.js +3 -0
  46. package/esm/locales/nnNO.js +3 -0
  47. package/esm/locales/plPL.js +3 -0
  48. package/esm/locales/ptBR.js +3 -0
  49. package/esm/locales/ptPT.js +3 -0
  50. package/esm/locales/roRO.js +3 -0
  51. package/esm/locales/ruRU.js +3 -0
  52. package/esm/locales/skSK.js +3 -0
  53. package/esm/locales/svSE.js +3 -0
  54. package/esm/locales/trTR.js +3 -0
  55. package/esm/locales/ukUA.js +3 -0
  56. package/esm/locales/urPK.js +3 -0
  57. package/esm/locales/viVN.js +3 -0
  58. package/esm/locales/zhCN.js +3 -0
  59. package/esm/locales/zhHK.js +3 -0
  60. package/esm/locales/zhTW.js +3 -0
  61. package/esm/material/icons/index.d.ts +2 -0
  62. package/esm/material/icons/index.js +6 -0
  63. package/esm/material/index.js +3 -1
  64. package/esm/models/api/gridLocaleTextApi.d.ts +2 -0
  65. package/esm/models/gridIconSlotsComponent.d.ts +10 -0
  66. package/esm/models/gridStateCommunity.d.ts +1 -1
  67. package/esm/utils/keyboardUtils.d.ts +3 -1
  68. package/esm/utils/keyboardUtils.js +6 -0
  69. package/hooks/core/useGridProps.js +5 -3
  70. package/hooks/features/focus/useGridFocus.js +25 -1
  71. package/hooks/features/scroll/useGridScroll.js +3 -3
  72. package/index.js +1 -1
  73. package/internals/index.d.ts +1 -1
  74. package/internals/index.js +14 -0
  75. package/locales/arSD.js +3 -0
  76. package/locales/beBY.js +3 -0
  77. package/locales/bgBG.js +3 -0
  78. package/locales/bnBD.js +3 -0
  79. package/locales/caES.js +3 -0
  80. package/locales/csCZ.js +3 -0
  81. package/locales/daDK.js +3 -0
  82. package/locales/deDE.js +3 -0
  83. package/locales/elGR.js +3 -0
  84. package/locales/esES.js +3 -0
  85. package/locales/faIR.js +3 -0
  86. package/locales/fiFI.js +3 -0
  87. package/locales/frFR.js +3 -0
  88. package/locales/heIL.js +3 -0
  89. package/locales/hrHR.js +3 -0
  90. package/locales/huHU.js +3 -0
  91. package/locales/hyAM.js +3 -0
  92. package/locales/idID.js +3 -0
  93. package/locales/isIS.js +3 -0
  94. package/locales/itIT.js +3 -0
  95. package/locales/jaJP.js +3 -0
  96. package/locales/koKR.js +3 -0
  97. package/locales/nbNO.js +3 -0
  98. package/locales/nlNL.js +3 -0
  99. package/locales/nnNO.js +3 -0
  100. package/locales/plPL.js +3 -0
  101. package/locales/ptBR.js +3 -0
  102. package/locales/ptPT.js +3 -0
  103. package/locales/roRO.js +3 -0
  104. package/locales/ruRU.js +3 -0
  105. package/locales/skSK.js +3 -0
  106. package/locales/svSE.js +3 -0
  107. package/locales/trTR.js +3 -0
  108. package/locales/ukUA.js +3 -0
  109. package/locales/urPK.js +3 -0
  110. package/locales/viVN.js +3 -0
  111. package/locales/zhCN.js +3 -0
  112. package/locales/zhHK.js +3 -0
  113. package/locales/zhTW.js +3 -0
  114. package/material/icons/index.d.ts +2 -0
  115. package/material/icons/index.js +7 -1
  116. package/material/index.js +2 -0
  117. package/models/api/gridLocaleTextApi.d.ts +2 -0
  118. package/models/gridIconSlotsComponent.d.ts +10 -0
  119. package/models/gridStateCommunity.d.ts +1 -1
  120. package/package.json +3 -3
  121. package/utils/keyboardUtils.d.ts +3 -1
  122. package/utils/keyboardUtils.js +8 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,90 @@
5
5
  All notable changes to this project will be documented in this file.
6
6
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
7
 
8
+ ## 8.26.0
9
+
10
+ _Jan 22, 2026_
11
+
12
+ We'd like to extend a big thank you to the 6 contributors who made this release possible. Here are some highlights ✨:
13
+
14
+ - 🔄 Data Grid now supports undo and redo actions. See the [Undo and redo](https://mui.com/x/react-data-grid/undo-redo/) page for details about out-of-the-box support and customization options.
15
+ - 🐞 Bugfixes
16
+
17
+ Special thanks go out to these community members for their valuable contributions:
18
+ @jhe-iqbis
19
+
20
+ The following team members contributed to this release:
21
+ @arminmeh, @cherniavskii, @flaviendelangle, @JCQuintas, @romgrk
22
+
23
+ ### Data Grid
24
+
25
+ #### `@mui/x-data-grid@8.26.0`
26
+
27
+ - [DataGrid] Add `onMenuOpen()` and `onMenuClose()` event handlers in `GridActionsCell` (#20994) @jhe-iqbis
28
+ - [DataGrid] Fix scroll position when virtualization is disabled (#20958) @romgrk
29
+
30
+ #### `@mui/x-data-grid-pro@8.26.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
31
+
32
+ Same changes as in `@mui/x-data-grid@8.26.0`.
33
+
34
+ #### `@mui/x-data-grid-premium@8.26.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
35
+
36
+ Same changes as in `@mui/x-data-grid-pro@8.26.0`, plus:
37
+
38
+ - [DataGridPremium] Undo and redo (#20993) @arminmeh
39
+
40
+ ### Date and Time Pickers
41
+
42
+ #### `@mui/x-date-pickers@8.26.0`
43
+
44
+ Internal changes.
45
+
46
+ #### `@mui/x-date-pickers-pro@8.26.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
47
+
48
+ Same changes as in `@mui/x-date-pickers@8.26.0`.
49
+
50
+ ### Charts
51
+
52
+ #### `@mui/x-charts@8.26.0`
53
+
54
+ Internal changes.
55
+
56
+ #### `@mui/x-charts-pro@8.26.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
57
+
58
+ Same changes as in `@mui/x-charts@8.26.0`.
59
+
60
+ #### `@mui/x-charts-premium@8.26.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
61
+
62
+ Same changes as in `@mui/x-charts-pro@8.26.0`.
63
+
64
+ ### Tree View
65
+
66
+ #### `@mui/x-tree-view@8.26.0`
67
+
68
+ - [tree view] Fix `props.id` not passed to the root element (#20976) @flaviendelangle
69
+
70
+ #### `@mui/x-tree-view-pro@8.26.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
71
+
72
+ Same changes as in `@mui/x-tree-view@8.26.0`.
73
+
74
+ ### Codemod
75
+
76
+ #### `@mui/x-codemod@8.26.0`
77
+
78
+ Internal changes.
79
+
80
+ ### Docs
81
+
82
+ - [docs] Recipe for lazy loading DataGrid's detail panels with auto height (#20995) @arminmeh
83
+
84
+ ### Core
85
+
86
+ - [code-infra] Update `master` to `v8` references (#20864) @JCQuintas
87
+ - [code-infra] Update v8 branch tags (#20926) @JCQuintas
88
+ - [code-infra] V8 changes in master (#20919) @JCQuintas
89
+ - [code-infra] Allow user to select target branch if it exists for current major (#21005) @JCQuintas
90
+ - [internal] Set up shared instructions for coding agents (#21000) @cherniavskii
91
+
8
92
  ## 8.25.0
9
93
 
10
94
  <!-- generated comparing v8.24.0..master -->
@@ -1,7 +1,9 @@
1
1
  import * as React from 'react';
2
+ import { GridRowParams } from "../../models/params/gridRowParams.js";
2
3
  import { GridRenderCellParams } from "../../models/params/gridCellParams.js";
3
4
  import { GridMenuProps } from "../menu/GridMenu.js";
4
- interface GridActionsCellProps extends Omit<GridRenderCellParams, 'api'> {
5
+ import { GridValidRowModel, GridTreeNodeWithRender } from "../../models/gridRows.js";
6
+ interface GridActionsCellProps<R extends GridValidRowModel = any, V = any, F = V, N extends GridTreeNodeWithRender = GridTreeNodeWithRender> extends Omit<GridRenderCellParams<R, V, F, N>, 'api'> {
5
7
  api?: GridRenderCellParams['api'];
6
8
  position?: GridMenuProps['position'];
7
9
  children: React.ReactNode;
@@ -12,8 +14,26 @@ interface GridActionsCellProps extends Omit<GridRenderCellParams, 'api'> {
12
14
  * @default false
13
15
  */
14
16
  suppressChildrenValidation?: boolean;
17
+ /**
18
+ * Callback to fire before the menu gets opened.
19
+ * Use this callback to prevent the menu from opening.
20
+ *
21
+ * @param {GridRowParams<R>} params Row parameters.
22
+ * @param {React.MouseEvent<HTMLElement>} event The event triggering this callback.
23
+ * @returns {boolean} if the menu should be opened.
24
+ */
25
+ onMenuOpen?: (params: GridRowParams<R>, event: React.MouseEvent<HTMLElement>) => boolean;
26
+ /**
27
+ * Callback to fire before the menu gets closed.
28
+ * Use this callback to prevent the menu from closing.
29
+ *
30
+ * @param {GridRowParams<R>} params Row parameters.
31
+ * @param {React.MouseEvent<HTMLElement> | React.KeyboardEvent | MouseEvent | TouchEvent | undefined} event The event triggering this callback.
32
+ * @returns {boolean} if the menu should be closed.
33
+ */
34
+ onMenuClose?: (params: GridRowParams<R>, event: React.MouseEvent<HTMLElement> | React.KeyboardEvent | MouseEvent | TouchEvent | undefined) => boolean;
15
35
  }
16
- declare function GridActionsCell(props: GridActionsCellProps): import("react/jsx-runtime").JSX.Element;
36
+ declare function GridActionsCell<R extends GridValidRowModel = any, V = any, F = V, N extends GridTreeNodeWithRender = GridTreeNodeWithRender>(props: GridActionsCellProps<R, V, F, N>): import("react/jsx-runtime").JSX.Element;
17
37
  declare namespace GridActionsCell {
18
38
  var propTypes: any;
19
39
  }
@@ -21,13 +21,16 @@ var _useGridRootProps = require("../../hooks/utils/useGridRootProps");
21
21
  var _useGridApiContext = require("../../hooks/utils/useGridApiContext");
22
22
  var _GridActionsCellItem = require("./GridActionsCellItem");
23
23
  var _jsxRuntime = require("react/jsx-runtime");
24
- const _excluded = ["api", "colDef", "id", "hasFocus", "isEditable", "field", "value", "formattedValue", "row", "rowNode", "cellMode", "tabIndex", "position", "children", "suppressChildrenValidation"];
24
+ const _excluded = ["api", "colDef", "id", "hasFocus", "isEditable", "field", "value", "formattedValue", "row", "rowNode", "cellMode", "tabIndex", "position", "onMenuOpen", "onMenuClose", "children", "suppressChildrenValidation"];
25
25
  const hasActions = colDef => typeof colDef.getActions === 'function';
26
26
  function GridActionsCell(props) {
27
27
  const {
28
+ id,
28
29
  hasFocus,
29
30
  tabIndex,
30
31
  position = 'bottom-end',
32
+ onMenuOpen,
33
+ onMenuClose,
31
34
  children,
32
35
  suppressChildrenValidation
33
36
  } = props,
@@ -43,6 +46,7 @@ function GridActionsCell(props) {
43
46
  const menuId = (0, _useId.default)();
44
47
  const buttonId = (0, _useId.default)();
45
48
  const rootProps = (0, _useGridRootProps.useGridRootProps)();
49
+ const rowParams = apiRef.current.getRowParams(id);
46
50
  const actions = [];
47
51
  React.Children.forEach(children, child => {
48
52
  // Unwrap React.Fragment
@@ -101,21 +105,27 @@ If this is intentional, you can suppress this warning by passing the \`suppressC
101
105
  setFocusedButtonIndex(numberOfButtons - 1);
102
106
  }
103
107
  }, [focusedButtonIndex, numberOfButtons]);
104
- const showMenu = () => {
108
+ const showMenu = event => {
109
+ if (onMenuOpen && !onMenuOpen(rowParams, event)) {
110
+ return;
111
+ }
105
112
  setOpen(true);
106
113
  setFocusedButtonIndex(numberOfButtons - 1);
107
114
  ignoreCallToFocus.current = true;
108
115
  };
109
- const hideMenu = () => {
116
+ const hideMenu = event => {
117
+ if (onMenuClose && !onMenuClose(rowParams, event)) {
118
+ return;
119
+ }
110
120
  setOpen(false);
111
121
  };
112
122
  const toggleMenu = event => {
113
123
  event.stopPropagation();
114
124
  event.preventDefault();
115
125
  if (open) {
116
- hideMenu();
126
+ hideMenu(event);
117
127
  } else {
118
- showMenu();
128
+ showMenu(event);
119
129
  }
120
130
  };
121
131
  const handleTouchRippleRef = index => instance => {
@@ -2,6 +2,11 @@ import { GridToolbarContainerProps } from "../containers/GridToolbarContainer.js
2
2
  import { GridToolbarExportProps } from "./GridToolbarExport.js";
3
3
  import { GridToolbarQuickFilterProps } from "./GridToolbarQuickFilter.js";
4
4
  export interface GridToolbarProps extends GridToolbarContainerProps, GridToolbarExportProps {
5
+ /**
6
+ * Show the history controls (undo/redo buttons).
7
+ * @default true
8
+ */
9
+ showHistoryControls?: boolean;
5
10
  /**
6
11
  * Show the quick filter component.
7
12
  * @default true
@@ -74,6 +74,11 @@ process.env.NODE_ENV !== "production" ? GridToolbar.propTypes = {
74
74
  quickFilterParser: _propTypes.default.func,
75
75
  slotProps: _propTypes.default.object
76
76
  }),
77
+ /**
78
+ * Show the history controls (undo/redo buttons).
79
+ * @default true
80
+ */
81
+ showHistoryControls: _propTypes.default.bool,
77
82
  /**
78
83
  * Show the quick filter component.
79
84
  * @default true
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { GridSlotProps } from "../../models/gridSlotsComponentsProps.js";
3
3
  interface GridToolbarInternalProps {
4
- additionalItems?: React.ReactNode;
4
+ mainControls?: React.ReactNode;
5
5
  additionalExportMenuItems?: (onMenuItemClick: () => void) => React.ReactNode;
6
6
  }
7
7
  export type GridToolbarProps = GridSlotProps['toolbar'] & GridToolbarInternalProps;
@@ -31,7 +31,7 @@ var _gridClasses = require("../../constants/gridClasses");
31
31
  var _jsxRuntime = require("react/jsx-runtime");
32
32
  const _excluded = ["className"],
33
33
  _excluded2 = ["className"],
34
- _excluded3 = ["showQuickFilter", "quickFilterProps", "csvOptions", "printOptions", "additionalItems", "additionalExportMenuItems"];
34
+ _excluded3 = ["showQuickFilter", "quickFilterProps", "csvOptions", "printOptions", "mainControls", "additionalExportMenuItems"];
35
35
  const useUtilityClasses = ownerState => {
36
36
  const {
37
37
  classes
@@ -93,7 +93,7 @@ function GridToolbar(props) {
93
93
  quickFilterProps,
94
94
  csvOptions,
95
95
  printOptions,
96
- additionalItems,
96
+ mainControls,
97
97
  additionalExportMenuItems
98
98
  } = props,
99
99
  other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded3);
@@ -108,30 +108,32 @@ function GridToolbar(props) {
108
108
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Toolbar.Toolbar, (0, _extends2.default)({}, other, {
109
109
  children: [rootProps.label && /*#__PURE__*/(0, _jsxRuntime.jsx)(GridToolbarLabel, {
110
110
  children: rootProps.label
111
- }), !rootProps.disableColumnSelector && /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseTooltip, {
112
- title: apiRef.current.getLocaleText('toolbarColumns'),
113
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_columnsPanel.ColumnsPanelTrigger, {
114
- render: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarButton.ToolbarButton, {}),
115
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.columnSelectorIcon, {
116
- fontSize: "small"
111
+ }), mainControls || /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
112
+ children: [!rootProps.disableColumnSelector && /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseTooltip, {
113
+ title: apiRef.current.getLocaleText('toolbarColumns'),
114
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_columnsPanel.ColumnsPanelTrigger, {
115
+ render: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarButton.ToolbarButton, {}),
116
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.columnSelectorIcon, {
117
+ fontSize: "small"
118
+ })
117
119
  })
118
- })
119
- }), !rootProps.disableColumnFilter && /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseTooltip, {
120
- title: apiRef.current.getLocaleText('toolbarFilters'),
121
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_filterPanel.FilterPanelTrigger, {
122
- render: (triggerProps, state) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarButton.ToolbarButton, (0, _extends2.default)({}, triggerProps, {
123
- color: state.filterCount > 0 ? 'primary' : 'default',
124
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseBadge, {
125
- badgeContent: state.filterCount,
126
- color: "primary",
127
- variant: "dot",
128
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.openFilterButtonIcon, {
129
- fontSize: "small"
120
+ }), !rootProps.disableColumnFilter && /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseTooltip, {
121
+ title: apiRef.current.getLocaleText('toolbarFilters'),
122
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_filterPanel.FilterPanelTrigger, {
123
+ render: (triggerProps, state) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarButton.ToolbarButton, (0, _extends2.default)({}, triggerProps, {
124
+ color: state.filterCount > 0 ? 'primary' : 'default',
125
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseBadge, {
126
+ badgeContent: state.filterCount,
127
+ color: "primary",
128
+ variant: "dot",
129
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.openFilterButtonIcon, {
130
+ fontSize: "small"
131
+ })
130
132
  })
131
- })
132
- }))
133
- })
134
- }), additionalItems, showExportMenu && (!rootProps.disableColumnFilter || !rootProps.disableColumnSelector) && /*#__PURE__*/(0, _jsxRuntime.jsx)(GridToolbarDivider, {}), showExportMenu && /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
133
+ }))
134
+ })
135
+ })]
136
+ }), showExportMenu && (!rootProps.disableColumnFilter || !rootProps.disableColumnSelector) && /*#__PURE__*/(0, _jsxRuntime.jsx)(GridToolbarDivider, {}), showExportMenu && /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
135
137
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseTooltip, {
136
138
  title: apiRef.current.getLocaleText('toolbarExport'),
137
139
  disableInteractive: exportMenuOpen,
@@ -17,6 +17,9 @@ const GRID_DEFAULT_LOCALE_TEXT = exports.GRID_DEFAULT_LOCALE_TEXT = {
17
17
  toolbarDensityCompact: 'Compact',
18
18
  toolbarDensityStandard: 'Standard',
19
19
  toolbarDensityComfortable: 'Comfortable',
20
+ // Undo/redo toolbar button text
21
+ toolbarUndo: 'Undo',
22
+ toolbarRedo: 'Redo',
20
23
  // Columns selector toolbar button text
21
24
  toolbarColumns: 'Columns',
22
25
  toolbarColumnsLabel: 'Select columns',
@@ -1,7 +1,9 @@
1
1
  import * as React from 'react';
2
+ import { GridRowParams } from "../../models/params/gridRowParams.js";
2
3
  import { GridRenderCellParams } from "../../models/params/gridCellParams.js";
3
4
  import { GridMenuProps } from "../menu/GridMenu.js";
4
- interface GridActionsCellProps extends Omit<GridRenderCellParams, 'api'> {
5
+ import { GridValidRowModel, GridTreeNodeWithRender } from "../../models/gridRows.js";
6
+ interface GridActionsCellProps<R extends GridValidRowModel = any, V = any, F = V, N extends GridTreeNodeWithRender = GridTreeNodeWithRender> extends Omit<GridRenderCellParams<R, V, F, N>, 'api'> {
5
7
  api?: GridRenderCellParams['api'];
6
8
  position?: GridMenuProps['position'];
7
9
  children: React.ReactNode;
@@ -12,8 +14,26 @@ interface GridActionsCellProps extends Omit<GridRenderCellParams, 'api'> {
12
14
  * @default false
13
15
  */
14
16
  suppressChildrenValidation?: boolean;
17
+ /**
18
+ * Callback to fire before the menu gets opened.
19
+ * Use this callback to prevent the menu from opening.
20
+ *
21
+ * @param {GridRowParams<R>} params Row parameters.
22
+ * @param {React.MouseEvent<HTMLElement>} event The event triggering this callback.
23
+ * @returns {boolean} if the menu should be opened.
24
+ */
25
+ onMenuOpen?: (params: GridRowParams<R>, event: React.MouseEvent<HTMLElement>) => boolean;
26
+ /**
27
+ * Callback to fire before the menu gets closed.
28
+ * Use this callback to prevent the menu from closing.
29
+ *
30
+ * @param {GridRowParams<R>} params Row parameters.
31
+ * @param {React.MouseEvent<HTMLElement> | React.KeyboardEvent | MouseEvent | TouchEvent | undefined} event The event triggering this callback.
32
+ * @returns {boolean} if the menu should be closed.
33
+ */
34
+ onMenuClose?: (params: GridRowParams<R>, event: React.MouseEvent<HTMLElement> | React.KeyboardEvent | MouseEvent | TouchEvent | undefined) => boolean;
15
35
  }
16
- declare function GridActionsCell(props: GridActionsCellProps): import("react/jsx-runtime").JSX.Element;
36
+ declare function GridActionsCell<R extends GridValidRowModel = any, V = any, F = V, N extends GridTreeNodeWithRender = GridTreeNodeWithRender>(props: GridActionsCellProps<R, V, F, N>): import("react/jsx-runtime").JSX.Element;
17
37
  declare namespace GridActionsCell {
18
38
  var propTypes: any;
19
39
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
5
- const _excluded = ["api", "colDef", "id", "hasFocus", "isEditable", "field", "value", "formattedValue", "row", "rowNode", "cellMode", "tabIndex", "position", "children", "suppressChildrenValidation"];
5
+ const _excluded = ["api", "colDef", "id", "hasFocus", "isEditable", "field", "value", "formattedValue", "row", "rowNode", "cellMode", "tabIndex", "position", "onMenuOpen", "onMenuClose", "children", "suppressChildrenValidation"];
6
6
  import * as React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { useRtl } from '@mui/system/RtlProvider';
@@ -17,9 +17,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
17
  const hasActions = colDef => typeof colDef.getActions === 'function';
18
18
  function GridActionsCell(props) {
19
19
  const {
20
+ id,
20
21
  hasFocus,
21
22
  tabIndex,
22
23
  position = 'bottom-end',
24
+ onMenuOpen,
25
+ onMenuClose,
23
26
  children,
24
27
  suppressChildrenValidation
25
28
  } = props,
@@ -35,6 +38,7 @@ function GridActionsCell(props) {
35
38
  const menuId = useId();
36
39
  const buttonId = useId();
37
40
  const rootProps = useGridRootProps();
41
+ const rowParams = apiRef.current.getRowParams(id);
38
42
  const actions = [];
39
43
  React.Children.forEach(children, child => {
40
44
  // Unwrap React.Fragment
@@ -93,21 +97,27 @@ If this is intentional, you can suppress this warning by passing the \`suppressC
93
97
  setFocusedButtonIndex(numberOfButtons - 1);
94
98
  }
95
99
  }, [focusedButtonIndex, numberOfButtons]);
96
- const showMenu = () => {
100
+ const showMenu = event => {
101
+ if (onMenuOpen && !onMenuOpen(rowParams, event)) {
102
+ return;
103
+ }
97
104
  setOpen(true);
98
105
  setFocusedButtonIndex(numberOfButtons - 1);
99
106
  ignoreCallToFocus.current = true;
100
107
  };
101
- const hideMenu = () => {
108
+ const hideMenu = event => {
109
+ if (onMenuClose && !onMenuClose(rowParams, event)) {
110
+ return;
111
+ }
102
112
  setOpen(false);
103
113
  };
104
114
  const toggleMenu = event => {
105
115
  event.stopPropagation();
106
116
  event.preventDefault();
107
117
  if (open) {
108
- hideMenu();
118
+ hideMenu(event);
109
119
  } else {
110
- showMenu();
120
+ showMenu(event);
111
121
  }
112
122
  };
113
123
  const handleTouchRippleRef = index => instance => {
@@ -2,6 +2,11 @@ import { GridToolbarContainerProps } from "../containers/GridToolbarContainer.js
2
2
  import { GridToolbarExportProps } from "./GridToolbarExport.js";
3
3
  import { GridToolbarQuickFilterProps } from "./GridToolbarQuickFilter.js";
4
4
  export interface GridToolbarProps extends GridToolbarContainerProps, GridToolbarExportProps {
5
+ /**
6
+ * Show the history controls (undo/redo buttons).
7
+ * @default true
8
+ */
9
+ showHistoryControls?: boolean;
5
10
  /**
6
11
  * Show the quick filter component.
7
12
  * @default true
@@ -68,6 +68,11 @@ process.env.NODE_ENV !== "production" ? GridToolbar.propTypes = {
68
68
  quickFilterParser: PropTypes.func,
69
69
  slotProps: PropTypes.object
70
70
  }),
71
+ /**
72
+ * Show the history controls (undo/redo buttons).
73
+ * @default true
74
+ */
75
+ showHistoryControls: PropTypes.bool,
71
76
  /**
72
77
  * Show the quick filter component.
73
78
  * @default true
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { GridSlotProps } from "../../models/gridSlotsComponentsProps.js";
3
3
  interface GridToolbarInternalProps {
4
- additionalItems?: React.ReactNode;
4
+ mainControls?: React.ReactNode;
5
5
  additionalExportMenuItems?: (onMenuItemClick: () => void) => React.ReactNode;
6
6
  }
7
7
  export type GridToolbarProps = GridSlotProps['toolbar'] & GridToolbarInternalProps;
@@ -4,7 +4,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
5
5
  const _excluded = ["className"],
6
6
  _excluded2 = ["className"],
7
- _excluded3 = ["showQuickFilter", "quickFilterProps", "csvOptions", "printOptions", "additionalItems", "additionalExportMenuItems"];
7
+ _excluded3 = ["showQuickFilter", "quickFilterProps", "csvOptions", "printOptions", "mainControls", "additionalExportMenuItems"];
8
8
  import * as React from 'react';
9
9
  import PropTypes from 'prop-types';
10
10
  import useId from '@mui/utils/useId';
@@ -84,7 +84,7 @@ function GridToolbar(props) {
84
84
  quickFilterProps,
85
85
  csvOptions,
86
86
  printOptions,
87
- additionalItems,
87
+ mainControls,
88
88
  additionalExportMenuItems
89
89
  } = props,
90
90
  other = _objectWithoutPropertiesLoose(props, _excluded3);
@@ -99,30 +99,32 @@ function GridToolbar(props) {
99
99
  return /*#__PURE__*/_jsxs(Toolbar, _extends({}, other, {
100
100
  children: [rootProps.label && /*#__PURE__*/_jsx(GridToolbarLabel, {
101
101
  children: rootProps.label
102
- }), !rootProps.disableColumnSelector && /*#__PURE__*/_jsx(rootProps.slots.baseTooltip, {
103
- title: apiRef.current.getLocaleText('toolbarColumns'),
104
- children: /*#__PURE__*/_jsx(ColumnsPanelTrigger, {
105
- render: /*#__PURE__*/_jsx(ToolbarButton, {}),
106
- children: /*#__PURE__*/_jsx(rootProps.slots.columnSelectorIcon, {
107
- fontSize: "small"
102
+ }), mainControls || /*#__PURE__*/_jsxs(React.Fragment, {
103
+ children: [!rootProps.disableColumnSelector && /*#__PURE__*/_jsx(rootProps.slots.baseTooltip, {
104
+ title: apiRef.current.getLocaleText('toolbarColumns'),
105
+ children: /*#__PURE__*/_jsx(ColumnsPanelTrigger, {
106
+ render: /*#__PURE__*/_jsx(ToolbarButton, {}),
107
+ children: /*#__PURE__*/_jsx(rootProps.slots.columnSelectorIcon, {
108
+ fontSize: "small"
109
+ })
108
110
  })
109
- })
110
- }), !rootProps.disableColumnFilter && /*#__PURE__*/_jsx(rootProps.slots.baseTooltip, {
111
- title: apiRef.current.getLocaleText('toolbarFilters'),
112
- children: /*#__PURE__*/_jsx(FilterPanelTrigger, {
113
- render: (triggerProps, state) => /*#__PURE__*/_jsx(ToolbarButton, _extends({}, triggerProps, {
114
- color: state.filterCount > 0 ? 'primary' : 'default',
115
- children: /*#__PURE__*/_jsx(rootProps.slots.baseBadge, {
116
- badgeContent: state.filterCount,
117
- color: "primary",
118
- variant: "dot",
119
- children: /*#__PURE__*/_jsx(rootProps.slots.openFilterButtonIcon, {
120
- fontSize: "small"
111
+ }), !rootProps.disableColumnFilter && /*#__PURE__*/_jsx(rootProps.slots.baseTooltip, {
112
+ title: apiRef.current.getLocaleText('toolbarFilters'),
113
+ children: /*#__PURE__*/_jsx(FilterPanelTrigger, {
114
+ render: (triggerProps, state) => /*#__PURE__*/_jsx(ToolbarButton, _extends({}, triggerProps, {
115
+ color: state.filterCount > 0 ? 'primary' : 'default',
116
+ children: /*#__PURE__*/_jsx(rootProps.slots.baseBadge, {
117
+ badgeContent: state.filterCount,
118
+ color: "primary",
119
+ variant: "dot",
120
+ children: /*#__PURE__*/_jsx(rootProps.slots.openFilterButtonIcon, {
121
+ fontSize: "small"
122
+ })
121
123
  })
122
- })
123
- }))
124
- })
125
- }), additionalItems, showExportMenu && (!rootProps.disableColumnFilter || !rootProps.disableColumnSelector) && /*#__PURE__*/_jsx(GridToolbarDivider, {}), showExportMenu && /*#__PURE__*/_jsxs(React.Fragment, {
124
+ }))
125
+ })
126
+ })]
127
+ }), showExportMenu && (!rootProps.disableColumnFilter || !rootProps.disableColumnSelector) && /*#__PURE__*/_jsx(GridToolbarDivider, {}), showExportMenu && /*#__PURE__*/_jsxs(React.Fragment, {
126
128
  children: [/*#__PURE__*/_jsx(rootProps.slots.baseTooltip, {
127
129
  title: apiRef.current.getLocaleText('toolbarExport'),
128
130
  disableInteractive: exportMenuOpen,
@@ -11,6 +11,9 @@ export const GRID_DEFAULT_LOCALE_TEXT = {
11
11
  toolbarDensityCompact: 'Compact',
12
12
  toolbarDensityStandard: 'Standard',
13
13
  toolbarDensityComfortable: 'Comfortable',
14
+ // Undo/redo toolbar button text
15
+ toolbarUndo: 'Undo',
16
+ toolbarRedo: 'Redo',
14
17
  // Columns selector toolbar button text
15
18
  toolbarColumns: 'Columns',
16
19
  toolbarColumnsLabel: 'Select columns',
@@ -8,7 +8,8 @@ export const propsStateInitializer = (state, props) => {
8
8
  listView: props.listView,
9
9
  getRowId: props.getRowId,
10
10
  isCellEditable: props.isCellEditable,
11
- isRowSelectable: props.isRowSelectable
11
+ isRowSelectable: props.isRowSelectable,
12
+ dataSource: props.dataSource
12
13
  }
13
14
  });
14
15
  };
@@ -24,8 +25,9 @@ export const useGridProps = (apiRef, props) => {
24
25
  listView: props.listView,
25
26
  getRowId: props.getRowId,
26
27
  isCellEditable: props.isCellEditable,
27
- isRowSelectable: props.isRowSelectable
28
+ isRowSelectable: props.isRowSelectable,
29
+ dataSource: props.dataSource
28
30
  }
29
31
  }));
30
- }, [apiRef, props.listView, props.getRowId, props.isCellEditable, props.isRowSelectable]);
32
+ }, [apiRef, props.listView, props.getRowId, props.isCellEditable, props.isRowSelectable, props.dataSource]);
31
33
  };
@@ -9,8 +9,9 @@ import { gridClasses } from "../../../constants/gridClasses.js";
9
9
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
10
10
  import { useGridLogger } from "../../utils/useGridLogger.js";
11
11
  import { useGridEvent } from "../../utils/useGridEvent.js";
12
- import { isNavigationKey } from "../../../utils/keyboardUtils.js";
12
+ import { isNavigationKey, isPasteShortcut } from "../../../utils/keyboardUtils.js";
13
13
  import { gridFocusCellSelector, gridFocusColumnGroupHeaderSelector } from "./gridFocusStateSelector.js";
14
+ import { doesSupportPreventScroll } from "../../../utils/doesSupportPreventScroll.js";
14
15
  import { gridVisibleColumnDefinitionsSelector } from "../columns/gridColumnsSelector.js";
15
16
  import { getVisibleRows } from "../../utils/useGridVisibleRows.js";
16
17
  import { clamp } from "../../../utils/utils.js";
@@ -50,6 +51,29 @@ export const useGridFocus = (apiRef, props) => {
50
51
  const setCellFocus = React.useCallback((id, field) => {
51
52
  const focusedCell = gridFocusCellSelector(apiRef);
52
53
  if (focusedCell?.id === id && focusedCell?.field === field) {
54
+ /**
55
+ * Check if the state matches the actual DOM focus. They can get out of sync after `updateRows()` remounts the cell.
56
+ */
57
+ if (apiRef.current.getCellMode(id, field) !== 'view') {
58
+ return;
59
+ }
60
+ const cellElement = apiRef.current.getCellElement(id, field);
61
+ if (!cellElement) {
62
+ return;
63
+ }
64
+ const doc = ownerDocument(apiRef.current.rootElementRef.current);
65
+ if (cellElement.contains(doc.activeElement)) {
66
+ return;
67
+ }
68
+ if (doesSupportPreventScroll()) {
69
+ cellElement.focus({
70
+ preventScroll: true
71
+ });
72
+ } else {
73
+ const scrollPosition = apiRef.current.getScrollPosition();
74
+ cellElement.focus();
75
+ apiRef.current.scroll(scrollPosition);
76
+ }
53
77
  return;
54
78
  }
55
79
  apiRef.current.setState(state => {
@@ -224,7 +248,7 @@ export const useGridFocus = (apiRef, props) => {
224
248
  apiRef.current.setCellFocus(id, field);
225
249
  }, [apiRef]);
226
250
  const handleCellKeyDown = React.useCallback((params, event) => {
227
- if (event.key === 'Enter' || event.key === 'Tab' || event.key === 'Shift' || isNavigationKey(event.key)) {
251
+ if (isPasteShortcut(event) || event.key === 'Enter' || event.key === 'Tab' || event.key === 'Shift' || isNavigationKey(event.key)) {
228
252
  return;
229
253
  }
230
254
  apiRef.current.setCellFocus(params.id, params.field);
@@ -56,7 +56,7 @@ export const useGridScroll = (apiRef, props) => {
56
56
  }
57
57
  logger.debug(`Scrolling to cell at row ${params.rowIndex}, col: ${params.colIndex} `);
58
58
  let scrollCoordinates = {};
59
- if (params.colIndex !== undefined) {
59
+ if (params.colIndex !== undefined && visibleColumns[params.colIndex]) {
60
60
  const columnPositions = gridColumnPositionsSelector(apiRef);
61
61
  let cellWidth;
62
62
  if (typeof params.rowIndex !== 'undefined') {
@@ -73,7 +73,7 @@ export const useGridScroll = (apiRef, props) => {
73
73
  // When using RTL, `scrollLeft` becomes negative, so we must ensure that we only compare values.
74
74
  scrollCoordinates.left = scrollIntoView({
75
75
  containerSize: dimensions.viewportOuterSize.width,
76
- scrollPosition: Math.abs(virtualScrollerRef.current.scrollLeft),
76
+ scrollPosition: Math.abs(virtualScrollerRef.current?.scrollLeft ?? 0),
77
77
  elementSize: cellWidth,
78
78
  elementOffset: columnPositions[params.colIndex]
79
79
  });
@@ -86,7 +86,7 @@ export const useGridScroll = (apiRef, props) => {
86
86
  const targetOffsetHeight = rowsMeta.positions[elementIndex + 1] ? rowsMeta.positions[elementIndex + 1] - rowsMeta.positions[elementIndex] : rowsMeta.currentPageTotalHeight - rowsMeta.positions[elementIndex];
87
87
  scrollCoordinates.top = scrollIntoView({
88
88
  containerSize: dimensions.viewportInnerSize.height,
89
- scrollPosition: virtualScrollerRef.current.scrollTop,
89
+ scrollPosition: virtualScrollerRef.current?.scrollTop ?? 0,
90
90
  elementSize: targetOffsetHeight,
91
91
  elementOffset: rowsMeta.positions[elementIndex]
92
92
  });
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v8.25.0
2
+ * @mui/x-data-grid v8.26.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the