@mui/x-data-grid-pro 8.8.0 → 8.9.1

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,131 @@
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.9.1
9
+
10
+ _Jul 21, 2025_
11
+
12
+ We'd like to extend a big thank you to the 2 contributors who made this release possible. Here are some highlights ✨:
13
+
14
+ 🐞 Fix package publish issue
15
+
16
+ The following are all team members who have contributed to this release:
17
+ @KenanYusuf, @MBilalShafi
18
+
19
+ ### Data Grid
20
+
21
+ #### `@mui/x-data-grid@8.9.1`
22
+
23
+ - [DataGrid] Move conditional list view column logic into `gridVisibleColumnDefinitionsSelector` (#18724) @KenanYusuf
24
+ - [DataGrid] Fix row selection "exclude" model inconsistency (#18844) @MBilalShafi
25
+
26
+ #### `@mui/x-data-grid-pro@8.9.1` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
27
+
28
+ Same changes as in `@mui/x-data-grid@8.9.1`.
29
+
30
+ #### `@mui/x-data-grid-premium@8.9.1` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link "Premium plan")
31
+
32
+ Same changes as in `@mui/x-data-grid-pro@8.9.1`.
33
+
34
+ ### Core
35
+
36
+ - [core] Follow yml syntax convention @oliviertassinari
37
+
38
+ ## 8.9.0
39
+
40
+ _Jul 17, 2025_
41
+
42
+ We'd like to extend a big thank you to the 10 contributors who made this release possible. Here are some highlights ✨:
43
+
44
+ - ✨ Improve the drag and drop interaction for Data Grid [row reordering](https://mui.com/x/react-data-grid/row-ordering/) feature. It uses a drop indicator to point to the position the row would be moving to.
45
+
46
+ https://github.com/user-attachments/assets/37284c4f-e8d4-4fc6-a6af-a780592905ef
47
+
48
+ - 🚀 Improve Data Grid Pivoting and Aggregation performance
49
+
50
+ - 📊 Add `minBarSize` to set a minimum height for bars
51
+
52
+ Special thanks go out to the community members for their valuable contributions:
53
+ @lauri865
54
+
55
+ The following are all team members who have contributed to this release:
56
+ @alexfauquette, @arminmeh, @bernardobelchior, @flaviendelangle, @JCQuintas, @LukasTy, @mapache-salvaje, @noraleonte, @MBilalShafi
57
+
58
+ ### Data Grid
59
+
60
+ #### `@mui/x-data-grid@8.9.0`
61
+
62
+ Internal changes.
63
+
64
+ #### `@mui/x-data-grid-pro@8.9.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
65
+
66
+ Same changes as in `@mui/x-data-grid@8.9.0`, plus:
67
+
68
+ - [DataGridPro] Row reorder using drop indicator (#18627) @MBilalShafi
69
+
70
+ #### `@mui/x-data-grid-premium@8.9.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
71
+
72
+ Same changes as in `@mui/x-data-grid-pro@8.9.0`, plus:
73
+
74
+ - [DataGridPremium] Allow group column overrides with pivoting (#18765) @arminmeh
75
+ - [DataGridPremium] Support sort-dependent aggregation and improve performance (#18348) @lauri865
76
+
77
+ ### Date and Time Pickers
78
+
79
+ #### `@mui/x-date-pickers@8.9.0`
80
+
81
+ - [pickers] Avoid useless date creation in `AdapterDayjs` (#18429) @flaviendelangle
82
+ - [pickers] Fix `timeSteps` JSDoc (#18807) @LukasTy
83
+
84
+ #### `@mui/x-date-pickers-pro@8.9.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
85
+
86
+ Same changes as in `@mui/x-date-pickers@8.9.0`.
87
+
88
+ ### Charts
89
+
90
+ #### `@mui/x-charts@8.9.0`
91
+
92
+ - [charts] Add `minBarSize` to prevent bars from having 0 height (#18798) @JCQuintas
93
+ - [charts] Add click listener to radar charts (#18773) @alexfauquette
94
+ - [charts] Improve scatter chart pointer move performance (#18775) @bernardobelchior
95
+ - [charts] Simplify radar internal hooks (#18760) @alexfauquette
96
+ - [charts] `minBarSize` now ignores `0` and `null` values (#18816) @JCQuintas
97
+ - [charts] Fix y-axis tick label overlap when using log scale (#18744) @bernardobelchior
98
+ - [charts] Expose <ChartType>Series type for all chart types (#18805) @bernardobelchior
99
+
100
+ #### `@mui/x-charts-pro@8.9.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
101
+
102
+ Same changes as in `@mui/x-charts@8.9.0` plus:
103
+
104
+ [charts-pro] Fix issue where charts gestures weren't properly working when inside the shadow-dom (#18837) @JCQuintas
105
+
106
+ ### Tree View
107
+
108
+ #### `@mui/x-tree-view@8.9.0`
109
+
110
+ Internal changes.
111
+
112
+ #### `@mui/x-tree-view-pro@8.9.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
113
+
114
+ Same changes as in `@mui/x-tree-view@8.9.0`.
115
+
116
+ ### Codemod
117
+
118
+ #### `@mui/x-codemod@8.9.0`
119
+
120
+ Internal changes.
121
+
122
+ ### Docs
123
+
124
+ - [data grid][docs] Revise the Pro filter docs (#17929) @mapache-salvaje
125
+ - [charts][docs] Move mark outside clip-path (#18806) @alexfauquette
126
+
127
+ ### Miscellaneous
128
+
129
+ - [code-infra] Fix ESLint `import` restriction rule for test files (#18669) @LukasTy
130
+ - [code-infra] Remove charts benchmarks dependency on `@testing-library/jest-dom` (#18800) @bernardobelchior
131
+ - [code-infra] Remove duplicate dependency from `eslint-plugin-mui-x` (#18797) @bernardobelchior
132
+
8
133
  ## 8.8.0
9
134
 
10
135
  _Jul 11, 2025_
@@ -216,6 +341,7 @@ We'd like to extend a big thank you to the 12 contributors who made this release
216
341
  - 📅 Add `usePickerAdapter` hook to access the date adapter.
217
342
 
218
343
  You can use the adapter in your custom components if you need them to work with multiple date libraries — [Learn more](https://mui.com/x/react-date-pickers/custom-components/#access-date-adapter).
344
+
219
345
  - 🌎 Improve Danish (da-DK) locale
220
346
  - 🌎 Improve German (de-DE) locale
221
347
 
@@ -234,13 +360,13 @@ The following are all team members who have contributed to this release:
234
360
  - [l10n] Improve Danish (da-DK) locale (#18428) @ShahrazH
235
361
  - [l10n] Improve German (de-DE) locale (#18388) @omalyutin
236
362
 
237
- #### `@mui/x-data-grid-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
363
+ #### `@mui/x-data-grid-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
238
364
 
239
365
  Same changes as in `@mui/x-data-grid@8.6.0`, plus:
240
366
 
241
367
  - [DataGridPro] Fix lazy loading params calculated from rendering context (#18460) @arminmeh
242
368
 
243
- #### `@mui/x-data-grid-premium@8.6.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link "Premium plan")
369
+ #### `@mui/x-data-grid-premium@8.6.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
244
370
 
245
371
  Same changes as in `@mui/x-data-grid-pro@8.6.0`.
246
372
 
@@ -252,7 +378,7 @@ Same changes as in `@mui/x-data-grid-pro@8.6.0`.
252
378
  - [pickers] Fix to use latest `value` when updating `lastCommittedValue` in internal state (#18518) @LukasTy
253
379
  - [pickers] Use `usePickerAdapter` hook internally instead of `useUtils` (#18465) @LukasTy
254
380
 
255
- #### `@mui/x-date-pickers-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
381
+ #### `@mui/x-date-pickers-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
256
382
 
257
383
  Same changes as in `@mui/x-date-pickers@8.6.0`.
258
384
 
@@ -269,7 +395,7 @@ Same changes as in `@mui/x-date-pickers@8.6.0`.
269
395
  - [charts] Improve touch behavior for polar axis (#18531) @JCQuintas
270
396
  - [charts] Add `isElementInside` helper (#18530) @JCQuintas
271
397
 
272
- #### `@mui/x-charts-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
398
+ #### `@mui/x-charts-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
273
399
 
274
400
  Same changes as in `@mui/x-charts@8.6.0`, plus:
275
401
 
@@ -283,7 +409,7 @@ Same changes as in `@mui/x-charts@8.6.0`, plus:
283
409
 
284
410
  Internal changes.
285
411
 
286
- #### `@mui/x-tree-view-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
412
+ #### `@mui/x-tree-view-pro@8.6.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
287
413
 
288
414
  Same changes as in `@mui/x-tree-view@8.6.0`, plus:
289
415
 
@@ -1498,7 +1624,6 @@ Same changes as in `@mui/x-date-pickers@8.0.0-beta.3`.
1498
1624
 
1499
1625
  - Removed `react-spring` as a dependency of `@mui/x-charts`.
1500
1626
  A consequence of this change is that the props of some slots have been changed because the `SpringValue` wrapper has been removed. The affected slots and props are:
1501
-
1502
1627
  - the type of the `x`, `y`, `width` and `height` props of the `bar` slot are now `number`;
1503
1628
  - the type of `startAngle`, `endAngle`, `innerRadius`, `outerRadius`, `arcLabelRadius`, `cornerRadius` and `paddingAngle` props of `pieArc` and `pieArcLabel` slot are now `number`.
1504
1629
 
@@ -2066,7 +2191,6 @@ Following are all team members who have contributed to this release:
2066
2191
  - The `slots.baseFormControl` component was removed.
2067
2192
 
2068
2193
  - The "Reset" button in the column visibility panel now resets to the initial column visibility model. Previously it was reset to the model that was active at the time the panel was opened. The reset behavior follows these rules:
2069
-
2070
2194
  1. If an initial `columnVisibilityModel` is provided, it resets to that model.
2071
2195
  2. If a controlled `columnVisibilityModel` is provided, it resets to the first model value.
2072
2196
  3. When the columns are updated (via the `columns` prop or `updateColumns()` API method), the reset reference point updates to the current `columnVisibilityModel`.
@@ -2089,7 +2213,6 @@ Following are all team members who have contributed to this release:
2089
2213
  The `exclude` selection type allows to select all rows except the ones in the `ids` set.
2090
2214
 
2091
2215
  This change impacts the following props:
2092
-
2093
2216
  - `rowSelectionModel`
2094
2217
  - `onRowSelectionModelChange`
2095
2218
  - `initialState.rowSelectionModel`
@@ -2413,7 +2536,6 @@ Same changes as in `@mui/x-data-grid-pro@8.0.0-alpha.12`, plus:
2413
2536
  - The `aria-label` on the `<Clock />` component and Time Picker opening button has been fixed to rely on the set `ampm` property instead of defaulting to the user's locale.
2414
2537
 
2415
2538
  - The following unused formats have been removed from the adapters and can no longer be overridden via the `dateFormats` prop on the `<LocalizationProvider />` component:
2416
-
2417
2539
  - `fullTime` - please use `fullTime12h` and `fullTime24h` instead:
2418
2540
  ```diff
2419
2541
  <LocalizationProvider
@@ -2628,7 +2750,6 @@ Following are all team members who have contributed to this release:
2628
2750
 
2629
2751
  - `viewportInnerSize.width` now includes pinned columns' widths (fixes recursive loops in updating dimensions <-> columns)
2630
2752
  - The Data Grid now has a default background color, and its customization has moved from `theme.mixins.MuiDataGrid` to `theme.palette.DataGrid` with the following properties:
2631
-
2632
2753
  - `bg`: Sets the background color of the entire grid (new property)
2633
2754
  - `headerBg`: Sets the background color of the header (previously named `containerBackground`)
2634
2755
  - `pinnedBg`: Sets the background color of pinned rows and columns (previously named `pinnedBackground`)
@@ -2657,7 +2778,6 @@ Following are all team members who have contributed to this release:
2657
2778
  Only the initial value and the type are updated. Logic that initializes the API and its availability remained the same, which means that if you could access API in a particular line of your code before, you are able to access it as well after this change.
2658
2779
 
2659
2780
  Depending on the context in which the API is being used, you can decide what is the best way to deal with `null` value. Some options are:
2660
-
2661
2781
  - Use optional chaining
2662
2782
  - Use non-null assertion operator if you are sure your code is always executed when the `apiRef` is not `null`
2663
2783
  - Return early if `apiRef` is `null`
@@ -3181,7 +3301,6 @@ Following are all team members who have contributed to this release:
3181
3301
  #### Breaking changes
3182
3302
 
3183
3303
  - Passing additional props (like `data-*`, `aria-*`) directly on the Data Grid component is no longer supported. To pass the props, use `slotProps`:
3184
-
3185
3304
  - For `.root` element, use `slotProps.root`.
3186
3305
  - For `.main` element (the one with `role="grid"`), use `slotProps.main`.
3187
3306
 
@@ -3698,14 +3817,12 @@ Same changes as in `@mui/x-date-pickers@v8.0.0-alpha.1`, plus:
3698
3817
  #### Breaking change
3699
3818
 
3700
3819
  - The DX of the Tooltip customization has been refactored
3701
-
3702
3820
  - The `tooltip` prop has been removed in favor of `slotProps.tooltip` for consistency.
3703
3821
  - The `popper`, `axisContent`, and `itemContent` slots have been removed in favor of the `tooltip` slot which overrides the entire tooltip.
3704
3822
  - To override the tooltip content, use the `useItemTooltip` or `useAxisTooltip` hook to get the data, and wrap your component in `ChartsTooltipContainer` to follow the pointer position.
3705
3823
  - To override the tooltip placement, use the `ChartsItemTooltipContent` or `ChartsItemTooltipContent` to get default data and place them in your custom tooltip.
3706
3824
 
3707
3825
  - The library now uses the SVG `filter` attribute instead of `d3-color` for color manipulation.
3708
-
3709
3826
  - This modification impacts the `LinePlot`, `AreaPlot`, and `BarPlot` components.
3710
3827
  If you've customized the `fill` of those elements, you might need to override it by using the CSS `filter`.
3711
3828
  - The `theme.styleOverride` is removed for `MuiLineElement`, `MuiAreaElement`, and `MuiBarElement` to improve performance.
@@ -3815,14 +3932,12 @@ Same changes as in `@mui/x-data-grid-pro@8.0.0-alpha.0`, plus:
3815
3932
  #### Breaking changes
3816
3933
 
3817
3934
  - The default DOM structure of the field has changed [Learn more](https://mui.com/x/migration/migration-pickers-v7/#new-dom-structure-for-the-field).
3818
-
3819
3935
  - Before version `v8.x`, the fields' DOM structure consisted of an `<input />`, which held the whole value for the component, but unfortunately presents a few limitations in terms of accessibility when managing multiple section values.
3820
3936
  - Starting with version `v8.x`, all the field and picker components come with a new DOM structure that allows the field component to set aria attributes on individual sections, providing a far better experience with screen readers.
3821
3937
 
3822
3938
  - Some translation keys no longer require `utils` and the date object as parameters, but only the formatted value as a string. The keys affected by this changes are: `clockLabelText`, `openDatePickerDialogue` and `openTimePickerDialogue` — [Learn more](https://mui.com/x/migration/migration-pickers-v7/#stop-passing-utils-and-the-date-object-to-some-translation-keys).
3823
3939
 
3824
3940
  - The following types are no longer exported by `@mui/x-date-pickers` and `@mui/x-date-pickers-pro` — [Learn more](https://mui.com/x/migration/migration-pickers-v7/#removed-types).
3825
-
3826
3941
  - `UseDateFieldComponentProps`
3827
3942
  - `UseTimeFieldComponentProps`
3828
3943
  - `UseDateTimeFieldComponentProps`
@@ -3870,7 +3985,6 @@ Same changes as in `@mui/x-date-pickers@8.0.0-alpha.0`.
3870
3985
  - The `slots.legend` does not receive the `drawingArea` prop. You can still access your custom legend with the `useDrawingArea()` hook if your custom legend needs it.
3871
3986
 
3872
3987
  - Removed or renamed multiple props from Series — [Learn more](https://mui.com/x/migration/migration-charts-v7/#series-properties-renaming).
3873
-
3874
3988
  - The `highlighted` and `faded` properties of highlightScope have been deprecated in favor of `highlight` and `fade`.
3875
3989
  The deprecated ones are now removed.
3876
3990
  - The `xAxisKey`, `yAxisKey`, and `zAxisKey` properties have been deprecated in favor of `xAxisId`, `yAxisId`, and `zAxisId`.
@@ -6134,7 +6248,6 @@ We'd like to offer a big thanks to the 12 contributors who made this release pos
6134
6248
  <img width="287" src="https://github.com/user-attachments/assets/78bd83c5-7ce4-4ed7-acf9-be70b2dbce54" alt="Item reordering using drag and drop" />
6135
6249
 
6136
6250
  - 📦 Support CommonJS bundle out of the box on `@mui/x-charts` by adding vendored D3 dependencies.
6137
-
6138
6251
  - This modifies how the package imports D3.js. It will impact you if you use `d3` packages installed by `@mui/x-charts` and don't have them in your `package.json`. You shouldn't be affected otherwise.
6139
6252
  - For more context, the initial issue is caused by D3 only exporting ESM.
6140
6253
 
@@ -7669,7 +7782,6 @@ We'd like to offer a big thanks to the 12 contributors who made this release pos
7669
7782
  #### Breaking changes
7670
7783
 
7671
7784
  - The `density` is a [controlled prop](https://mui.com/x/react-data-grid/accessibility/#set-the-density-programmatically) now, if you were previously passing the `density` prop to the Data Grid, you will need to do one of the following:
7672
-
7673
7785
  1. Move it to the `initialState.density` to initialize it.
7674
7786
 
7675
7787
  ```diff
@@ -8175,7 +8287,6 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.4`.
8175
8287
  ```
8176
8288
 
8177
8289
  - The following internal types were exported by mistake and have been removed from the public API:
8178
-
8179
8290
  - `UseDateFieldDefaultizedProps`
8180
8291
  - `UseTimeFieldDefaultizedProps`
8181
8292
  - `UseDateTimeFieldDefaultizedProps`
@@ -8416,7 +8527,6 @@ We'd like to offer a big thanks to the 12 contributors who made this release pos
8416
8527
  - 🎁 The Line Chart component now has animation by default (#11620) @alexfauquette
8417
8528
  - 🚀 All charts have click handlers (#11411) @alexfauquette
8418
8529
  Test their respective documentation demonstrations to know more about the data format:
8419
-
8420
8530
  - [Scatter Chart](https://v7.mui.com/x/react-charts/scatter/#click-event)
8421
8531
  - [Line Chart](https://v7.mui.com/x/react-charts/lines/#click-event)
8422
8532
  - [Bar Chart](https://v7.mui.com/x/react-charts/bars/#click-event)
@@ -8696,7 +8806,6 @@ We'd like to offer a big thanks to the 11 contributors who made this release pos
8696
8806
  ```
8697
8807
 
8698
8808
  The most notable changes that might affect your application or tests are:
8699
-
8700
8809
  - The `role="grid"` attribute along with related ARIA attributes are now applied to the inner `div` element instead of the root `div` element:
8701
8810
 
8702
8811
  ```diff
@@ -9247,7 +9356,6 @@ We'd like to offer a big thanks to the 6 contributors who made this release poss
9247
9356
 
9248
9357
  - The filter panel no longer uses the native version of the [`Select`](https://mui.com/material-ui/react-select/) component for all components.
9249
9358
  - The `getOptionValue` and `getOptionLabel` props were removed from the following components:
9250
-
9251
9359
  - `GridEditSingleSelectCell`
9252
9360
  - `GridFilterInputSingleSelect`
9253
9361
  - `GridFilterInputMultipleSingleSelect`
@@ -9661,7 +9769,6 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.3`, plus:
9661
9769
  - Add new parameters to the `shortcuts` slot `onChange` callback
9662
9770
 
9663
9771
  The `onChange` callback fired when selecting a shortcut now requires two new parameters (previously they were optional):
9664
-
9665
9772
  - The [`changeImportance`](/x/react-date-pickers/shortcuts/#behavior-when-selecting-a-shortcut) of the shortcut.
9666
9773
  - The `item` containing the entire shortcut object.
9667
9774
 
@@ -29,7 +29,7 @@ const configuration = {
29
29
  useCellAggregationResult: () => null
30
30
  }
31
31
  };
32
- const releaseInfo = "MTc1MjE4NDgwMDAwMA==";
32
+ const releaseInfo = "MTc1MzAzODAwMDAwMA==";
33
33
  const watermark = /*#__PURE__*/(0, _jsxRuntime.jsx)(_xLicense.Watermark, {
34
34
  packageName: "x-data-grid-pro",
35
35
  releaseInfo: releaseInfo
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
+ 'use client';
2
3
 
4
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
3
5
  Object.defineProperty(exports, "__esModule", {
4
6
  value: true
5
7
  });
6
8
  exports.useDataGridProComponent = void 0;
9
+ var React = _interopRequireWildcard(require("react"));
7
10
  var _internals = require("@mui/x-data-grid/internals");
8
11
  var _useGridInfiniteLoader = require("../hooks/features/infiniteLoader/useGridInfiniteLoader");
9
12
  var _useGridColumnReorder = require("../hooks/features/columnReorder/useGridColumnReorder");
@@ -114,5 +117,10 @@ const useDataGridProComponent = (apiRef, props) => {
114
117
  (0, _internals.useGridVirtualization)(apiRef, props);
115
118
  (0, _useGridDataSourcePro.useGridDataSourcePro)(apiRef, props);
116
119
  (0, _internals.useGridListView)(apiRef, props);
120
+
121
+ // Should be the last thing to run, because all pre-processors should have been registered by now.
122
+ React.useEffect(() => {
123
+ apiRef.current.runAppliersForPendingProcessors();
124
+ });
117
125
  };
118
126
  exports.useDataGridProComponent = useDataGridProComponent;
@@ -42,6 +42,8 @@ function GridRowReorderCell(params) {
42
42
  const editRowsState = (0, _xDataGrid.useGridSelector)(apiRef, _internals.gridEditRowsStateSelector);
43
43
  // eslint-disable-next-line no-underscore-dangle
44
44
  const cellValue = params.row.__reorder__ || params.id;
45
+ const cellRef = React.useRef(null);
46
+ const listenerNodeRef = React.useRef(null);
45
47
 
46
48
  // TODO: remove sortModel and treeDepth checks once row reorder is compatible
47
49
  const isDraggable = React.useMemo(() => !!rootProps.rowReordering && !sortModel.length && treeDepth === 1 && Object.keys(editRowsState).length === 0, [rootProps.rowReordering, sortModel, treeDepth, editRowsState]);
@@ -74,12 +76,24 @@ function GridRowReorderCell(params) {
74
76
  }, [apiRef]);
75
77
  const handleDragEnd = React.useCallback(event => {
76
78
  handleMouseUp();
77
- publish('rowDragEnd')(event);
78
- }, [publish, handleMouseUp]);
79
+ if (apiRef.current.getRow(params.id)) {
80
+ apiRef.current.publishEvent('rowDragEnd', apiRef.current.getRowParams(params.id), event);
81
+ }
82
+ listenerNodeRef.current.removeEventListener('dragend', handleDragEnd);
83
+ listenerNodeRef.current = null;
84
+ }, [apiRef, params.id, handleMouseUp]);
85
+ const handleDragStart = React.useCallback(event => {
86
+ if (!cellRef.current) {
87
+ return;
88
+ }
89
+ publish('rowDragStart')(event);
90
+ cellRef.current.addEventListener('dragend', handleDragEnd);
91
+ // cache the node to remove the listener when the drag ends
92
+ listenerNodeRef.current = cellRef.current;
93
+ }, [publish, handleDragEnd]);
79
94
  const draggableEventHandlers = isDraggable ? {
80
- onDragStart: publish('rowDragStart'),
95
+ onDragStart: handleDragStart,
81
96
  onDragOver: publish('rowDragOver'),
82
- onDragEnd: handleDragEnd,
83
97
  onMouseDown: handleMouseDown,
84
98
  onMouseUp: handleMouseUp
85
99
  } : null;
@@ -87,6 +101,7 @@ function GridRowReorderCell(params) {
87
101
  return null;
88
102
  }
89
103
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", (0, _extends2.default)({
104
+ ref: cellRef,
90
105
  className: classes.root,
91
106
  draggable: isDraggable
92
107
  }, draggableEventHandlers, {
@@ -22,7 +22,7 @@ const configuration = {
22
22
  useCellAggregationResult: () => null
23
23
  }
24
24
  };
25
- const releaseInfo = "MTc1MjE4NDgwMDAwMA==";
25
+ const releaseInfo = "MTc1MzAzODAwMDAwMA==";
26
26
  const watermark = /*#__PURE__*/_jsx(Watermark, {
27
27
  packageName: "x-data-grid-pro",
28
28
  releaseInfo: releaseInfo
@@ -1,3 +1,6 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
1
4
  import { useGridInitialization, useGridInitializeState, useGridClipboard, useGridColumnMenu, useGridColumns, columnsStateInitializer, useGridDensity, useGridCsvExport, useGridPrintExport, useGridFilter, filterStateInitializer, useGridFocus, useGridKeyboardNavigation, useGridPagination, paginationStateInitializer, useGridPreferencesPanel, useGridEditing, editingStateInitializer, useGridRows, useGridRowsPreProcessors, rowsStateInitializer, useGridRowsMeta, useGridParamsApi, useGridRowSelection, useGridSorting, sortingStateInitializer, useGridScroll, useGridEvents, dimensionsStateInitializer, useGridDimensions, useGridStatePersistence, useGridRowSelectionPreProcessors, useGridColumnSpanning, columnMenuStateInitializer, densityStateInitializer, focusStateInitializer, preferencePanelStateInitializer, rowsMetaStateInitializer, rowSelectionStateInitializer, useGridColumnGrouping, columnGroupsStateInitializer, headerFilteringStateInitializer, useGridHeaderFiltering, virtualizationStateInitializer, useGridVirtualization, useGridColumnResize, columnResizeStateInitializer, useGridRowSpanning, rowSpanningStateInitializer, useGridListView, listViewStateInitializer, propsStateInitializer } from '@mui/x-data-grid/internals';
2
5
  // Pro-only features
3
6
  import { useGridInfiniteLoader } from "../hooks/features/infiniteLoader/useGridInfiniteLoader.js";
@@ -107,4 +110,9 @@ export const useDataGridProComponent = (apiRef, props) => {
107
110
  useGridVirtualization(apiRef, props);
108
111
  useGridDataSource(apiRef, props);
109
112
  useGridListView(apiRef, props);
113
+
114
+ // Should be the last thing to run, because all pre-processors should have been registered by now.
115
+ React.useEffect(() => {
116
+ apiRef.current.runAppliersForPendingProcessors();
117
+ });
110
118
  };
@@ -33,6 +33,8 @@ function GridRowReorderCell(params) {
33
33
  const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector);
34
34
  // eslint-disable-next-line no-underscore-dangle
35
35
  const cellValue = params.row.__reorder__ || params.id;
36
+ const cellRef = React.useRef(null);
37
+ const listenerNodeRef = React.useRef(null);
36
38
 
37
39
  // TODO: remove sortModel and treeDepth checks once row reorder is compatible
38
40
  const isDraggable = React.useMemo(() => !!rootProps.rowReordering && !sortModel.length && treeDepth === 1 && Object.keys(editRowsState).length === 0, [rootProps.rowReordering, sortModel, treeDepth, editRowsState]);
@@ -65,12 +67,24 @@ function GridRowReorderCell(params) {
65
67
  }, [apiRef]);
66
68
  const handleDragEnd = React.useCallback(event => {
67
69
  handleMouseUp();
68
- publish('rowDragEnd')(event);
69
- }, [publish, handleMouseUp]);
70
+ if (apiRef.current.getRow(params.id)) {
71
+ apiRef.current.publishEvent('rowDragEnd', apiRef.current.getRowParams(params.id), event);
72
+ }
73
+ listenerNodeRef.current.removeEventListener('dragend', handleDragEnd);
74
+ listenerNodeRef.current = null;
75
+ }, [apiRef, params.id, handleMouseUp]);
76
+ const handleDragStart = React.useCallback(event => {
77
+ if (!cellRef.current) {
78
+ return;
79
+ }
80
+ publish('rowDragStart')(event);
81
+ cellRef.current.addEventListener('dragend', handleDragEnd);
82
+ // cache the node to remove the listener when the drag ends
83
+ listenerNodeRef.current = cellRef.current;
84
+ }, [publish, handleDragEnd]);
70
85
  const draggableEventHandlers = isDraggable ? {
71
- onDragStart: publish('rowDragStart'),
86
+ onDragStart: handleDragStart,
72
87
  onDragOver: publish('rowDragOver'),
73
- onDragEnd: handleDragEnd,
74
88
  onMouseDown: handleMouseDown,
75
89
  onMouseUp: handleMouseUp
76
90
  } : null;
@@ -78,6 +92,7 @@ function GridRowReorderCell(params) {
78
92
  return null;
79
93
  }
80
94
  return /*#__PURE__*/_jsxs("div", _extends({
95
+ ref: cellRef,
81
96
  className: classes.root,
82
97
  draggable: isDraggable
83
98
  }, draggableEventHandlers, {
@@ -10,17 +10,20 @@ var Direction = /*#__PURE__*/function (Direction) {
10
10
  Direction[Direction["DOWN"] = 1] = "DOWN";
11
11
  return Direction;
12
12
  }(Direction || {});
13
- let previousMousePosition = null;
14
- let previousReorderState = {
13
+ const EMPTY_REORDER_STATE = {
15
14
  previousTargetId: null,
16
- dragDirection: null
15
+ dragDirection: null,
16
+ previousDropPosition: null
17
17
  };
18
18
  const useUtilityClasses = ownerState => {
19
19
  const {
20
20
  classes
21
21
  } = ownerState;
22
22
  const slots = {
23
- rowDragging: ['row--dragging']
23
+ rowDragging: ['row--dragging'],
24
+ rowDropAbove: ['row--dropAbove'],
25
+ rowDropBelow: ['row--dropBelow'],
26
+ rowBeingDragged: ['row--beingDragged']
24
27
  };
25
28
  return composeClasses(slots, getDataGridUtilityClass, classes);
26
29
  };
@@ -36,12 +39,19 @@ export const useGridRowReorder = (apiRef, props) => {
36
39
  const dragRowNode = React.useRef(null);
37
40
  const originRowIndex = React.useRef(null);
38
41
  const removeDnDStylesTimeout = React.useRef(undefined);
42
+ const previousDropIndicatorRef = React.useRef(null);
39
43
  const ownerState = {
40
44
  classes: props.classes
41
45
  };
42
46
  const classes = useUtilityClasses(ownerState);
43
47
  const [dragRowId, setDragRowId] = React.useState('');
44
48
  const sortedRowIndexLookup = useGridSelector(apiRef, gridSortedRowIndexLookupSelector);
49
+ const previousReorderState = React.useRef(EMPTY_REORDER_STATE);
50
+ const [dropTarget, setDropTarget] = React.useState({
51
+ targetRowId: null,
52
+ targetRowIndex: null,
53
+ dropPosition: null
54
+ });
45
55
  React.useEffect(() => {
46
56
  return () => {
47
57
  clearTimeout(removeDnDStylesTimeout.current);
@@ -53,6 +63,86 @@ export const useGridRowReorder = (apiRef, props) => {
53
63
  const isRowReorderDisabled = React.useMemo(() => {
54
64
  return !props.rowReordering || !!sortModel.length || treeDepth !== 1;
55
65
  }, [props.rowReordering, sortModel, treeDepth]);
66
+ const applyDropIndicator = React.useCallback((targetRowId, position) => {
67
+ // Remove existing drop indicator from previous target
68
+ if (previousDropIndicatorRef.current) {
69
+ previousDropIndicatorRef.current.classList.remove(classes.rowDropAbove, classes.rowDropBelow);
70
+ previousDropIndicatorRef.current = null;
71
+ }
72
+
73
+ // Apply new drop indicator
74
+ if (targetRowId && position) {
75
+ const targetRow = apiRef.current.rootElementRef?.current?.querySelector(`[data-id="${targetRowId}"]`);
76
+ if (targetRow) {
77
+ targetRow.classList.add(position === 'above' ? classes.rowDropAbove : classes.rowDropBelow);
78
+ previousDropIndicatorRef.current = targetRow;
79
+ }
80
+ }
81
+ }, [apiRef, classes]);
82
+ const applyDraggedState = React.useCallback((rowId, isDragged) => {
83
+ if (rowId) {
84
+ const draggedRow = apiRef.current.rootElementRef?.current?.querySelector(`[data-id="${rowId}"]`);
85
+ if (draggedRow) {
86
+ if (isDragged) {
87
+ draggedRow.classList.add(classes.rowBeingDragged);
88
+ } else {
89
+ draggedRow.classList.remove(classes.rowBeingDragged);
90
+ }
91
+ }
92
+ }
93
+ }, [apiRef, classes.rowBeingDragged]);
94
+ const applyRowAnimation = React.useCallback(callback => {
95
+ const rootElement = apiRef.current.rootElementRef?.current;
96
+ if (!rootElement) {
97
+ return;
98
+ }
99
+ const visibleRows = rootElement.querySelectorAll('[data-id]');
100
+ if (!visibleRows.length) {
101
+ return;
102
+ }
103
+ const rowsArray = Array.from(visibleRows);
104
+ const initialPositions = new Map();
105
+ rowsArray.forEach(row => {
106
+ const rowId = row.getAttribute('data-id');
107
+ if (rowId) {
108
+ initialPositions.set(rowId, row.getBoundingClientRect());
109
+ }
110
+ });
111
+ callback();
112
+
113
+ // Use `requestAnimationFrame` to ensure DOM has updated
114
+ requestAnimationFrame(() => {
115
+ const newRows = rootElement.querySelectorAll('[data-id]');
116
+ const animations = [];
117
+ newRows.forEach(row => {
118
+ const rowId = row.getAttribute('data-id');
119
+ if (!rowId) {
120
+ return;
121
+ }
122
+ const prevRect = initialPositions.get(rowId);
123
+ if (!prevRect) {
124
+ return;
125
+ }
126
+ const currentRect = row.getBoundingClientRect();
127
+ const deltaY = prevRect.top - currentRect.top;
128
+ if (Math.abs(deltaY) > 1) {
129
+ const animation = row.animate([{
130
+ transform: `translateY(${deltaY}px)`
131
+ }, {
132
+ transform: 'translateY(0)'
133
+ }], {
134
+ duration: 200,
135
+ easing: 'ease-in-out',
136
+ fill: 'forwards'
137
+ });
138
+ animations.push(animation);
139
+ }
140
+ });
141
+ if (animations.length > 0) {
142
+ Promise.allSettled(animations.map(a => a.finished)).then(() => {});
143
+ }
144
+ });
145
+ }, [apiRef]);
56
146
  const handleDragStart = React.useCallback((params, event) => {
57
147
  // Call the gridEditRowsStateSelector directly to avoid infnite loop
58
148
  const editRowsState = gridEditRowsStateSelector(apiRef);
@@ -64,47 +154,91 @@ export const useGridRowReorder = (apiRef, props) => {
64
154
  // For more information check here https://github.com/mui/mui-x/issues/2680.
65
155
  event.stopPropagation();
66
156
  dragRowNode.current = event.currentTarget;
157
+ // Apply cell-level dragging class to the drag handle
67
158
  dragRowNode.current.classList.add(classes.rowDragging);
68
159
  setDragRowId(params.id);
160
+
161
+ // Apply the dragged state to the entire row
162
+ applyDraggedState(params.id, true);
69
163
  removeDnDStylesTimeout.current = setTimeout(() => {
70
164
  dragRowNode.current.classList.remove(classes.rowDragging);
71
165
  });
72
166
  originRowIndex.current = sortedRowIndexLookup[params.id];
73
167
  apiRef.current.setCellFocus(params.id, GRID_REORDER_COL_DEF.field);
74
- }, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, sortedRowIndexLookup]);
168
+ }, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, sortedRowIndexLookup, applyDraggedState]);
75
169
  const handleDragOver = React.useCallback((params, event) => {
76
170
  if (dragRowId === '') {
77
171
  return;
78
172
  }
79
173
  const rowNode = gridRowNodeSelector(apiRef, params.id);
80
- if (!rowNode || rowNode.type === 'footer' || rowNode.type === 'pinnedRow') {
174
+ if (!rowNode || rowNode.type === 'footer' || rowNode.type === 'pinnedRow' || !event.target) {
81
175
  return;
82
176
  }
177
+
178
+ // Find the relative 'y' mouse position based on the event.target
179
+ const targetRect = event.target.getBoundingClientRect();
180
+ const relativeY = Math.floor(event.clientY - targetRect.top);
181
+ const midPoint = Math.floor(targetRect.height / 2);
83
182
  logger.debug(`Dragging over row ${params.id}`);
84
183
  event.preventDefault();
85
184
  // Prevent drag events propagation.
86
185
  // For more information check here https://github.com/mui/mui-x/issues/2680.
87
186
  event.stopPropagation();
88
- const mouseMovementDiff = previousMousePosition ? previousMousePosition.y - event.clientY : event.clientY;
89
187
  if (params.id !== dragRowId) {
90
188
  const targetRowIndex = sortedRowIndexLookup[params.id];
91
- const dragDirection = mouseMovementDiff > 0 ? Direction.DOWN : Direction.UP;
189
+ const sourceRowIndex = sortedRowIndexLookup[dragRowId];
190
+
191
+ // Determine drop position based on relativeY position within the row
192
+ const dropPosition = relativeY < midPoint ? 'above' : 'below';
193
+
194
+ // Check if this drop would result in no actual movement
195
+ const wouldResultInNoMovement = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 ||
196
+ // dragging to immediately below (above next row)
197
+ dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1; // dragging to immediately above (below previous row)
198
+
92
199
  const currentReorderState = {
93
- dragDirection,
94
- previousTargetId: params.id
200
+ dragDirection: targetRowIndex < sourceRowIndex ? Direction.UP : Direction.DOWN,
201
+ previousTargetId: params.id,
202
+ previousDropPosition: dropPosition
95
203
  };
96
- const isStateChanged = currentReorderState.dragDirection !== previousReorderState.dragDirection || currentReorderState.previousTargetId !== previousReorderState.previousTargetId;
97
- if (previousReorderState.dragDirection === null || Math.abs(mouseMovementDiff) >= 1 && isStateChanged) {
98
- apiRef.current.setRowIndex(dragRowId, targetRowIndex);
99
- previousReorderState = currentReorderState;
204
+
205
+ // Only update visual indicator:
206
+ // 1. When dragging over a different row
207
+ // 2. When it would result in actual movement
208
+ if (previousReorderState.current.previousTargetId !== params.id || previousReorderState.current.previousDropPosition !== dropPosition) {
209
+ if (wouldResultInNoMovement) {
210
+ // Clear any existing indicators since this wouldn't result in movement
211
+ setDropTarget({
212
+ targetRowId: null,
213
+ targetRowIndex: null,
214
+ dropPosition: null
215
+ });
216
+ applyDropIndicator(null, null);
217
+ } else {
218
+ setDropTarget({
219
+ targetRowId: params.id,
220
+ targetRowIndex,
221
+ dropPosition
222
+ });
223
+ applyDropIndicator(params.id, dropPosition);
224
+ }
225
+ previousReorderState.current = currentReorderState;
100
226
  }
227
+ } else if (previousReorderState.current.previousTargetId !== null) {
228
+ setDropTarget({
229
+ targetRowId: null,
230
+ targetRowIndex: null,
231
+ dropPosition: null
232
+ });
233
+ applyDropIndicator(null, null);
234
+ previousReorderState.current = {
235
+ previousTargetId: null,
236
+ dragDirection: null,
237
+ previousDropPosition: null
238
+ };
101
239
  }
102
- previousMousePosition = {
103
- x: event.clientX,
104
- y: event.clientY
105
- };
106
- }, [dragRowId, apiRef, logger, sortedRowIndexLookup]);
107
- const handleDragEnd = React.useCallback((params, event) => {
240
+ }, [dragRowId, apiRef, logger, sortedRowIndexLookup, applyDropIndicator]);
241
+ const handleDragEnd = React.useCallback((_, event) => {
108
242
  // Call the gridEditRowsStateSelector directly to avoid infnite loop
109
243
  const editRowsState = gridEditRowsStateSelector(apiRef);
110
244
  if (dragRowId === '' || isRowReorderDisabled || Object.keys(editRowsState).length !== 0) {
@@ -117,24 +251,65 @@ export const useGridRowReorder = (apiRef, props) => {
117
251
  event.stopPropagation();
118
252
  clearTimeout(removeDnDStylesTimeout.current);
119
253
  dragRowNode.current = null;
120
- previousReorderState.dragDirection = null;
254
+ const dragDirection = previousReorderState.current.dragDirection;
255
+ previousReorderState.current = EMPTY_REORDER_STATE;
256
+
257
+ // Clear visual indicators and dragged state
258
+ applyDropIndicator(null, null);
259
+ applyDraggedState(dragRowId, false);
121
260
 
122
261
  // Check if the row was dropped outside the grid.
123
- if (event.dataTransfer.dropEffect === 'none') {
124
- // Accessing params.field may contain the wrong field as header elements are reused
125
- apiRef.current.setRowIndex(dragRowId, originRowIndex.current);
262
+ if (!event.dataTransfer || event.dataTransfer.dropEffect === 'none') {
263
+ // Reset drop target state
264
+ setDropTarget({
265
+ targetRowId: null,
266
+ targetRowIndex: null,
267
+ dropPosition: null
268
+ });
126
269
  originRowIndex.current = null;
127
270
  } else {
128
- // Emit the rowOrderChange event only once when the reordering stops.
129
- const rowOrderChangeParams = {
130
- row: apiRef.current.getRow(dragRowId),
131
- targetIndex: sortedRowIndexLookup[params.id],
132
- oldIndex: originRowIndex.current
133
- };
134
- apiRef.current.publishEvent('rowOrderChange', rowOrderChangeParams);
271
+ if (dropTarget.targetRowIndex !== null && dropTarget.targetRowId !== null) {
272
+ const sourceRowIndex = originRowIndex.current;
273
+ const targetRowIndex = dropTarget.targetRowIndex;
274
+ const dropPosition = dropTarget.dropPosition;
275
+
276
+ // Calculate the correct target index based on drop position
277
+ let finalTargetIndex;
278
+ if (dragDirection === Direction.UP) {
279
+ finalTargetIndex = dropPosition === 'above' ? targetRowIndex : targetRowIndex + 1;
280
+ } else {
281
+ finalTargetIndex = dropPosition === 'above' ? targetRowIndex - 1 : targetRowIndex;
282
+ }
283
+ const isReorderInvalid = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 ||
284
+ // dragging to immediately below (above next row)
285
+ dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1 ||
286
+ // dragging to immediately above (below previous row)
287
+ dropTarget.targetRowId === dragRowId; // dragging to the same row
288
+
289
+ if (!isReorderInvalid) {
290
+ applyRowAnimation(() => {
291
+ apiRef.current.setRowIndex(dragRowId, finalTargetIndex);
292
+
293
+ // Emit the rowOrderChange event only once when the reordering stops.
294
+ const rowOrderChangeParams = {
295
+ row: apiRef.current.getRow(dragRowId),
296
+ targetIndex: finalTargetIndex,
297
+ oldIndex: sourceRowIndex
298
+ };
299
+ apiRef.current.publishEvent('rowOrderChange', rowOrderChangeParams);
300
+ });
301
+ }
302
+ }
303
+
304
+ // Reset drop target state
305
+ setDropTarget({
306
+ targetRowId: null,
307
+ targetRowIndex: null,
308
+ dropPosition: null
309
+ });
135
310
  }
136
311
  setDragRowId('');
137
- }, [apiRef, dragRowId, isRowReorderDisabled, logger, sortedRowIndexLookup]);
312
+ }, [apiRef, dragRowId, isRowReorderDisabled, logger, dropTarget, applyDropIndicator, applyDraggedState, applyRowAnimation]);
138
313
  useGridEvent(apiRef, 'rowDragStart', handleDragStart);
139
314
  useGridEvent(apiRef, 'rowDragOver', handleDragOver);
140
315
  useGridEvent(apiRef, 'rowDragEnd', handleDragEnd);
package/esm/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @mui/x-data-grid-pro v8.8.0
2
+ * @mui/x-data-grid-pro v8.9.1
3
3
  *
4
- * @license MUI X Commercial
5
- * This source code is licensed under the commercial license found in the
4
+ * @license SEE LICENSE IN LICENSE
5
+ * This source code is licensed under the SEE LICENSE IN LICENSE license found in the
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  import "./typeOverloads/index.js";
@@ -17,17 +17,20 @@ var Direction = /*#__PURE__*/function (Direction) {
17
17
  Direction[Direction["DOWN"] = 1] = "DOWN";
18
18
  return Direction;
19
19
  }(Direction || {});
20
- let previousMousePosition = null;
21
- let previousReorderState = {
20
+ const EMPTY_REORDER_STATE = {
22
21
  previousTargetId: null,
23
- dragDirection: null
22
+ dragDirection: null,
23
+ previousDropPosition: null
24
24
  };
25
25
  const useUtilityClasses = ownerState => {
26
26
  const {
27
27
  classes
28
28
  } = ownerState;
29
29
  const slots = {
30
- rowDragging: ['row--dragging']
30
+ rowDragging: ['row--dragging'],
31
+ rowDropAbove: ['row--dropAbove'],
32
+ rowDropBelow: ['row--dropBelow'],
33
+ rowBeingDragged: ['row--beingDragged']
31
34
  };
32
35
  return (0, _composeClasses.default)(slots, _xDataGrid.getDataGridUtilityClass, classes);
33
36
  };
@@ -43,12 +46,19 @@ const useGridRowReorder = (apiRef, props) => {
43
46
  const dragRowNode = React.useRef(null);
44
47
  const originRowIndex = React.useRef(null);
45
48
  const removeDnDStylesTimeout = React.useRef(undefined);
49
+ const previousDropIndicatorRef = React.useRef(null);
46
50
  const ownerState = {
47
51
  classes: props.classes
48
52
  };
49
53
  const classes = useUtilityClasses(ownerState);
50
54
  const [dragRowId, setDragRowId] = React.useState('');
51
55
  const sortedRowIndexLookup = (0, _xDataGrid.useGridSelector)(apiRef, _internals.gridSortedRowIndexLookupSelector);
56
+ const previousReorderState = React.useRef(EMPTY_REORDER_STATE);
57
+ const [dropTarget, setDropTarget] = React.useState({
58
+ targetRowId: null,
59
+ targetRowIndex: null,
60
+ dropPosition: null
61
+ });
52
62
  React.useEffect(() => {
53
63
  return () => {
54
64
  clearTimeout(removeDnDStylesTimeout.current);
@@ -60,6 +70,86 @@ const useGridRowReorder = (apiRef, props) => {
60
70
  const isRowReorderDisabled = React.useMemo(() => {
61
71
  return !props.rowReordering || !!sortModel.length || treeDepth !== 1;
62
72
  }, [props.rowReordering, sortModel, treeDepth]);
73
+ const applyDropIndicator = React.useCallback((targetRowId, position) => {
74
+ // Remove existing drop indicator from previous target
75
+ if (previousDropIndicatorRef.current) {
76
+ previousDropIndicatorRef.current.classList.remove(classes.rowDropAbove, classes.rowDropBelow);
77
+ previousDropIndicatorRef.current = null;
78
+ }
79
+
80
+ // Apply new drop indicator
81
+ if (targetRowId && position) {
82
+ const targetRow = apiRef.current.rootElementRef?.current?.querySelector(`[data-id="${targetRowId}"]`);
83
+ if (targetRow) {
84
+ targetRow.classList.add(position === 'above' ? classes.rowDropAbove : classes.rowDropBelow);
85
+ previousDropIndicatorRef.current = targetRow;
86
+ }
87
+ }
88
+ }, [apiRef, classes]);
89
+ const applyDraggedState = React.useCallback((rowId, isDragged) => {
90
+ if (rowId) {
91
+ const draggedRow = apiRef.current.rootElementRef?.current?.querySelector(`[data-id="${rowId}"]`);
92
+ if (draggedRow) {
93
+ if (isDragged) {
94
+ draggedRow.classList.add(classes.rowBeingDragged);
95
+ } else {
96
+ draggedRow.classList.remove(classes.rowBeingDragged);
97
+ }
98
+ }
99
+ }
100
+ }, [apiRef, classes.rowBeingDragged]);
101
+ const applyRowAnimation = React.useCallback(callback => {
102
+ const rootElement = apiRef.current.rootElementRef?.current;
103
+ if (!rootElement) {
104
+ return;
105
+ }
106
+ const visibleRows = rootElement.querySelectorAll('[data-id]');
107
+ if (!visibleRows.length) {
108
+ return;
109
+ }
110
+ const rowsArray = Array.from(visibleRows);
111
+ const initialPositions = new Map();
112
+ rowsArray.forEach(row => {
113
+ const rowId = row.getAttribute('data-id');
114
+ if (rowId) {
115
+ initialPositions.set(rowId, row.getBoundingClientRect());
116
+ }
117
+ });
118
+ callback();
119
+
120
+ // Use `requestAnimationFrame` to ensure DOM has updated
121
+ requestAnimationFrame(() => {
122
+ const newRows = rootElement.querySelectorAll('[data-id]');
123
+ const animations = [];
124
+ newRows.forEach(row => {
125
+ const rowId = row.getAttribute('data-id');
126
+ if (!rowId) {
127
+ return;
128
+ }
129
+ const prevRect = initialPositions.get(rowId);
130
+ if (!prevRect) {
131
+ return;
132
+ }
133
+ const currentRect = row.getBoundingClientRect();
134
+ const deltaY = prevRect.top - currentRect.top;
135
+ if (Math.abs(deltaY) > 1) {
136
+ const animation = row.animate([{
137
+ transform: `translateY(${deltaY}px)`
138
+ }, {
139
+ transform: 'translateY(0)'
140
+ }], {
141
+ duration: 200,
142
+ easing: 'ease-in-out',
143
+ fill: 'forwards'
144
+ });
145
+ animations.push(animation);
146
+ }
147
+ });
148
+ if (animations.length > 0) {
149
+ Promise.allSettled(animations.map(a => a.finished)).then(() => {});
150
+ }
151
+ });
152
+ }, [apiRef]);
63
153
  const handleDragStart = React.useCallback((params, event) => {
64
154
  // Call the gridEditRowsStateSelector directly to avoid infnite loop
65
155
  const editRowsState = (0, _internals.gridEditRowsStateSelector)(apiRef);
@@ -71,47 +161,91 @@ const useGridRowReorder = (apiRef, props) => {
71
161
  // For more information check here https://github.com/mui/mui-x/issues/2680.
72
162
  event.stopPropagation();
73
163
  dragRowNode.current = event.currentTarget;
164
+ // Apply cell-level dragging class to the drag handle
74
165
  dragRowNode.current.classList.add(classes.rowDragging);
75
166
  setDragRowId(params.id);
167
+
168
+ // Apply the dragged state to the entire row
169
+ applyDraggedState(params.id, true);
76
170
  removeDnDStylesTimeout.current = setTimeout(() => {
77
171
  dragRowNode.current.classList.remove(classes.rowDragging);
78
172
  });
79
173
  originRowIndex.current = sortedRowIndexLookup[params.id];
80
174
  apiRef.current.setCellFocus(params.id, _gridRowReorderColDef.GRID_REORDER_COL_DEF.field);
81
- }, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, sortedRowIndexLookup]);
175
+ }, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, sortedRowIndexLookup, applyDraggedState]);
82
176
  const handleDragOver = React.useCallback((params, event) => {
83
177
  if (dragRowId === '') {
84
178
  return;
85
179
  }
86
180
  const rowNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, params.id);
87
- if (!rowNode || rowNode.type === 'footer' || rowNode.type === 'pinnedRow') {
181
+ if (!rowNode || rowNode.type === 'footer' || rowNode.type === 'pinnedRow' || !event.target) {
88
182
  return;
89
183
  }
184
+
185
+ // Find the relative 'y' mouse position based on the event.target
186
+ const targetRect = event.target.getBoundingClientRect();
187
+ const relativeY = Math.floor(event.clientY - targetRect.top);
188
+ const midPoint = Math.floor(targetRect.height / 2);
90
189
  logger.debug(`Dragging over row ${params.id}`);
91
190
  event.preventDefault();
92
191
  // Prevent drag events propagation.
93
192
  // For more information check here https://github.com/mui/mui-x/issues/2680.
94
193
  event.stopPropagation();
95
- const mouseMovementDiff = previousMousePosition ? previousMousePosition.y - event.clientY : event.clientY;
96
194
  if (params.id !== dragRowId) {
97
195
  const targetRowIndex = sortedRowIndexLookup[params.id];
98
- const dragDirection = mouseMovementDiff > 0 ? Direction.DOWN : Direction.UP;
196
+ const sourceRowIndex = sortedRowIndexLookup[dragRowId];
197
+
198
+ // Determine drop position based on relativeY position within the row
199
+ const dropPosition = relativeY < midPoint ? 'above' : 'below';
200
+
201
+ // Check if this drop would result in no actual movement
202
+ const wouldResultInNoMovement = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 ||
203
+ // dragging to immediately below (above next row)
204
+ dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1; // dragging to immediately above (below previous row)
205
+
99
206
  const currentReorderState = {
100
- dragDirection,
101
- previousTargetId: params.id
207
+ dragDirection: targetRowIndex < sourceRowIndex ? Direction.UP : Direction.DOWN,
208
+ previousTargetId: params.id,
209
+ previousDropPosition: dropPosition
102
210
  };
103
- const isStateChanged = currentReorderState.dragDirection !== previousReorderState.dragDirection || currentReorderState.previousTargetId !== previousReorderState.previousTargetId;
104
- if (previousReorderState.dragDirection === null || Math.abs(mouseMovementDiff) >= 1 && isStateChanged) {
105
- apiRef.current.setRowIndex(dragRowId, targetRowIndex);
106
- previousReorderState = currentReorderState;
211
+
212
+ // Only update visual indicator:
213
+ // 1. When dragging over a different row
214
+ // 2. When it would result in actual movement
215
+ if (previousReorderState.current.previousTargetId !== params.id || previousReorderState.current.previousDropPosition !== dropPosition) {
216
+ if (wouldResultInNoMovement) {
217
+ // Clear any existing indicators since this wouldn't result in movement
218
+ setDropTarget({
219
+ targetRowId: null,
220
+ targetRowIndex: null,
221
+ dropPosition: null
222
+ });
223
+ applyDropIndicator(null, null);
224
+ } else {
225
+ setDropTarget({
226
+ targetRowId: params.id,
227
+ targetRowIndex,
228
+ dropPosition
229
+ });
230
+ applyDropIndicator(params.id, dropPosition);
231
+ }
232
+ previousReorderState.current = currentReorderState;
107
233
  }
234
+ } else if (previousReorderState.current.previousTargetId !== null) {
235
+ setDropTarget({
236
+ targetRowId: null,
237
+ targetRowIndex: null,
238
+ dropPosition: null
239
+ });
240
+ applyDropIndicator(null, null);
241
+ previousReorderState.current = {
242
+ previousTargetId: null,
243
+ dragDirection: null,
244
+ previousDropPosition: null
245
+ };
108
246
  }
109
- previousMousePosition = {
110
- x: event.clientX,
111
- y: event.clientY
112
- };
113
- }, [dragRowId, apiRef, logger, sortedRowIndexLookup]);
114
- const handleDragEnd = React.useCallback((params, event) => {
247
+ }, [dragRowId, apiRef, logger, sortedRowIndexLookup, applyDropIndicator]);
248
+ const handleDragEnd = React.useCallback((_, event) => {
115
249
  // Call the gridEditRowsStateSelector directly to avoid infnite loop
116
250
  const editRowsState = (0, _internals.gridEditRowsStateSelector)(apiRef);
117
251
  if (dragRowId === '' || isRowReorderDisabled || Object.keys(editRowsState).length !== 0) {
@@ -124,24 +258,65 @@ const useGridRowReorder = (apiRef, props) => {
124
258
  event.stopPropagation();
125
259
  clearTimeout(removeDnDStylesTimeout.current);
126
260
  dragRowNode.current = null;
127
- previousReorderState.dragDirection = null;
261
+ const dragDirection = previousReorderState.current.dragDirection;
262
+ previousReorderState.current = EMPTY_REORDER_STATE;
263
+
264
+ // Clear visual indicators and dragged state
265
+ applyDropIndicator(null, null);
266
+ applyDraggedState(dragRowId, false);
128
267
 
129
268
  // Check if the row was dropped outside the grid.
130
- if (event.dataTransfer.dropEffect === 'none') {
131
- // Accessing params.field may contain the wrong field as header elements are reused
132
- apiRef.current.setRowIndex(dragRowId, originRowIndex.current);
269
+ if (!event.dataTransfer || event.dataTransfer.dropEffect === 'none') {
270
+ // Reset drop target state
271
+ setDropTarget({
272
+ targetRowId: null,
273
+ targetRowIndex: null,
274
+ dropPosition: null
275
+ });
133
276
  originRowIndex.current = null;
134
277
  } else {
135
- // Emit the rowOrderChange event only once when the reordering stops.
136
- const rowOrderChangeParams = {
137
- row: apiRef.current.getRow(dragRowId),
138
- targetIndex: sortedRowIndexLookup[params.id],
139
- oldIndex: originRowIndex.current
140
- };
141
- apiRef.current.publishEvent('rowOrderChange', rowOrderChangeParams);
278
+ if (dropTarget.targetRowIndex !== null && dropTarget.targetRowId !== null) {
279
+ const sourceRowIndex = originRowIndex.current;
280
+ const targetRowIndex = dropTarget.targetRowIndex;
281
+ const dropPosition = dropTarget.dropPosition;
282
+
283
+ // Calculate the correct target index based on drop position
284
+ let finalTargetIndex;
285
+ if (dragDirection === Direction.UP) {
286
+ finalTargetIndex = dropPosition === 'above' ? targetRowIndex : targetRowIndex + 1;
287
+ } else {
288
+ finalTargetIndex = dropPosition === 'above' ? targetRowIndex - 1 : targetRowIndex;
289
+ }
290
+ const isReorderInvalid = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 ||
291
+ // dragging to immediately below (above next row)
292
+ dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1 ||
293
+ // dragging to immediately above (below previous row)
294
+ dropTarget.targetRowId === dragRowId; // dragging to the same row
295
+
296
+ if (!isReorderInvalid) {
297
+ applyRowAnimation(() => {
298
+ apiRef.current.setRowIndex(dragRowId, finalTargetIndex);
299
+
300
+ // Emit the rowOrderChange event only once when the reordering stops.
301
+ const rowOrderChangeParams = {
302
+ row: apiRef.current.getRow(dragRowId),
303
+ targetIndex: finalTargetIndex,
304
+ oldIndex: sourceRowIndex
305
+ };
306
+ apiRef.current.publishEvent('rowOrderChange', rowOrderChangeParams);
307
+ });
308
+ }
309
+ }
310
+
311
+ // Reset drop target state
312
+ setDropTarget({
313
+ targetRowId: null,
314
+ targetRowIndex: null,
315
+ dropPosition: null
316
+ });
142
317
  }
143
318
  setDragRowId('');
144
- }, [apiRef, dragRowId, isRowReorderDisabled, logger, sortedRowIndexLookup]);
319
+ }, [apiRef, dragRowId, isRowReorderDisabled, logger, dropTarget, applyDropIndicator, applyDraggedState, applyRowAnimation]);
145
320
  (0, _xDataGrid.useGridEvent)(apiRef, 'rowDragStart', handleDragStart);
146
321
  (0, _xDataGrid.useGridEvent)(apiRef, 'rowDragOver', handleDragOver);
147
322
  (0, _xDataGrid.useGridEvent)(apiRef, 'rowDragEnd', handleDragEnd);
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @mui/x-data-grid-pro v8.8.0
2
+ * @mui/x-data-grid-pro v8.9.1
3
3
  *
4
- * @license MUI X Commercial
5
- * This source code is licensed under the commercial license found in the
4
+ * @license SEE LICENSE IN LICENSE
5
+ * This source code is licensed under the SEE LICENSE IN LICENSE license found in the
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  "use strict";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-data-grid-pro",
3
- "version": "8.8.0",
3
+ "version": "8.9.1",
4
4
  "author": "MUI Team",
5
5
  "description": "The Pro plan edition of the MUI X Data Grid components.",
6
6
  "main": "./index.js",
@@ -41,8 +41,8 @@
41
41
  "clsx": "^2.1.1",
42
42
  "prop-types": "^15.8.1",
43
43
  "@mui/x-internals": "8.8.0",
44
- "@mui/x-data-grid": "8.8.0",
45
- "@mui/x-license": "8.8.0"
44
+ "@mui/x-license": "8.9.0",
45
+ "@mui/x-data-grid": "8.9.1"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@emotion/react": "^11.9.0",