@opencorestack/opengridx 1.3.0 → 2.0.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 +53 -0
- package/dist/index.d.ts +22 -6
- package/dist/opengridx.es.js +3673 -3617
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +16 -16
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +7 -9
- package/docs/components/datagrid.md +20 -2
- package/docs/components/header.md +3 -1
- package/docs/components/row.md +4 -9
- package/docs/features/pinning.md +0 -1
- package/docs/features/selection.md +22 -0
- package/docs/features/sorting-pagination.md +35 -0
- package/docs/features/tree-data-grouping.md +47 -4
- package/lib/components/DataGrid/DataGrid.tsx +86 -18
- package/lib/components/DataGrid/DataGrid.v2.test.tsx +127 -0
- package/lib/components/Header/Header.test.tsx +135 -0
- package/lib/components/Header/Header.tsx +20 -5
- package/lib/hooks/core/useGridColumns.tsx +2 -2
- package/lib/hooks/core/useGridControlledState.ts +3 -3
- package/lib/hooks/features/useAggregation.test.ts +40 -0
- package/lib/hooks/features/useAggregation.ts +14 -1
- package/lib/hooks/useRowGrouping.test.ts +53 -0
- package/lib/hooks/useRowGrouping.ts +22 -3
- package/lib/types/index.ts +18 -8
- package/llms.txt +1 -1
- package/package.json +1 -1
package/docs/API_REFERENCE.md
CHANGED
|
@@ -45,6 +45,7 @@ The main component for displaying and interacting with data.
|
|
|
45
45
|
| :--- | :--- | :--- | :--- |
|
|
46
46
|
| `sortingMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles sorting. The grid calls `onSortModelChange` but does not re-sort rows locally. |
|
|
47
47
|
| `filterMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles filtering. The grid calls `onFilterModelChange` but does not re-filter rows locally. |
|
|
48
|
+
| `multiSort` | `boolean` | `false` | When `true`, clicking any sortable column header appends/cycles it in the sort model instead of replacing it — no Shift key required. Shift+click always appends regardless of this prop. |
|
|
48
49
|
| `onSortModelChange` | `(model: GridSortItem[]) => void` | — | Fired when the active sort model changes. |
|
|
49
50
|
| `onFilterModelChange` | `(model: GridFilterModel) => void` | — | Fired when the active filter model changes. |
|
|
50
51
|
|
|
@@ -81,7 +82,6 @@ The main component for displaying and interacting with data.
|
|
|
81
82
|
| `pinnedColumns` | `GridColumnPinning` | — | Columns pinned to the left or right viewport edges. See [Column & Row Pinning](#-column--row-pinning). |
|
|
82
83
|
| `onPinnedColumnsChange` | `(model: GridColumnPinning) => void` | — | Fired when column pinning changes. |
|
|
83
84
|
| `pinnedRows` | `GridRowPinning` | — | Row IDs pinned to the top or bottom of the viewport. |
|
|
84
|
-
| `onPinnedRowsChange` | `(model: GridRowPinning) => void` | — | Fired when row pinning changes. |
|
|
85
85
|
| `pinCheckboxColumn` | `boolean` | `false` | Keeps the checkbox column visible during horizontal scrolling. |
|
|
86
86
|
| `pinExpandColumn` | `boolean` | `false` | Keeps the Master-Detail expansion column visible during horizontal scrolling. |
|
|
87
87
|
|
|
@@ -107,7 +107,7 @@ The main component for displaying and interacting with data.
|
|
|
107
107
|
| :--- | :--- | :--- | :--- |
|
|
108
108
|
| `treeData` | `boolean` | `false` | Enables hierarchical tree data display. |
|
|
109
109
|
| `getTreeDataPath` | `(row: R) => string[]` | — | Returns the hierarchy path for a row (e.g. `['Engineering', 'Frontend']`). |
|
|
110
|
-
| `groupingColDef` | `GridColDef
|
|
110
|
+
| `groupingColDef` | `Partial<GridColDef>` | — | Configures the dedicated `__group__` column prepended at position 0 (auto-pinned left) when row grouping is active. Accepts `headerName`, `width`, `renderCell`, and any non-system `GridColDef` field. The column's `field` is always `'__group__'`; it is never sorted, exported, hidden, or re-ordered. |
|
|
111
111
|
| `defaultGroupingExpansionDepth` | `number` | `0` | Number of tree levels expanded on initial render (`-1` = all). |
|
|
112
112
|
|
|
113
113
|
#### Row Grouping & Aggregation
|
|
@@ -115,7 +115,6 @@ The main component for displaying and interacting with data.
|
|
|
115
115
|
| Prop | Type | Default | Description |
|
|
116
116
|
| :--- | :--- | :--- | :--- |
|
|
117
117
|
| `rowGroupingModel` | `GridRowGroupingModel` | `[]` | Array of field names to group rows by (e.g. `['department', 'team']`). See [Row Grouping](#️-row-grouping). |
|
|
118
|
-
| `onRowGroupingModelChange` | `(model: GridRowGroupingModel) => void` | — | Fired when the grouping model changes. |
|
|
119
118
|
| `aggregationModel` | `GridAggregationModel` | — | Map of `field → aggFn` (e.g. `{ salary: 'sum', age: 'avg' }`). See [Aggregation Reference](#-aggregation-reference). |
|
|
120
119
|
| `onAggregationModelChange` | `(model: GridAggregationModel) => void` | — | Fired when the aggregation model changes. |
|
|
121
120
|
| `getAggregationPosition` | `(groupNode: GridTreeNode \| null) => 'inline' \| 'footer' \| null` | — | Controls where aggregation results appear. `'inline'` = inside the group row, `'footer'` = a dedicated row below the group, `null` = hidden. Pass `null` groupNode = grand-total (root) position. |
|
|
@@ -279,7 +278,7 @@ Defines the behavior and appearance of a single column.
|
|
|
279
278
|
| :--- | :--- | :--- | :--- |
|
|
280
279
|
| `field` | `string` | — | **Required.** Unique identifier matching the row object key. |
|
|
281
280
|
| `headerName` | `string` | — | Text shown in the column header cell. |
|
|
282
|
-
| `description` | `string` | — | Tooltip shown on header hover (accessibility
|
|
281
|
+
| `description` | `string` | — | Tooltip shown on column header hover (rendered as the HTML `title` attribute — improves accessibility). |
|
|
283
282
|
| `width` | `number \| string` | `100` | Fixed width in pixels or a percentage string. |
|
|
284
283
|
| `minWidth` | `number` | — | Minimum width in pixels (enforced during resize). |
|
|
285
284
|
| `maxWidth` | `number` | — | Maximum width in pixels (enforced during resize). |
|
|
@@ -335,9 +334,10 @@ Defines the behavior and appearance of a single column.
|
|
|
335
334
|
| `pinnable` | `boolean` | `true` | Allow this column to be pinned via the UI. |
|
|
336
335
|
| `disableColumnMenu` | `boolean` | `false` | Hide the column header kebab/context menu. |
|
|
337
336
|
| `exportable` | `boolean` | `true` | Set to `false` to exclude from CSV, Excel, JSON, and Print exports. |
|
|
338
|
-
| `groupable` | `boolean` | `true` | Allow this column to be used as a row grouping dimension. |
|
|
337
|
+
| `groupable` | `boolean` | `true` | Allow this column to be used as a row grouping dimension. Set to `false` to prevent this field from being grouped, even when it appears in `rowGroupingModel`. |
|
|
338
|
+
| `groupingValueFormatter` | `(params: { field: string; value: unknown }) => string` | — | Custom formatter for group-header labels when this column is the active grouping field. Falls back to `"field: value"` when omitted. |
|
|
339
339
|
| `aggregable` | `boolean` | `true` | Allow this column to be aggregated. |
|
|
340
|
-
| `availableAggregationFunctions` | `string[]` | all built-ins | Restrict which aggregation functions are available for this column (e.g. `['sum', 'avg']`). |
|
|
340
|
+
| `availableAggregationFunctions` | `string[]` | all built-ins | Restrict which aggregation functions are computed and available for this column (e.g. `['sum', 'avg']`). Functions outside this list are skipped even if set in `aggregationModel`. |
|
|
341
341
|
|
|
342
342
|
---
|
|
343
343
|
|
|
@@ -1087,7 +1087,6 @@ import { DataGrid, GridRowGroupingModel, GridAggregationModel } from '@opencores
|
|
|
1087
1087
|
import { useState } from 'react';
|
|
1088
1088
|
|
|
1089
1089
|
export default function GroupingExample() {
|
|
1090
|
-
const [rowGroupingModel, setRowGroupingModel] = useState<GridRowGroupingModel>(['department']);
|
|
1091
1090
|
const [aggregationModel, setAggregationModel] = useState<GridAggregationModel>({
|
|
1092
1091
|
salary: 'sum',
|
|
1093
1092
|
age: 'avg',
|
|
@@ -1097,8 +1096,7 @@ export default function GroupingExample() {
|
|
|
1097
1096
|
<DataGrid
|
|
1098
1097
|
rows={rows}
|
|
1099
1098
|
columns={columns}
|
|
1100
|
-
rowGroupingModel={
|
|
1101
|
-
onRowGroupingModelChange={setRowGroupingModel}
|
|
1099
|
+
rowGroupingModel={['department']}
|
|
1102
1100
|
aggregationModel={aggregationModel}
|
|
1103
1101
|
onAggregationModelChange={setAggregationModel}
|
|
1104
1102
|
/>
|
|
@@ -27,11 +27,29 @@ function MyGrid() {
|
|
|
27
27
|
| `getRowId` | `(row: R) => GridRowId` | `row.id` | Unique ID for each row. |
|
|
28
28
|
| `height` | `number \| string` | `undefined` | Height of the grid container. |
|
|
29
29
|
| `loading` | `boolean` | `false` | Displays a loading skeleton/shimmer. |
|
|
30
|
+
| `density` | `'compact' \| 'standard' \| 'comfortable'` | `'standard'` | Row height preset: compact = 32 px, standard = `rowHeight`, comfortable = 72 px. |
|
|
30
31
|
| `checkboxSelection` | `boolean` | `false` | Enable row checkboxes. |
|
|
32
|
+
| `disableRowSelectionOnClick` | `boolean` | `false` | When `true`, clicking a row does not toggle its selection. |
|
|
33
|
+
| `disableMultipleRowSelection` | `boolean` | `false` | When `true`, at most one row can be selected at a time. |
|
|
31
34
|
| `pagination` | `boolean` | `false` | Enable/Disable bottom pagination bar. |
|
|
32
|
-
| `paginationModel` | `GridPaginationModel` | — | Controlled pagination state (`{ page, pageSize }`). |
|
|
35
|
+
| `paginationModel` | `GridPaginationModel` | — | Controlled pagination state (`{ page, pageSize }`). Omit for uncontrolled; use `initialState` to set the initial page/pageSize. |
|
|
33
36
|
| `pageSizeOptions` | `number[]` | `[10, 25, 50]` | Available page size options. |
|
|
34
|
-
| `initialState` | `GridInitialState` | `undefined` | Initial configuration (sorting, columns, etc). |
|
|
37
|
+
| `initialState` | `GridInitialState` | `undefined` | Initial configuration (sorting, columns, pagination, etc). |
|
|
38
|
+
|
|
39
|
+
## 🔃 Sorting
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
| :--- | :--- | :--- | :--- |
|
|
43
|
+
| `sortModel` | `GridSortItem[]` | — | Controlled sort state. |
|
|
44
|
+
| `onSortModelChange` | `(model: GridSortItem[]) => void` | — | Fired when the sort model changes. |
|
|
45
|
+
| `multiSort` | `boolean` | `false` | When `true`, every click appends/cycles the column in the sort model instead of replacing it. Shift+click always appends regardless of this prop. |
|
|
46
|
+
|
|
47
|
+
## 🌳 Row Grouping
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Default | Description |
|
|
50
|
+
| :--- | :--- | :--- | :--- |
|
|
51
|
+
| `rowGroupingModel` | `string[]` | `[]` | Fields to group by (in order). |
|
|
52
|
+
| `groupingColDef` | `GridColDef` | — | Config for the dedicated `__group__` column created at position 0, auto-pinned left, when grouping is active. |
|
|
35
53
|
|
|
36
54
|
## 🖱️ Interaction & Events
|
|
37
55
|
|
|
@@ -9,7 +9,9 @@ Manages the column headers, sorting triggers, column resizing, and column groupi
|
|
|
9
9
|
| `columns` | `GridColDef[]` | Column definitions for the visible viewport. |
|
|
10
10
|
| `columnGroupingModel` | `GridColumnGroupingModel` | Hierarchy for multi-level header spanning. |
|
|
11
11
|
| `sortModel` | `GridSortModel` | Current sorting state for highlight and icons. |
|
|
12
|
-
| `onSort` | `(field, direction) => void` | Callback triggered
|
|
12
|
+
| `onSort` | `(field, direction) => void` | Callback triggered on a plain click — replaces the sort model with a single key. |
|
|
13
|
+
| `onSortAdd` | `(field, direction) => void` | Callback triggered on Shift+click or when `multiSort` is active — appends/cycles the key without replacing others. |
|
|
14
|
+
| `multiSort` | `boolean` | When `true`, every click routes to `onSortAdd` instead of `onSort`. Wired from the `multiSort` prop on `<DataGrid>`. |
|
|
13
15
|
| `onColumnResize` | `Function` | Callback for manual column width changes. |
|
|
14
16
|
| `pinnedColumns` | `GridColumnPinning` | Coordinates sticky positioning for headers. |
|
|
15
17
|
| `checkboxSelection` | `boolean` | Renders the "Select All" checkbox. |
|
package/docs/components/row.md
CHANGED
|
@@ -30,23 +30,18 @@ The grid supports "Zebra" striping via CSS:
|
|
|
30
30
|
|
|
31
31
|
When `getDetailPanelContent` is provided, the `<Row />` renders an expandable container below itself to show supplemental data.
|
|
32
32
|
|
|
33
|
-
## ⚠️
|
|
33
|
+
## ⚠️ v2.0 — Runtime `_*` hierarchy fields removed
|
|
34
34
|
|
|
35
|
-
The internal hierarchy fields (`_hasChildren`, `_treeDepth`, `_isExpanded`, `_groupingField`, `_groupingValue`, `_descendantCount`, `_isGroupRow`) are no longer
|
|
36
|
-
|
|
37
|
-
**Before (still works at runtime in v1.1 — `params.row._hasChildren` is `unknown`, not `boolean`; assignment to typed variables and arithmetic will error, truthiness checks will not):**
|
|
35
|
+
The internal hierarchy fields (`_hasChildren`, `_treeDepth`, `_isExpanded`, `_groupingField`, `_groupingValue`, `_descendantCount`, `_isGroupRow`) are no longer injected onto the runtime row object as of v2.0. Use `params.rowMeta` instead:
|
|
38
36
|
|
|
39
37
|
```tsx
|
|
38
|
+
// ❌ v1 shim — no longer works in v2:
|
|
40
39
|
renderCell: (params) => {
|
|
41
|
-
// v1.1 accepts this at runtime; _hasChildren resolves to unknown via [key: string]: unknown
|
|
42
40
|
const hasChildren = (params.row as Record<string, unknown>)._hasChildren;
|
|
43
41
|
return hasChildren ? <GroupIcon /> : params.value;
|
|
44
42
|
}
|
|
45
|
-
```
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```tsx
|
|
44
|
+
// ✅ v2:
|
|
50
45
|
renderCell: (params) => {
|
|
51
46
|
const hasChildren = params.rowMeta?.hasChildren;
|
|
52
47
|
return hasChildren ? <GroupIcon /> : params.value;
|
package/docs/features/pinning.md
CHANGED
|
@@ -55,7 +55,6 @@ Rows can be pinned to the top or bottom of the grid. Pinned rows remain visible
|
|
|
55
55
|
| `pinnedColumns` | `GridColumnPinning` | `{ left: string[], right: string[] }` |
|
|
56
56
|
| `pinnedRows` | `GridRowPinning` | `{ top: GridRowId[], bottom: GridRowId[] }` |
|
|
57
57
|
| `onPinnedColumnsChange` | `(model) => void` | Callback triggered when pinning changes. |
|
|
58
|
-
| `onPinnedRowsChange` | `(model) => void` | Callback triggered when row pinning changes. |
|
|
59
58
|
|
|
60
59
|
---
|
|
61
60
|
|
|
@@ -36,6 +36,28 @@ const [selection, setSelection] = useState<GridRowId[]>([]);
|
|
|
36
36
|
/>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### Disable Click-to-Select
|
|
40
|
+
|
|
41
|
+
Use `disableRowSelectionOnClick` to prevent clicking a row from changing the selection (useful when rows have their own click actions like navigation). Selection via checkboxes still works.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<DataGrid
|
|
45
|
+
checkboxSelection
|
|
46
|
+
disableRowSelectionOnClick
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Single-Row Selection Only
|
|
51
|
+
|
|
52
|
+
Use `disableMultipleRowSelection` to cap selection to one row at a time. Clicking a second row deselects the first; clicking an already-selected row deselects it.
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<DataGrid
|
|
56
|
+
disableMultipleRowSelection
|
|
57
|
+
onRowSelectionModelChange={(model) => console.log('selected:', model)}
|
|
58
|
+
/>
|
|
59
|
+
```
|
|
60
|
+
|
|
39
61
|
---
|
|
40
62
|
|
|
41
63
|
## ⚙️ API Reference
|
|
@@ -15,6 +15,41 @@ Sorting can be toggled by clicking column headers. It supports single-column and
|
|
|
15
15
|
| `sortModel` | `GridSortItem[]` | Controlled state of active sorts. |
|
|
16
16
|
| `onSortModelChange` | `(model) => void` | Callback triggered when sorting changes. |
|
|
17
17
|
|
|
18
|
+
### Multi-Column Sort
|
|
19
|
+
|
|
20
|
+
OpenGridX supports two modes of multi-column sorting:
|
|
21
|
+
|
|
22
|
+
**`multiSort` prop (single-click, recommended):** Pass `multiSort` to make every click append or cycle a column in the sort model rather than replacing it. No modifier key required.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
<DataGrid
|
|
26
|
+
multiSort
|
|
27
|
+
sortModel={sortModel}
|
|
28
|
+
onSortModelChange={setSortModel}
|
|
29
|
+
...
|
|
30
|
+
/>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Shift+Click (always available):** Even without `multiSort`, holding **Shift** while clicking a column header appends it to the active sort. This works in all grids regardless of the prop.
|
|
34
|
+
|
|
35
|
+
Each active sort column shows a numbered priority badge (`1`, `2`, `3`…) next to its arrow.
|
|
36
|
+
|
|
37
|
+
- Clicking (or shift-clicking) an already-sorted column cycles its direction: `asc → desc → removed`.
|
|
38
|
+
- A plain click *without* `multiSort` and without Shift always resets to a single-key sort on that column.
|
|
39
|
+
- Set `sortable: false` on a column to exclude it entirely.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// Controlled multi-sort — set programmatically or drive from UI with multiSort:
|
|
43
|
+
<DataGrid
|
|
44
|
+
multiSort
|
|
45
|
+
sortModel={[
|
|
46
|
+
{ field: 'department', sort: 'asc' },
|
|
47
|
+
{ field: 'salary', sort: 'desc' },
|
|
48
|
+
]}
|
|
49
|
+
onSortModelChange={setSortModel}
|
|
50
|
+
/>
|
|
51
|
+
```
|
|
52
|
+
|
|
18
53
|
### Column-Specific Config
|
|
19
54
|
Disable sorting for specific columns in `GridColDef`:
|
|
20
55
|
```typescript
|
|
@@ -54,14 +54,57 @@ Tree data is used for data that has a natural parent-child relationship (e.g., a
|
|
|
54
54
|
---
|
|
55
55
|
|
|
56
56
|
## 🎨 Customizing the Group Column
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
When `rowGroupingModel` is active, pass `groupingColDef` to configure a dedicated `__group__` column that is prepended at position 0 and auto-pinned left, separate from your data columns. It accepts any `GridColDef` fields except `field` (always `'__group__'`).
|
|
58
59
|
|
|
59
60
|
```tsx
|
|
60
61
|
<DataGrid
|
|
62
|
+
rows={rows}
|
|
63
|
+
columns={columns}
|
|
64
|
+
rowGroupingModel={['department']}
|
|
61
65
|
groupingColDef={{
|
|
62
|
-
headerName: '
|
|
63
|
-
width:
|
|
64
|
-
renderCell: (params) => { /* custom renderer */ }
|
|
66
|
+
headerName: 'Department Group',
|
|
67
|
+
width: 240,
|
|
65
68
|
}}
|
|
66
69
|
/>
|
|
67
70
|
```
|
|
71
|
+
|
|
72
|
+
Without `groupingColDef`, the group toggle is overlaid on the first data column. With it, the group indicator always appears in its own fixed column regardless of your `columns` order.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 🏷️ Customizing Group Labels (`groupingValueFormatter`)
|
|
77
|
+
|
|
78
|
+
By default each group-header row displays `"field: value"`. Use `groupingValueFormatter` on a `GridColDef` to override the label for that grouping level:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
const columns: GridColDef[] = [
|
|
82
|
+
{
|
|
83
|
+
field: 'department',
|
|
84
|
+
headerName: 'Department',
|
|
85
|
+
groupingValueFormatter: ({ value }) => `📁 ${String(value)}`,
|
|
86
|
+
},
|
|
87
|
+
{ field: 'name', headerName: 'Name' },
|
|
88
|
+
{ field: 'salary', headerName: 'Salary' },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
<DataGrid rows={rows} columns={columns} rowGroupingModel={['department']} />
|
|
92
|
+
// Group headers now show: "📁 Engineering", "📁 HR", etc.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🚫 Preventing a Column from Being Grouped (`groupable: false`)
|
|
98
|
+
|
|
99
|
+
Set `groupable: false` on a `GridColDef` to prevent that field from being used as a grouping dimension. The column still renders normally, but the grid skips it when building the group tree:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
const columns: GridColDef[] = [
|
|
103
|
+
{ field: 'id', headerName: 'ID', groupable: false },
|
|
104
|
+
{ field: 'department', headerName: 'Department' },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
// Even if rowGroupingModel includes 'id', it will be silently skipped.
|
|
108
|
+
<DataGrid rows={rows} columns={columns} rowGroupingModel={['id', 'department']} />
|
|
109
|
+
// Result: grouped by 'department' only — 'id' is skipped.
|
|
110
|
+
```
|
|
@@ -39,7 +39,10 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
39
39
|
rowHeight = 52,
|
|
40
40
|
headerHeight = 56,
|
|
41
41
|
autoHeight = false,
|
|
42
|
+
density,
|
|
42
43
|
checkboxSelection = false,
|
|
44
|
+
disableRowSelectionOnClick = false,
|
|
45
|
+
disableMultipleRowSelection = false,
|
|
43
46
|
rowSelectionModel: propRowSelectionModel,
|
|
44
47
|
onRowSelectionModelChange: propOnRowSelectionModelChange,
|
|
45
48
|
onRowClick,
|
|
@@ -50,7 +53,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
50
53
|
sortModel: propSortModel,
|
|
51
54
|
onSortModelChange,
|
|
52
55
|
pagination: propPagination = false,
|
|
53
|
-
paginationModel
|
|
56
|
+
paginationModel,
|
|
54
57
|
onPaginationModelChange,
|
|
55
58
|
pageSizeOptions = [10, 25, 50, 100],
|
|
56
59
|
pinnedColumns: propPinnedColumns,
|
|
@@ -74,6 +77,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
74
77
|
treeData = false,
|
|
75
78
|
getTreeDataPath,
|
|
76
79
|
defaultGroupingExpansionDepth,
|
|
80
|
+
groupingColDef,
|
|
77
81
|
|
|
78
82
|
rowGroupingModel: propRowGroupingModel,
|
|
79
83
|
|
|
@@ -86,6 +90,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
86
90
|
onProcessRowUpdateError,
|
|
87
91
|
|
|
88
92
|
dataSource,
|
|
93
|
+
multiSort = false,
|
|
89
94
|
sortingMode = 'client',
|
|
90
95
|
filterMode = 'client',
|
|
91
96
|
paginationMode = 'client',
|
|
@@ -191,8 +196,35 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
191
196
|
|
|
192
197
|
const pivot = usePivot(rows as GridRowModel[], columns as unknown as GridColDef[], currentPivotModel, pivotMode);
|
|
193
198
|
|
|
199
|
+
const effectiveRowHeight = density === 'compact' ? 32 : density === 'comfortable' ? 72 : rowHeight;
|
|
200
|
+
|
|
194
201
|
const activeRows = (pivotMode && pivot.isValid ? pivot.pivotRows : rows) as unknown as R[];
|
|
195
|
-
const
|
|
202
|
+
const baseColumns = (pivotMode && pivot.isValid ? pivot.pivotColumns : columns) as unknown as GridColDef<R>[];
|
|
203
|
+
|
|
204
|
+
// When groupingColDef is provided and row grouping is active, prepend a
|
|
205
|
+
// dedicated synthetic __group__ column at position 0 (pinned-left).
|
|
206
|
+
const isRowGroupingActive = Boolean(propRowGroupingModel && propRowGroupingModel.length > 0);
|
|
207
|
+
|
|
208
|
+
// Auto-pin __group__ column to the left when groupingColDef is active.
|
|
209
|
+
const effectivePinnedColumns = (groupingColDef && isRowGroupingActive && !pivotMode)
|
|
210
|
+
? { ...pinnedColumns, left: ['__group__', ...((pinnedColumns?.left ?? []).filter(f => f !== '__group__'))] }
|
|
211
|
+
: pinnedColumns;
|
|
212
|
+
|
|
213
|
+
const activeColumns = (groupingColDef && isRowGroupingActive && !pivotMode)
|
|
214
|
+
? [
|
|
215
|
+
{
|
|
216
|
+
headerName: 'Group',
|
|
217
|
+
width: 220,
|
|
218
|
+
...groupingColDef,
|
|
219
|
+
field: '__group__',
|
|
220
|
+
hideable: false,
|
|
221
|
+
sortable: false,
|
|
222
|
+
pinnable: false,
|
|
223
|
+
exportable: false,
|
|
224
|
+
} as unknown as GridColDef<R>,
|
|
225
|
+
...baseColumns,
|
|
226
|
+
]
|
|
227
|
+
: baseColumns;
|
|
196
228
|
|
|
197
229
|
const [serverAggregationResults, setServerAggregationResults] = useState<GridAggregationResult | null>(null);
|
|
198
230
|
|
|
@@ -245,7 +277,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
245
277
|
const gridData = useDataGrid({
|
|
246
278
|
rows: normalizedRows,
|
|
247
279
|
columns: activeColumns,
|
|
248
|
-
rowHeight,
|
|
280
|
+
rowHeight: effectiveRowHeight,
|
|
249
281
|
headerHeight,
|
|
250
282
|
rowCount: propRowCount,
|
|
251
283
|
columnVisibilityModel,
|
|
@@ -309,6 +341,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
309
341
|
const rowGroupingHandlers = useRowGrouping({
|
|
310
342
|
rows: effectiveRows,
|
|
311
343
|
getRowId: effectiveGetRowId,
|
|
344
|
+
columns: activeColumns,
|
|
312
345
|
rowGroupingModel,
|
|
313
346
|
aggregationModel,
|
|
314
347
|
defaultGroupingExpansionDepth,
|
|
@@ -355,7 +388,27 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
355
388
|
}
|
|
356
389
|
|
|
357
390
|
onRowClick?.(params);
|
|
358
|
-
|
|
391
|
+
|
|
392
|
+
if (!disableRowSelectionOnClick) {
|
|
393
|
+
let next: GridRowId[];
|
|
394
|
+
if (disableMultipleRowSelection) {
|
|
395
|
+
const alreadySelected = selectedRowIds.has(id as GridRowId);
|
|
396
|
+
next = alreadySelected ? [] : [id as GridRowId];
|
|
397
|
+
} else {
|
|
398
|
+
const newSelection = new Set(selectedRowIds);
|
|
399
|
+
if (newSelection.has(id as GridRowId)) {
|
|
400
|
+
newSelection.delete(id as GridRowId);
|
|
401
|
+
} else {
|
|
402
|
+
newSelection.add(id as GridRowId);
|
|
403
|
+
}
|
|
404
|
+
next = Array.from(newSelection);
|
|
405
|
+
}
|
|
406
|
+
if (!isSelectionControlled) {
|
|
407
|
+
setInternalRowSelectionModel(next);
|
|
408
|
+
}
|
|
409
|
+
onRowSelectionModelChange?.(next);
|
|
410
|
+
}
|
|
411
|
+
}, [isHierarchyEnabled, activeHierarchyHandlers, onRowClick, rowMetaMap, disableRowSelectionOnClick, disableMultipleRowSelection, selectedRowIds, isSelectionControlled, setInternalRowSelectionModel, onRowSelectionModelChange]);
|
|
359
412
|
|
|
360
413
|
const dataSourceHandlers = useGridDataSource({
|
|
361
414
|
dataSource,
|
|
@@ -440,7 +493,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
440
493
|
columnWidths,
|
|
441
494
|
effectiveColumnOrder,
|
|
442
495
|
columnVisibilityModel,
|
|
443
|
-
pinnedColumns,
|
|
496
|
+
pinnedColumns: effectivePinnedColumns,
|
|
444
497
|
});
|
|
445
498
|
|
|
446
499
|
const rowPipeline = useGridRowPipeline<GridRowModel>({
|
|
@@ -486,6 +539,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
486
539
|
|
|
487
540
|
const { aggregationResult } = useAggregation({
|
|
488
541
|
rows: filteredRows,
|
|
542
|
+
columns: activeColumns as unknown as GridColDef[],
|
|
489
543
|
aggregationModel,
|
|
490
544
|
isServerSide: !!(dataSource && (paginationMode === 'server' || sortingMode === 'server')),
|
|
491
545
|
dataSource,
|
|
@@ -548,7 +602,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
548
602
|
}, [aggregationResult, aggregationModel, hasAggregation, gridData.apiRef, isRowGrouping, rowGroupingHandlers, sortedUnpinnedRows]);
|
|
549
603
|
|
|
550
604
|
const layout = useLayout({
|
|
551
|
-
rowHeight,
|
|
605
|
+
rowHeight: effectiveRowHeight,
|
|
552
606
|
pagination,
|
|
553
607
|
paginatedUnpinnedRows,
|
|
554
608
|
sortedUnpinnedRows,
|
|
@@ -557,7 +611,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
557
611
|
pinnedTopRowsLength: pinnedTopRows.length,
|
|
558
612
|
pinnedBottomRowsLength: pinnedBottomRows.length,
|
|
559
613
|
visibleOrderedColumns,
|
|
560
|
-
pinnedColumns,
|
|
614
|
+
pinnedColumns: effectivePinnedColumns,
|
|
561
615
|
columnWidths,
|
|
562
616
|
viewportWidth: state.dimensions.viewportWidth || 1000,
|
|
563
617
|
checkboxSelection,
|
|
@@ -688,6 +742,18 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
688
742
|
onSortModelChange?.(newSortModel as GridSortItem[]);
|
|
689
743
|
}, [isSortControlled, onSortModelChange, setInternalSortModel]);
|
|
690
744
|
|
|
745
|
+
const handleSortAdd = useCallback((field: string, direction: GridSortDirection) => {
|
|
746
|
+
const existing = sortModel.filter(item => item.field !== field);
|
|
747
|
+
const newSortModel: GridSortItem[] = direction
|
|
748
|
+
? [...existing, { field, sort: direction }]
|
|
749
|
+
: existing;
|
|
750
|
+
|
|
751
|
+
if (!isSortControlled) {
|
|
752
|
+
setInternalSortModel(newSortModel);
|
|
753
|
+
}
|
|
754
|
+
onSortModelChange?.(newSortModel);
|
|
755
|
+
}, [sortModel, isSortControlled, onSortModelChange, setInternalSortModel]);
|
|
756
|
+
|
|
691
757
|
const { focusedCell, setFocusedCell, handleFocus, handleBlur, handleKeyDown } = useGridKeyboardNavigation({
|
|
692
758
|
allRenderableRows,
|
|
693
759
|
navigationColumns,
|
|
@@ -809,7 +875,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
809
875
|
style={{
|
|
810
876
|
...style,
|
|
811
877
|
height: height ?? style?.height,
|
|
812
|
-
'--ogx-row-height': `${
|
|
878
|
+
'--ogx-row-height': `${effectiveRowHeight}px`,
|
|
813
879
|
'--ogx-header-height': `${headerHeight}px`
|
|
814
880
|
} as unknown as React.CSSProperties}
|
|
815
881
|
aria-busy={effectiveLoading}
|
|
@@ -844,7 +910,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
844
910
|
selectedRowIds={selectedRowIds}
|
|
845
911
|
listViewColumn={listViewColumn}
|
|
846
912
|
noRowsLabel={effectiveNoRowsLabel}
|
|
847
|
-
rowHeight={
|
|
913
|
+
rowHeight={effectiveRowHeight}
|
|
848
914
|
checkboxSelection={checkboxSelection}
|
|
849
915
|
paginationMode={paginationMode}
|
|
850
916
|
dataSource={dataSource}
|
|
@@ -909,10 +975,12 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
909
975
|
onSelectAll={handleSelectAll}
|
|
910
976
|
sortModel={sortModel}
|
|
911
977
|
onSort={handleSort}
|
|
978
|
+
onSortAdd={handleSortAdd}
|
|
979
|
+
multiSort={multiSort}
|
|
912
980
|
|
|
913
981
|
onColumnResize={handleColumnResize}
|
|
914
982
|
columnWidths={columnWidths}
|
|
915
|
-
pinnedColumns={
|
|
983
|
+
pinnedColumns={effectivePinnedColumns}
|
|
916
984
|
|
|
917
985
|
focusedCell={focusedCell}
|
|
918
986
|
onHeaderClick={(field) => {
|
|
@@ -969,7 +1037,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
969
1037
|
onCellClick={handleCellClick}
|
|
970
1038
|
onSelectionChange={handleSelectionChange}
|
|
971
1039
|
columnWidths={columnWidths}
|
|
972
|
-
pinnedColumns={
|
|
1040
|
+
pinnedColumns={effectivePinnedColumns}
|
|
973
1041
|
pinnedRows={pinnedRows}
|
|
974
1042
|
hasDetailPanel={hasDetailPanel}
|
|
975
1043
|
expandedRowIds={expandedRowIds}
|
|
@@ -981,7 +1049,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
981
1049
|
focusedCell={focusedCell}
|
|
982
1050
|
colspanMap={spanning.colspanMap}
|
|
983
1051
|
rowSpanningCaches={spanning.rowSpanningState.caches}
|
|
984
|
-
rowHeight={
|
|
1052
|
+
rowHeight={effectiveRowHeight}
|
|
985
1053
|
rowMetaMap={rowMetaMap}
|
|
986
1054
|
/>
|
|
987
1055
|
|
|
@@ -996,14 +1064,14 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
996
1064
|
checkboxSelection={checkboxSelection}
|
|
997
1065
|
hasDetailPanel={hasDetailPanel}
|
|
998
1066
|
rowReordering={rowReordering}
|
|
999
|
-
rowHeight={
|
|
1067
|
+
rowHeight={effectiveRowHeight}
|
|
1000
1068
|
pinnedRows={pinnedRows}
|
|
1001
1069
|
selectedRowIds={selectedRowIds}
|
|
1002
1070
|
onRowClick={handleRowClick}
|
|
1003
1071
|
onCellClick={handleCellClick}
|
|
1004
1072
|
onSelectionChange={handleSelectionChange}
|
|
1005
1073
|
columnWidths={columnWidths}
|
|
1006
|
-
pinnedColumns={
|
|
1074
|
+
pinnedColumns={effectivePinnedColumns}
|
|
1007
1075
|
expandedRowIds={expandedRowIds}
|
|
1008
1076
|
getDetailPanelContent={getDetailPanelContent}
|
|
1009
1077
|
getDetailPanelHeight={getDetailPanelHeight}
|
|
@@ -1033,7 +1101,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
1033
1101
|
onCellClick={handleCellClick}
|
|
1034
1102
|
onSelectionChange={handleSelectionChange}
|
|
1035
1103
|
columnWidths={columnWidths}
|
|
1036
|
-
pinnedColumns={
|
|
1104
|
+
pinnedColumns={effectivePinnedColumns}
|
|
1037
1105
|
pinnedRows={pinnedRows}
|
|
1038
1106
|
hasDetailPanel={hasDetailPanel}
|
|
1039
1107
|
expandedRowIds={expandedRowIds}
|
|
@@ -1045,7 +1113,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
1045
1113
|
focusedCell={focusedCell}
|
|
1046
1114
|
colspanMap={spanning.colspanMap}
|
|
1047
1115
|
rowSpanningCaches={spanning.rowSpanningState.caches}
|
|
1048
|
-
rowHeight={
|
|
1116
|
+
rowHeight={effectiveRowHeight}
|
|
1049
1117
|
rowMetaMap={rowMetaMap}
|
|
1050
1118
|
/>
|
|
1051
1119
|
|
|
@@ -1058,11 +1126,11 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
1058
1126
|
aggregationModel={aggregationModel}
|
|
1059
1127
|
aggregationResult={aggregationResult}
|
|
1060
1128
|
columnWidths={resolvedColumnWidths}
|
|
1061
|
-
rowHeight={
|
|
1129
|
+
rowHeight={effectiveRowHeight}
|
|
1062
1130
|
checkboxSelection={checkboxSelection}
|
|
1063
1131
|
hasDetailPanel={hasDetailPanel}
|
|
1064
1132
|
rowReordering={rowReordering}
|
|
1065
|
-
pinnedColumns={
|
|
1133
|
+
pinnedColumns={effectivePinnedColumns}
|
|
1066
1134
|
/>
|
|
1067
1135
|
)}
|
|
1068
1136
|
|