@opencorestack/opengridx 2.1.0 → 3.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 +399 -0
- package/README.md +95 -72
- package/dist/index.d.ts +590 -131
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +7891 -5631
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +17 -23
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +348 -166
- package/docs/README.md +6 -1
- package/docs/architecture/datagrid-orchestration.md +81 -0
- package/docs/architecture/grid-row-meta.md +102 -0
- package/docs/architecture/use-grid-columns.md +128 -0
- package/docs/architecture/use-grid-controlled-state.md +157 -0
- package/docs/architecture/use-grid-row-pipeline.md +120 -0
- package/docs/architecture/use-grid-scroll-sync.md +111 -0
- package/docs/architecture/use-grid-state-snapshot.md +90 -0
- package/docs/architecture/use-grid-virtualization.md +115 -0
- package/docs/architecture/use-grid-visible-rows.md +85 -0
- package/docs/assets/banner-alt.png +0 -0
- package/docs/assets/banner.png +0 -0
- package/docs/assets/hero.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/components/aggregation-footer.md +15 -10
- package/docs/components/cell.md +16 -6
- package/docs/components/column-group-header.md +55 -28
- package/docs/components/column-resize.md +17 -13
- package/docs/components/column-visibility.md +6 -7
- package/docs/components/datagrid.md +15 -13
- package/docs/components/empty-state.md +2 -1
- package/docs/components/error-overlay.md +12 -7
- package/docs/components/filter-panel.md +28 -8
- package/docs/components/header.md +28 -9
- package/docs/components/pagination.md +14 -8
- package/docs/components/row.md +21 -5
- package/docs/components/toolbar.md +27 -9
- package/docs/components/tooltip.md +16 -7
- package/docs/customization/slots-api.md +90 -38
- package/docs/customization/theming.md +66 -18
- package/docs/features/aggregation-pivot.md +75 -9
- package/docs/features/cell-spanning.md +80 -125
- package/docs/features/clipboard.md +41 -16
- package/docs/features/custom-pagination.md +38 -18
- package/docs/features/data-source.md +36 -13
- package/docs/features/editing-reordering.md +125 -16
- package/docs/features/export-guide.md +126 -39
- package/docs/features/filtering.md +44 -6
- package/docs/features/infinite-scroll.md +54 -46
- package/docs/features/keyboard-navigation.md +83 -0
- package/docs/features/list-view.md +58 -5
- package/docs/features/loading-states.md +18 -20
- package/docs/features/master-detail.md +18 -5
- package/docs/features/pdf-export.md +72 -19
- package/docs/features/pinning.md +24 -5
- package/docs/features/selection.md +50 -10
- package/docs/features/sorting-pagination.md +64 -14
- package/docs/features/state-persistence.md +78 -10
- package/docs/features/toolbar-customization.md +26 -17
- package/docs/features/tree-data-grouping.md +36 -8
- package/docs/features/virtualization.md +37 -14
- package/docs/migration/v1-to-v2.md +115 -0
- package/docs/migration/v2-to-v3.md +1233 -0
- package/docs/performance.md +124 -0
- package/docs/roadmap.md +5 -5
- package/lib/components/Cell/Cell.css +17 -1
- package/lib/components/Cell/Cell.tsx +129 -57
- package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
- package/lib/components/Cell/GridEditInputCell.tsx +114 -41
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +15 -2
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
- package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
- package/lib/components/DataGrid/DataGrid.css +85 -12
- package/lib/components/DataGrid/DataGrid.tsx +582 -876
- package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
- package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
- package/lib/components/DataGrid/GridListView.tsx +165 -24
- package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
- package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
- package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
- package/lib/components/DataGrid/GridPinnedRows.tsx +62 -11
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
- package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +40 -24
- package/lib/components/DetailPanel/DetailPanel.css +0 -6
- package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
- package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
- package/lib/components/FilterPanel/FilterPanel.css +13 -2
- package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
- package/lib/components/Header/ColumnMenu.tsx +91 -39
- package/lib/components/Header/Header.css +6 -3
- package/lib/components/Header/Header.tsx +181 -160
- package/lib/components/ListView/ListView.css +25 -0
- package/lib/components/ListView/ListViewRow.tsx +76 -10
- package/lib/components/Pagination/Pagination.tsx +15 -7
- package/lib/components/Row/Row.tsx +177 -77
- package/lib/components/Toolbar/GlobalSearch.css +6 -3
- package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
- package/lib/components/Toolbar/GridToolbar.css +12 -11
- package/lib/components/Toolbar/GridToolbar.tsx +133 -89
- package/lib/components/Toolbar/PivotPanel.tsx +89 -49
- package/lib/components/Tooltip/Tooltip.css +20 -0
- package/lib/components/Tooltip/Tooltip.tsx +68 -35
- package/lib/components/ui/Button.tsx +3 -0
- package/lib/components/ui/Checkbox.tsx +2 -1
- package/lib/components/ui/ExpandIcon.css +0 -9
- package/lib/components/ui/ExpandIcon.tsx +4 -1
- package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
- package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
- package/lib/hooks/core/useDataGrid.ts +115 -173
- package/lib/hooks/core/useGridAggregationApi.ts +72 -0
- package/lib/hooks/core/useGridApiMethods.ts +115 -0
- package/lib/hooks/core/useGridApiRef.ts +19 -4
- package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
- package/lib/hooks/core/useGridAriaRows.ts +38 -0
- package/lib/hooks/core/useGridClipboardApi.ts +38 -0
- package/lib/hooks/core/useGridColumnLookup.ts +30 -0
- package/lib/hooks/core/useGridColumns.tsx +279 -76
- package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
- package/lib/hooks/core/useGridControlledState.ts +99 -6
- package/lib/hooks/core/useGridDetailPanel.ts +51 -0
- package/lib/hooks/core/useGridDevWarnings.ts +104 -5
- package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
- package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
- package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
- package/lib/hooks/core/useGridHierarchy.ts +81 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +447 -243
- package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
- package/lib/hooks/core/useGridPageCorrection.ts +55 -0
- package/lib/hooks/core/useGridRowIdOf.ts +32 -0
- package/lib/hooks/core/useGridRowInteractions.ts +81 -0
- package/lib/hooks/core/useGridRowPipeline.ts +69 -39
- package/lib/hooks/core/useGridRowSelection.ts +91 -0
- package/lib/hooks/core/useGridScrollSync.ts +80 -32
- package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
- package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
- package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
- package/lib/hooks/core/useGridToolbarProps.ts +97 -0
- package/lib/hooks/core/useGridViewport.ts +38 -0
- package/lib/hooks/core/useGridViewportSize.ts +26 -0
- package/lib/hooks/core/useGridVisibleRows.ts +17 -5
- package/lib/hooks/core/useLayout.ts +215 -195
- package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
- package/lib/hooks/core/useStableColumns.ts +34 -0
- package/lib/hooks/features/useAggregation.ts +82 -51
- package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
- package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
- package/lib/hooks/features/useGridClipboard.ts +133 -73
- package/lib/hooks/features/useGridDataSource.ts +354 -128
- package/lib/hooks/features/useGridEditing.ts +211 -51
- package/lib/hooks/features/useGridPivot.ts +92 -0
- package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
- package/lib/hooks/features/useGridSpanning.ts +410 -306
- package/lib/hooks/features/usePivot.ts +0 -0
- package/lib/hooks/features/useServerTreeChildren.ts +50 -0
- package/lib/hooks/useColumnReorder.ts +38 -44
- package/lib/hooks/useRowGrouping.ts +298 -160
- package/lib/hooks/useRowReorder.ts +62 -31
- package/lib/hooks/useTreeData.ts +287 -220
- package/lib/index.ts +28 -2
- package/lib/state/index.ts +1 -0
- package/lib/state/types.ts +7 -1
- package/lib/state/useGridStateStorage.ts +88 -29
- package/lib/theme/DataGridThemeProvider.tsx +143 -38
- package/lib/theme/gridThemeContext.ts +5 -0
- package/lib/theme/index.ts +5 -0
- package/lib/theme/presets.ts +5 -4
- package/lib/theme/types.ts +40 -2
- package/lib/types/index.ts +313 -116
- package/lib/utils/aggregation/index.ts +119 -24
- package/lib/utils/aria/index.ts +40 -0
- package/lib/utils/columnGroups.ts +110 -0
- package/lib/utils/columnLookup.ts +33 -0
- package/lib/utils/columnWidth.ts +22 -0
- package/lib/utils/dataSource/index.ts +46 -0
- package/lib/utils/detailPanel.ts +66 -0
- package/lib/utils/dragData.ts +44 -0
- package/lib/utils/editing/index.ts +30 -0
- package/lib/utils/export/exportShared.ts +197 -0
- package/lib/utils/export/exportToExcelAdvanced.ts +278 -139
- package/lib/utils/export/exportToPdf.ts +143 -92
- package/lib/utils/export/groupLabel.ts +14 -0
- package/lib/utils/export/index.ts +278 -272
- package/lib/utils/export/pdfText.ts +50 -0
- package/lib/utils/filtering/index.ts +277 -148
- package/lib/utils/focus/index.ts +115 -0
- package/lib/utils/gridApiRows.ts +35 -0
- package/lib/utils/gridModes.ts +77 -0
- package/lib/utils/grouping/groupedExportRows.ts +91 -0
- package/lib/utils/pagination/index.ts +56 -0
- package/lib/utils/pinning/index.ts +77 -33
- package/lib/utils/pivot/index.ts +368 -0
- package/lib/utils/scroll/index.ts +55 -5
- package/lib/utils/scroll/scrollEnd.ts +18 -0
- package/lib/utils/sorting/index.ts +154 -30
- package/lib/utils/syntheticRows.ts +12 -0
- package/lib/utils/values.ts +206 -0
- package/lib/utils/viewport.ts +14 -0
- package/llms.txt +261 -164
- package/package.json +13 -5
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
- package/lib/components/DataGrid/DataGrid.defects.test.tsx +0 -103
- package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
- package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
- package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
- package/lib/components/DataGrid/DataGrid.virtualization.browser.test.tsx +0 -183
- package/lib/components/Header/Header.test.tsx +0 -135
- package/lib/components/Pagination/Pagination.test.tsx +0 -64
- package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
- package/lib/hooks/core/useGridControlledState.test.ts +0 -86
- package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
- package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
- package/lib/hooks/features/useAggregation.test.ts +0 -175
- package/lib/hooks/features/usePivot.test.ts +0 -202
- package/lib/hooks/useRowGrouping.test.ts +0 -298
- package/lib/hooks/useTreeData.test.ts +0 -83
- package/lib/utils/aggregation/aggregation.test.ts +0 -16
- package/lib/utils/export/exportToExcelAdvanced.test.ts +0 -109
- package/lib/utils/export/exportToPdf.test.ts +0 -120
- package/lib/utils/export/groupedExport.test.ts +0 -144
- package/lib/utils/filtering/filtering.test.ts +0 -178
- package/lib/utils/sorting/sorting.test.ts +0 -88
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenGridX — AI Agent Context
|
|
2
2
|
# Package: @opencorestack/opengridx
|
|
3
|
-
# Version:
|
|
3
|
+
# Version: 3.0.1
|
|
4
4
|
# Docs: https://opencorestack.github.io/OpenGridX/
|
|
5
5
|
# This file is machine-readable. AI developers: start here.
|
|
6
6
|
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
OpenGridX is a zero-dependency, high-performance React DataGrid library.
|
|
12
12
|
It matches and extends the feature set of MUI X DataGrid Pro — offered completely free.
|
|
13
13
|
|
|
14
|
-
- **React**: 18+ required
|
|
14
|
+
- **React**: 18+ required (react and react-dom are peer dependencies)
|
|
15
15
|
- **TypeScript**: Full typings shipped in `dist/index.d.ts`
|
|
16
|
-
- **
|
|
17
|
-
- **CSS**:
|
|
16
|
+
- **Optional peers**: `exceljs` (exportToExcelAdvanced), `jspdf` + `jspdf-autotable` (exportToPdf). Loaded lazily, never bundled.
|
|
17
|
+
- **CSS**: NOT loaded by the JS bundle. Always add `import '@opencorestack/opengridx/styles'` once in the app root.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -22,22 +22,26 @@ It matches and extends the feature set of MUI X DataGrid Pro — offered complet
|
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
npm install @opencorestack/opengridx
|
|
25
|
-
# Optional — for
|
|
25
|
+
# Optional — only for exportToExcelAdvanced:
|
|
26
26
|
npm install exceljs
|
|
27
|
+
# Optional — only for exportToPdf:
|
|
28
|
+
npm install jspdf jspdf-autotable
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
```tsx
|
|
30
32
|
import { DataGrid } from '@opencorestack/opengridx';
|
|
31
|
-
//
|
|
32
|
-
import '@opencorestack/opengridx/styles';
|
|
33
|
+
import '@opencorestack/opengridx/styles'; // required: the stylesheet is a separate file
|
|
33
34
|
```
|
|
34
35
|
|
|
36
|
+
Without the stylesheet the grid is unstyled AND the viewport does not scroll, so every row is rendered (virtualization is lost).
|
|
37
|
+
|
|
35
38
|
---
|
|
36
39
|
|
|
37
40
|
## MINIMAL EXAMPLE
|
|
38
41
|
|
|
39
42
|
```tsx
|
|
40
|
-
import { DataGrid, GridColDef } from '@opencorestack/opengridx';
|
|
43
|
+
import { DataGrid, type GridColDef } from '@opencorestack/opengridx';
|
|
44
|
+
import '@opencorestack/opengridx/styles';
|
|
41
45
|
|
|
42
46
|
const columns: GridColDef[] = [
|
|
43
47
|
{ field: 'id', headerName: 'ID', width: 70 },
|
|
@@ -56,37 +60,38 @@ export default function App() {
|
|
|
56
60
|
|
|
57
61
|
---
|
|
58
62
|
|
|
59
|
-
## DataGrid PROPS (DataGridProps<R extends GridRowModel>)
|
|
63
|
+
## DataGrid PROPS (DataGridProps<R extends GridValidRowModel = GridRowModel>)
|
|
60
64
|
|
|
61
65
|
### Required
|
|
62
66
|
| Prop | Type | Description |
|
|
63
67
|
|-----------|------------------|----------------------------------|
|
|
64
|
-
| rows |
|
|
68
|
+
| rows | R[] | Data array. Each row needs an id (or pass getRowId). Rows are never copied or mutated |
|
|
65
69
|
| columns | GridColDef<R>[] | Column definitions |
|
|
66
70
|
|
|
67
71
|
### Display
|
|
68
72
|
| Prop | Type | Default | Description |
|
|
69
73
|
|-------------------|-----------|---------|----------------------------------------|
|
|
70
|
-
| height | number/string | — | Grid height.
|
|
71
|
-
| autoHeight | boolean | false | Fit height to rows
|
|
72
|
-
| overscanRowCount | number | 3 | Minimum rows rendered outside the viewport (
|
|
74
|
+
| height | number/string | — | Grid height. No default: fills its container (class ogx--fill, 3.0.1+); the container must have a BOUNDED height or it renders every row (see GOTCHAS) |
|
|
75
|
+
| autoHeight | boolean | false | Fit height to rows (renders every row) |
|
|
76
|
+
| overscanRowCount | number | 3 | Minimum rows rendered outside the viewport (raised automatically while scrolling fast) |
|
|
73
77
|
| ariaLabel | string | — | Accessible name for the grid |
|
|
74
|
-
| localeText | GridLocaleText | — | Override
|
|
78
|
+
| localeText | GridLocaleText | — | Override pager / no-rows strings: paginationRowsPerPage, paginationOf(from,to,count), paginationPage(page,count), noRowsLabel |
|
|
75
79
|
| rowHeight | number | 52 | Row height in pixels |
|
|
76
80
|
| headerHeight | number | 56 | Header height in pixels |
|
|
77
|
-
| loading | boolean | false |
|
|
78
|
-
| noRowsLabel | string |
|
|
79
|
-
| density | 'compact'/'standard'/'comfortable' | 'standard' |
|
|
81
|
+
| loading | boolean | false | No rows: skeleton rows (or slots.loadingOverlay). Rows shown: they stay, a progress bar (.ogx__loading-bar) runs along the top, or slots.loadingOverlay is shown over them |
|
|
82
|
+
| noRowsLabel | string | 'No Data' | Empty state message |
|
|
83
|
+
| density | 'compact'/'standard'/'comfortable' | 'standard' | 32px / rowHeight / 72px rows |
|
|
80
84
|
|
|
81
85
|
### Selection
|
|
82
86
|
| Prop | Type | Description |
|
|
83
87
|
|----------------------------|-------------------------------|-------------------------------------|
|
|
84
|
-
| checkboxSelection | boolean | Enable checkbox column
|
|
85
|
-
| disableMultipleRowSelection | boolean | Cap selection to one row
|
|
88
|
+
| checkboxSelection | boolean | Enable checkbox column (header checkbox = select all filtered rows) |
|
|
89
|
+
| disableMultipleRowSelection | boolean | Cap selection to one row (checkboxes, Space, apiRef too); removes the select-all checkbox |
|
|
86
90
|
| pinCheckboxColumn | boolean (default true) | Pin the checkbox column left |
|
|
87
91
|
| rowSelectionModel | GridRowId[] | Controlled selection |
|
|
88
|
-
| onRowSelectionModelChange | (model: GridRowId[]) => void | Selection change callback
|
|
92
|
+
| onRowSelectionModelChange | (model: GridRowId[]) => void | Selection change callback. Also fires once with ids of removed rows pruned (client-owned rows) |
|
|
89
93
|
| disableRowSelectionOnClick | boolean | Prevent click toggling selection |
|
|
94
|
+
| disableClipboardCopy | boolean | Turn off the grid's Ctrl/Cmd+C copy (copySelectedRows still works) |
|
|
90
95
|
|
|
91
96
|
### Sorting
|
|
92
97
|
| Prop | Type | Description |
|
|
@@ -94,118 +99,125 @@ export default function App() {
|
|
|
94
99
|
| sortModel | GridSortItem[] | Controlled sort state |
|
|
95
100
|
| multiSort | boolean | Plain click appends to sortModel (Shift+click always does) |
|
|
96
101
|
| onSortModelChange | (model: GridSortItem[]) => void | Sort change callback |
|
|
97
|
-
| sortingMode | 'client' / 'server' |
|
|
102
|
+
| sortingMode | 'client' / 'server' | 'server': rows are shown in the order given |
|
|
98
103
|
|
|
99
104
|
### Filtering
|
|
100
105
|
| Prop | Type | Description |
|
|
101
106
|
|---------------------|--------------------------------|-----------------------------------|
|
|
102
|
-
| filterModel | GridFilterModel | Controlled filter state
|
|
107
|
+
| filterModel | GridFilterModel | Controlled filter state (optional: the grid keeps its own filter model otherwise) |
|
|
103
108
|
| onFilterModelChange | (model: GridFilterModel) => void | Filter change callback |
|
|
104
|
-
| filterMode | 'client' / 'server' |
|
|
109
|
+
| filterMode | 'client' / 'server' | 'server': rows are shown unfiltered as given |
|
|
105
110
|
|
|
106
111
|
### Pagination
|
|
107
112
|
| Prop | Type | Description |
|
|
108
113
|
|-------------------------|-----------------------------------------|----------------------------------|
|
|
109
|
-
| pagination | boolean |
|
|
110
|
-
| paginationModel | GridPaginationModel | {page, pageSize} controlled
|
|
111
|
-
| onPaginationModelChange | (model: GridPaginationModel) => void | Pagination change callback
|
|
112
|
-
| pageSizeOptions | number[]
|
|
113
|
-
| paginationMode | 'client' / 'server' / 'infinite' |
|
|
114
|
-
| rowCount | number |
|
|
114
|
+
| pagination | boolean | Show the pager. Ignored under rowGroupingModel and with paginationMode='infinite' |
|
|
115
|
+
| paginationModel | GridPaginationModel | {page, pageSize} controlled (pass onPaginationModelChange too) |
|
|
116
|
+
| onPaginationModelChange | (model: GridPaginationModel) => void | Pagination change callback (also fires when the grid clamps a page past the end) |
|
|
117
|
+
| pageSizeOptions | number[] (default [10, 25, 50, 100]) | Available page sizes |
|
|
118
|
+
| paginationMode | 'client' / 'server' / 'infinite' | 'server': rows is the current page. 'infinite': needs dataSource |
|
|
119
|
+
| rowCount | number | Server total for paginationMode='server' without dataSource (dataSource response rowCount wins) |
|
|
115
120
|
|
|
116
121
|
### Column Features
|
|
117
122
|
| Prop | Type | Description |
|
|
118
123
|
|---------------------------|---------------------------|-------------------------------------------|
|
|
119
|
-
| pinnedColumns | GridColumnPinning | {left: string[], right: string[]}
|
|
124
|
+
| pinnedColumns | GridColumnPinning | {left: string[], right: string[]}; rendered in that order |
|
|
120
125
|
| onPinnedColumnsChange | (model) => void | Pin change callback |
|
|
121
126
|
| columnVisibilityModel | Record<string, boolean> | {field: false} to hide |
|
|
122
127
|
| onColumnVisibilityModelChange | (model) => void | Visibility change callback |
|
|
123
128
|
| columnOrder | string[] | Controlled column order |
|
|
124
|
-
| onColumnOrderChange | (
|
|
129
|
+
| onColumnOrderChange | ({column, oldIndex, targetIndex}) => void | One column moved; indices in the full current column order |
|
|
130
|
+
| onColumnOrderModelChange | (columnOrder: string[]) => void | Whole new order after every change, incl. Columns panel Reset (use this to control columnOrder) |
|
|
125
131
|
| disableColumnReorder | boolean | Disable drag-to-reorder |
|
|
126
|
-
| columnGroupingModel | GridColumnGroupingModel | Multi-level column header groups
|
|
132
|
+
| columnGroupingModel | GridColumnGroupingModel | Multi-level column header groups; drag-reorder stays within a group |
|
|
127
133
|
|
|
128
134
|
### Row Features
|
|
129
135
|
| Prop | Type | Description |
|
|
130
136
|
|-------------------|---------------------------|-------------------------------------------|
|
|
131
|
-
| pinnedRows | GridRowPinning | {top: id[], bottom: id[]}
|
|
132
|
-
| rowReordering | boolean | Enable drag-and-drop row reordering
|
|
133
|
-
| onRowOrderChange | (
|
|
134
|
-
| getRowId | (row: R) => GridRowId | Custom row ID accessor (default: row.id)
|
|
137
|
+
| pinnedRows | GridRowPinning | {top: id[], bottom: id[]}. Ignored under treeData / rowGroupingModel (dev warning) |
|
|
138
|
+
| rowReordering | boolean | Enable drag-and-drop row reordering (no effect in pivot mode) |
|
|
139
|
+
| onRowOrderChange | ({row, oldIndex, targetIndex}) => void | Indices are positions in the `rows` prop |
|
|
140
|
+
| getRowId | (row: R) => GridRowId | Custom row ID accessor (default: row.id). Row objects are NOT given an `id` |
|
|
135
141
|
|
|
136
142
|
### Cell Editing
|
|
137
143
|
| Prop | Type | Description |
|
|
138
144
|
|-------------------------|----------------------------|-----------------------------------------|
|
|
139
|
-
| processRowUpdate | (newRow, oldRow) => R
|
|
140
|
-
| onProcessRowUpdateError | (error) => void |
|
|
141
|
-
| isCellEditable | (params) => boolean
|
|
145
|
+
| processRowUpdate | (newRow, oldRow) => R / Promise<R> | Called once per commit. Must return the updated row |
|
|
146
|
+
| onProcessRowUpdateError | (error) => void | processRowUpdate threw, rejected or returned no row (editor stays open) |
|
|
147
|
+
| isCellEditable | (params: GridCellParams) => boolean | Gates double-click, Enter and Tab editing; rejected cells get aria-readonly |
|
|
142
148
|
|
|
143
149
|
### Detail Panels (Master-Detail)
|
|
144
150
|
| Prop | Type | Description |
|
|
145
151
|
|-------------------------|----------------------------|-----------------------------------------|
|
|
146
|
-
| getDetailPanelContent | (params) => ReactNode
|
|
152
|
+
| getDetailPanelContent | (params: {row, id, rowIndex}) => ReactNode | Called for expanded data rows only (never group rows) |
|
|
147
153
|
| pinExpandColumn | boolean (default true) | Pin the detail-panel expand column |
|
|
148
|
-
| getDetailPanelHeight | (params) => number / 'auto'|
|
|
154
|
+
| getDetailPanelHeight | (params) => number / 'auto'| px, or 'auto' = measured content height. Default 200 |
|
|
149
155
|
| detailPanelExpandedRowIds | Set<GridRowId> | Controlled expanded rows |
|
|
150
|
-
| onDetailPanelExpandedRowIdsChange | (ids) => void | Expand/collapse callback
|
|
156
|
+
| onDetailPanelExpandedRowIdsChange | (ids: Set<GridRowId>) => void | Expand/collapse callback |
|
|
151
157
|
|
|
152
158
|
### Grouping & Aggregation
|
|
153
159
|
| Prop | Type | Description |
|
|
154
160
|
|------------------------|---------------------------|------------------------------------------|
|
|
155
|
-
| rowGroupingModel | string[] | Fields to group by (one nesting level per field). Disables pagination |
|
|
156
|
-
| aggregationModel | GridAggregationModel | {field: 'sum'/'avg'/'min'/'max'/'count'} |
|
|
157
|
-
| onAggregationModelChange | (model) => void | Aggregation model changed
|
|
158
|
-
| groupingColDef | Partial<GridColDef
|
|
159
|
-
| getAggregationPosition | (
|
|
160
|
-
| defaultGroupingExpansionDepth | number |
|
|
161
|
+
| rowGroupingModel | string[] | Fields to group by (one nesting level per field). Disables pagination. groupable:false fields are skipped |
|
|
162
|
+
| aggregationModel | GridAggregationModel | {field: 'sum'/'avg'/'min'/'max'/'count'/'unique'} |
|
|
163
|
+
| onAggregationModelChange | (model) => void | Aggregation model changed (toolbar Summaries panel) |
|
|
164
|
+
| groupingColDef | Partial<GridColDef<R>> | Adds a dedicated `__group__` column pinned left (rowGroupingModel or treeData); no `field` needed; never exported |
|
|
165
|
+
| getAggregationPosition | (groupNode: GridTreeNode / null) => 'inline'/'footer'/null | Per group: 'inline' (default) on the group row, 'footer' on a subtotal row, null hides. Called with null for the grand total: null hides the footer |
|
|
166
|
+
| defaultGroupingExpansionDepth | number | Initial expansion depth. Default 0 (all collapsed); -1 = expand all |
|
|
161
167
|
|
|
162
168
|
### Tree Data
|
|
163
169
|
| Prop | Type | Description |
|
|
164
170
|
|---------------------------|----------------------------|--------------------------------------|
|
|
165
171
|
| treeData | boolean | Enable tree data mode |
|
|
166
|
-
| getTreeDataPath | (row) => string[] |
|
|
167
|
-
| defaultGroupingExpansionDepth | number | Same as grouping
|
|
172
|
+
| getTreeDataPath | (row) => string[] | Required with treeData (without it rows show flat). Keep it stable (module scope / useCallback) |
|
|
173
|
+
| defaultGroupingExpansionDepth | number | Same as grouping (default 0) |
|
|
174
|
+
|
|
175
|
+
Missing path segments become auto-created parent rows `{ id }` (id like `auto-group-["a","b"]`); their label is `params.rowMeta.groupLabel`.
|
|
168
176
|
|
|
169
177
|
### Pivot
|
|
170
178
|
| Prop | Type | Description |
|
|
171
179
|
|----------------|----------------|-----------------------------------------------|
|
|
172
|
-
| pivotMode | boolean | Enable pivot view
|
|
173
|
-
| pivotModel | GridPivotModel | {rowFields, columnFields, valueFields}
|
|
174
|
-
| onPivotModelChange | (model) => void | Pivot change callback
|
|
180
|
+
| pivotMode | boolean | Enable pivot view (treeData / rowGrouping / getRowId / dataSource are ignored while pivoting) |
|
|
181
|
+
| pivotModel | GridPivotModel | {rowFields, columnFields, valueFields: {field, aggFn: 'sum'/'avg'/'count'/'min'/'max', headerName?}[]} |
|
|
182
|
+
| onPivotModelChange | (model) => void | Pivot change callback (toolbar Pivot panel) |
|
|
183
|
+
|
|
184
|
+
Pivot row ids look like `__pivot_row__:["North"]`; the Grand Total row is always last and cannot be selected.
|
|
175
185
|
|
|
176
186
|
### Server-Side
|
|
177
187
|
| Prop | Type | Description |
|
|
178
188
|
|------------|-----------------|----------------------------------------------------|
|
|
179
|
-
| dataSource | GridDataSource | Adapter for server-side fetching (see
|
|
189
|
+
| dataSource | GridDataSource | Adapter for server-side fetching (see SERVER-SIDE DATA SOURCE) |
|
|
180
190
|
|
|
181
191
|
### Styling & Customization
|
|
182
192
|
| Prop | Type | Description |
|
|
183
193
|
|--------------|-----------------------|------------------------------------------|
|
|
184
|
-
| slots |
|
|
185
|
-
| slotProps |
|
|
194
|
+
| slots | { toolbar, pagination, noRowsOverlay, loadingOverlay, footer } | Replace built-in parts |
|
|
195
|
+
| slotProps | { toolbar, pagination, noRowsOverlay, loadingOverlay, footer } | Extra props for those slots |
|
|
186
196
|
| className | string | Extra CSS class on the root element |
|
|
187
197
|
| style | React.CSSProperties | Inline styles on the root element (use DataGridThemeProvider for theming) |
|
|
188
198
|
|
|
199
|
+
There are no getRowClassName / getCellClassName / theme props: use GridColDef.cellClassName (string or function) and DataGridThemeProvider.
|
|
200
|
+
|
|
189
201
|
### Callbacks
|
|
190
202
|
| Prop | Type | Description |
|
|
191
203
|
|-------------------|-----------------------|-------------------------------|
|
|
192
|
-
| onRowClick | (params) => void
|
|
193
|
-
| onCellClick | (params) => void
|
|
194
|
-
| onRowDoubleClick | (params) => void | Row double-click
|
|
195
|
-
| onRowsScrollEnd | (params) => void |
|
|
196
|
-
| onStateChange | (state) => void
|
|
204
|
+
| onRowClick | (params: {row, id, rowIndex}) => void | Row click, or Enter on a non-editable cell. Not fired for synthetic group rows (a click toggles them) |
|
|
205
|
+
| onCellClick | (params: GridCellParams) => void | Cell click (not inside an open editor) |
|
|
206
|
+
| onRowDoubleClick | (params) => void | Row double-click (also fires for group rows) |
|
|
207
|
+
| onRowsScrollEnd | (params) => void | Fires once each time the bottom comes into view (re-armed when rows are added or the user scrolls away) |
|
|
208
|
+
| onStateChange | (state: GridState) => void | Fires when sorting, filter, pagination, columns (widths/order/pinning/visibility) or density change |
|
|
197
209
|
|
|
198
210
|
### State Persistence
|
|
199
211
|
| Prop | Type | Description |
|
|
200
212
|
|--------------|------------------|----------------------------------------------|
|
|
201
|
-
| initialState | GridInitialState |
|
|
202
|
-
| apiRef |
|
|
213
|
+
| initialState | GridInitialState | Starting sorting, filter, pagination, columns (widths, order, pinning, visibility) and density. Read at mount only |
|
|
214
|
+
| apiRef | MutableRefObject<GridApi<R>> or MutableRefObject<GridApi> | Imperative API handle from useGridApiRef<R>() (typed row getters) or useGridApiRef() |
|
|
203
215
|
|
|
204
216
|
### List View
|
|
205
217
|
| Prop | Type | Description |
|
|
206
218
|
|----------------|--------------------|---------------------------------------------|
|
|
207
|
-
| listView | boolean | Render as
|
|
208
|
-
| listViewColumn |
|
|
219
|
+
| listView | boolean | Render as cards instead of a table (requires listViewColumn; without it the grid view shows + dev warning). Honours loading, noRowsOverlay/loadingOverlay and slots.footer; rows are a roving Tab stop (arrows, Home/End, PageUp/Down, Enter/Space, Alt+Arrow expand); tree/group parents get a chevron. Not virtualized (dev warning above 2,000 items; paginate) |
|
|
220
|
+
| listViewColumn | { field, renderCell } | Defines the card renderer |
|
|
209
221
|
|
|
210
222
|
---
|
|
211
223
|
|
|
@@ -215,31 +227,37 @@ export default function App() {
|
|
|
215
227
|
|-------------------|--------------------------------------------------------|--------------------------------------------|
|
|
216
228
|
| field | string | **Required.** Key in row data |
|
|
217
229
|
| headerName | string | Column label |
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
|
|
|
221
|
-
|
|
|
222
|
-
|
|
|
223
|
-
|
|
|
224
|
-
|
|
|
225
|
-
|
|
|
230
|
+
| description | string | Header tooltip |
|
|
231
|
+
| width | number / string (px or %) | Column width. Neither width nor flex = flex: 1 |
|
|
232
|
+
| flex | number | Flex grow factor; `width` is ignored when flex is set |
|
|
233
|
+
| minWidth | number | Minimum width (layout and resize; resize default 50) |
|
|
234
|
+
| maxWidth | number | Maximum width (layout and resize; no max by default) |
|
|
235
|
+
| type | 'string'/'number'/'boolean'/'date'/'singleSelect'/'image' | Drives filter operators, sorting, editors and, without a valueFormatter, the cell text (date: local calendar date, boolean: Yes/No, singleSelect: option label, image: <img>); formattedValue, list view and exports use the same text |
|
|
236
|
+
| editable | boolean | Enable double-click / Enter editing |
|
|
237
|
+
| valueOptions | (string / number / {value, label})[] | Options for singleSelect type (editor commits the original option value) |
|
|
238
|
+
| renderCell | (params: GridRenderCellParams<R>) => ReactNode | Custom cell renderer (errors contained per cell) |
|
|
226
239
|
| renderHeader | (params: GridRenderHeaderParams) => ReactNode | Custom header renderer |
|
|
227
|
-
| renderEditCell | (params:
|
|
228
|
-
| valueGetter | ({row, field, value}) =>
|
|
229
|
-
|
|
|
240
|
+
| renderEditCell | (params: GridRenderEditCellParams<R>) => ReactNode | Custom editor: call params.onValueChange(v), params.onCommit(), params.onCancel() |
|
|
241
|
+
| valueGetter | ({row, field, value}) => unknown | Computed value; used by sort, filter, quick search, aggregation, copy, export. Must be pure (quick filter caches per row object); a throw reads as undefined + dev warning |
|
|
242
|
+
| valueSetter | ({value, row, field}) => R | Write an edited value back onto the row (editable valueGetter columns) |
|
|
243
|
+
| valueFormatter | ({value, row, field}) => string | Format display value (value is `unknown`: cast before calling methods) |
|
|
230
244
|
| cellClassName | string / (params) => string | CSS class on cells |
|
|
231
245
|
| headerClassName | string | CSS class on header cell |
|
|
232
|
-
| colSpan | number / (params) => number | Cell column span
|
|
233
|
-
| rowSpan | number / (params) => number | Cell row span
|
|
234
|
-
| align | 'left'/'center'/'right'
|
|
235
|
-
| headerAlign | 'left'/'center'/'right'
|
|
246
|
+
| colSpan | number / (params) => number | Cell column span (clamped to the pinned section; hidden columns skipped) |
|
|
247
|
+
| rowSpan | number / (params) => number | Cell row span (clamped to the row section) |
|
|
248
|
+
| align | 'left'/'center'/'right' | Cell content alignment |
|
|
249
|
+
| headerAlign | 'left'/'center'/'right' | Header content alignment |
|
|
236
250
|
| sortable | boolean (default true) | Enable sorting |
|
|
237
|
-
| filterable | boolean (default true) | Enable filtering
|
|
238
|
-
| resizable | boolean (default true) | Enable resize handle
|
|
239
|
-
| hideable | boolean (default true) | Allow hiding via toolbar
|
|
240
|
-
| pinnable | boolean (default true) | Allow pinning via column menu
|
|
241
|
-
| exportable | boolean (default true) | Include in CSV/Excel/JSON/Print exports
|
|
251
|
+
| filterable | boolean (default true) | Enable filtering and quick search |
|
|
252
|
+
| resizable | boolean (default true) | Enable resize handle (mouse, touch, Alt+Arrow on header) |
|
|
253
|
+
| hideable | boolean (default true) | Allow hiding via toolbar / column menu |
|
|
254
|
+
| pinnable | boolean (default true) | Allow pinning via column menu (does not affect drag-reorder) |
|
|
255
|
+
| exportable | boolean (default true) | Include in CSV/Excel/JSON/Print/PDF exports and clipboard copy |
|
|
242
256
|
| disableColumnMenu | boolean | Hide the ⋮ column menu button |
|
|
257
|
+
| groupable | boolean | false = skipped by rowGroupingModel and the pivot panel |
|
|
258
|
+
| groupingValueFormatter | ({field, value}) => string | Group row label (default "field: value") |
|
|
259
|
+
| aggregable | boolean | false = no aggregation. Summaries panel offers number columns + aggregable: true |
|
|
260
|
+
| availableAggregationFunctions | string[] | Restrict aggregation functions for this column |
|
|
243
261
|
|
|
244
262
|
---
|
|
245
263
|
|
|
@@ -248,37 +266,45 @@ export default function App() {
|
|
|
248
266
|
```tsx
|
|
249
267
|
import { useGridApiRef } from '@opencorestack/opengridx';
|
|
250
268
|
const apiRef = useGridApiRef();
|
|
251
|
-
<DataGrid apiRef={apiRef}
|
|
269
|
+
<DataGrid apiRef={apiRef} rows={rows} columns={columns} />
|
|
252
270
|
|
|
253
271
|
// Row access
|
|
254
|
-
apiRef.current.getRow(id)
|
|
255
|
-
apiRef.current.getAllRows()
|
|
256
|
-
apiRef.current.getVisibleRows()
|
|
272
|
+
apiRef.current.getRow(id) // → R | null with useGridApiRef<R>(), else GridRowModel | null
|
|
273
|
+
apiRef.current.getAllRows() // → every loaded row
|
|
274
|
+
apiRef.current.getVisibleRows() // → rows on screen: current page + pinned rows
|
|
275
|
+
apiRef.current.getAllFilteredRows() // → every filtered + sorted row, all pages (use for exports); tree data + filter: matching rows only
|
|
276
|
+
apiRef.current.getGroupedExportRows() // → GridGroupedExportRow[] | null (row grouping only)
|
|
257
277
|
|
|
258
278
|
// Column access
|
|
259
|
-
apiRef.current.getColumn(field)
|
|
260
|
-
apiRef.current.getAllColumns()
|
|
261
|
-
apiRef.current.getVisibleColumns()
|
|
279
|
+
apiRef.current.getColumn(field) // → GridColDef | null
|
|
280
|
+
apiRef.current.getAllColumns() // → GridColDef[]
|
|
281
|
+
apiRef.current.getVisibleColumns() // → visible columns in render order (left-pinned, unpinned, right-pinned)
|
|
262
282
|
|
|
263
283
|
// Selection
|
|
264
|
-
apiRef.current.selectRow(id, isSelected?)
|
|
265
|
-
apiRef.current.selectRows(ids, isSelected?) // bulk select
|
|
266
|
-
apiRef.current.getSelectedRows()
|
|
284
|
+
apiRef.current.selectRow(id, isSelected?) // select/deselect a row (synthetic group/total ids ignored; no-op fires nothing)
|
|
285
|
+
apiRef.current.selectRows(ids, isSelected?) // bulk select (same rules)
|
|
286
|
+
apiRef.current.getSelectedRows() // → GridRowId[]
|
|
267
287
|
|
|
268
|
-
// Sort / Filter
|
|
288
|
+
// Sort / Filter (setters update the grid and fire the change callbacks)
|
|
269
289
|
apiRef.current.sortColumn(field, 'asc' | 'desc' | null)
|
|
270
|
-
apiRef.current.getSortModel()
|
|
290
|
+
apiRef.current.getSortModel() // → GridSortItem[]
|
|
271
291
|
apiRef.current.setFilterModel(model)
|
|
292
|
+
apiRef.current.getFilterModel() // → GridFilterModel
|
|
272
293
|
|
|
273
294
|
// Pagination
|
|
274
295
|
apiRef.current.setPage(page)
|
|
275
296
|
apiRef.current.setPageSize(size)
|
|
276
297
|
|
|
298
|
+
// Scroll
|
|
299
|
+
apiRef.current.scrollToIndexes({ rowIndex, colIndex })
|
|
300
|
+
|
|
277
301
|
// Clipboard
|
|
278
|
-
apiRef.current.copySelectedRows()
|
|
302
|
+
apiRef.current.copySelectedRows() // → Promise<void>; rejects if the write fails. Copies every selected row that passes
|
|
303
|
+
// the filter (all pages, collapsed groups, pinned rows), visible columns in screen order
|
|
279
304
|
|
|
280
|
-
// Aggregation
|
|
281
|
-
apiRef.current.getAggregationResult()
|
|
305
|
+
// Aggregation
|
|
306
|
+
apiRef.current.getAggregationResult() // → Record<string, unknown> | null (null in pivot mode)
|
|
307
|
+
apiRef.current.getAggregationModel() // → GridAggregationModel | null
|
|
282
308
|
```
|
|
283
309
|
|
|
284
310
|
---
|
|
@@ -286,10 +312,13 @@ apiRef.current.getAggregationResult() // → GridAggregationResult | null
|
|
|
286
312
|
## CELL EDITING — FULL EXAMPLE
|
|
287
313
|
|
|
288
314
|
```tsx
|
|
289
|
-
|
|
315
|
+
// Your own row type: an interface or a type alias, no index signature needed (v3+)
|
|
316
|
+
interface Person { id: number; name: string; age: number; role: string }
|
|
317
|
+
|
|
318
|
+
const [rows, setRows] = useState<Person[]>(initialRows);
|
|
290
319
|
|
|
291
|
-
const processRowUpdate = (newRow
|
|
292
|
-
setRows(prev => prev.map(r => r.id === newRow.id ? newRow : r));
|
|
320
|
+
const processRowUpdate = (newRow: Person) => {
|
|
321
|
+
setRows(prev => prev.map(r => (r.id === newRow.id ? newRow : r)));
|
|
293
322
|
return newRow; // Must return the committed row
|
|
294
323
|
};
|
|
295
324
|
|
|
@@ -306,41 +335,57 @@ const processRowUpdate = (newRow, oldRow) => {
|
|
|
306
335
|
/>
|
|
307
336
|
```
|
|
308
337
|
|
|
338
|
+
Double-click or Enter opens the editor; Enter / Tab / blur commits; Escape cancels. An edit whose row scrolls, pages or filters away is committed, not dropped.
|
|
339
|
+
|
|
309
340
|
---
|
|
310
341
|
|
|
311
342
|
## SERVER-SIDE DATA SOURCE
|
|
312
343
|
|
|
344
|
+
Pass a `dataSource` object to DataGrid; there is no separate data-source hook to import.
|
|
345
|
+
|
|
313
346
|
```tsx
|
|
314
|
-
import {
|
|
347
|
+
import { DataGrid, type GridDataSource } from '@opencorestack/opengridx';
|
|
315
348
|
|
|
316
349
|
const dataSource: GridDataSource = {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
350
|
+
// params: { startRow, endRow, sortModel, filterModel, groupKeys, aggregationModel? }
|
|
351
|
+
getRows: async ({ startRow, endRow, sortModel, filterModel, groupKeys }) => {
|
|
352
|
+
const res = await fetch('/api/rows', {
|
|
353
|
+
method: 'POST',
|
|
354
|
+
body: JSON.stringify({ startRow, endRow, sortModel, filterModel, groupKeys }),
|
|
355
|
+
});
|
|
356
|
+
const data = await res.json();
|
|
357
|
+
return { rows: data.items, rowCount: data.total }; // rowCount optional, needed for server paging
|
|
358
|
+
},
|
|
359
|
+
// Optional: totals for the aggregation footer
|
|
360
|
+
getAggregations: async ({ sortModel, filterModel, aggregationModel }) => {
|
|
361
|
+
return fetch('/api/totals').then(r => r.json());
|
|
320
362
|
},
|
|
321
|
-
// Optional: lazy-load children for server-side tree data
|
|
322
|
-
getChildren: async ({ row }) => {
|
|
323
|
-
return fetch(`/api/rows/${row.id}/children`).then(r => r.json());
|
|
324
|
-
}
|
|
325
363
|
};
|
|
326
364
|
|
|
327
365
|
<DataGrid
|
|
328
366
|
columns={columns}
|
|
329
|
-
rows={[]} //
|
|
367
|
+
rows={[]} // data comes from dataSource
|
|
330
368
|
dataSource={dataSource}
|
|
369
|
+
pagination
|
|
331
370
|
paginationMode="server"
|
|
332
371
|
sortingMode="server"
|
|
333
372
|
filterMode="server"
|
|
334
|
-
rowCount={1000}
|
|
335
373
|
/>
|
|
336
374
|
```
|
|
337
375
|
|
|
376
|
+
- Requests are page-based: server pagination asks for `[page*pageSize, (page+1)*pageSize)`; client pagination (default modes) asks once for every row (`endRow: Number.MAX_SAFE_INTEGER`); `paginationMode="infinite"` asks for the rows after those loaded, up to `(page+1)*pageSize`, and appends them (advance `paginationModel.page` from `onRowsScrollEnd`).
|
|
377
|
+
- `endRow` is exclusive: return `allRows.slice(startRow, endRow)`.
|
|
378
|
+
- Server tree data: rows with `serverChildrenCount > 0` get an expand toggle; expanding calls `getRows` with the row's path in `groupKeys`. There is no `getChildren`.
|
|
379
|
+
- Refetch happens only when a model's content or the `getRows` function changes; stale responses are discarded; 300 ms debounce.
|
|
380
|
+
- A rejected `getRows` (or a response that cannot be processed) shows the error overlay; its Retry button calls `getRows` again.
|
|
381
|
+
- Row grouping with `paginationMode="server"` fetches every row in one request (`[0, Number.MAX_SAFE_INTEGER)`, dev warning). Server tree data with `defaultGroupingExpansionDepth` auto-expands and loads lazy nodes (one request per node).
|
|
382
|
+
|
|
338
383
|
---
|
|
339
384
|
|
|
340
385
|
## THEMING
|
|
341
386
|
|
|
342
387
|
```tsx
|
|
343
|
-
import { DataGridThemeProvider, darkTheme } from '@opencorestack/opengridx';
|
|
388
|
+
import { DataGridThemeProvider, darkTheme, type GridTheme } from '@opencorestack/opengridx';
|
|
344
389
|
|
|
345
390
|
// Built-in themes: darkTheme, roseTheme, emeraldTheme, amberTheme, compactTheme
|
|
346
391
|
<DataGridThemeProvider theme={darkTheme}>
|
|
@@ -348,25 +393,27 @@ import { DataGridThemeProvider, darkTheme } from '@opencorestack/opengridx';
|
|
|
348
393
|
</DataGridThemeProvider>
|
|
349
394
|
|
|
350
395
|
// Custom theme override:
|
|
351
|
-
const myTheme = {
|
|
396
|
+
const myTheme: GridTheme = {
|
|
397
|
+
mode: 'light', // base palette the provider pins ('light' default, or 'dark')
|
|
352
398
|
colors: {
|
|
353
399
|
primary: '#6366f1',
|
|
354
400
|
},
|
|
355
401
|
grid: {
|
|
356
|
-
headerBackground: '#
|
|
357
|
-
rowHoverBackground: '#
|
|
402
|
+
headerBackground: '#eef2ff',
|
|
403
|
+
rowHoverBackground: '#f5f3ff',
|
|
404
|
+
rowHeightStandard: '40px', // theme heights size rows/header; rowHeight/headerHeight props win
|
|
358
405
|
},
|
|
359
406
|
overlays: {
|
|
360
|
-
background: '#
|
|
361
|
-
border: '#
|
|
362
|
-
}
|
|
407
|
+
background: '#ffffff',
|
|
408
|
+
border: '#e0e7ff',
|
|
409
|
+
},
|
|
363
410
|
};
|
|
364
411
|
<DataGridThemeProvider theme={myTheme}>
|
|
365
|
-
<DataGrid
|
|
412
|
+
<DataGrid rows={rows} columns={columns} />
|
|
366
413
|
</DataGridThemeProvider>
|
|
367
414
|
|
|
368
|
-
//
|
|
369
|
-
// `.ogx-theme-provider
|
|
415
|
+
// The provider pins a complete light or dark palette regardless of the OS colour scheme.
|
|
416
|
+
// Panels, menus and tooltips render into `.ogx-theme-provider`, so they inherit the theme.
|
|
370
417
|
```
|
|
371
418
|
|
|
372
419
|
---
|
|
@@ -376,21 +423,24 @@ const myTheme = {
|
|
|
376
423
|
```tsx
|
|
377
424
|
import { GridToolbar } from '@opencorestack/opengridx';
|
|
378
425
|
|
|
379
|
-
// Replace built-in components:
|
|
380
426
|
<DataGrid
|
|
427
|
+
rows={rows}
|
|
428
|
+
columns={columns}
|
|
381
429
|
slots={{
|
|
382
|
-
toolbar:
|
|
383
|
-
pagination: MyPagination,
|
|
430
|
+
toolbar: GridToolbar, // or your own component
|
|
431
|
+
pagination: MyPagination, // props: page, pageSize, rowCount, pageSizeOptions, onPageChange, onPageSizeChange, localeText
|
|
384
432
|
noRowsOverlay: EmptyState,
|
|
385
433
|
loadingOverlay: Spinner,
|
|
386
|
-
footer: CustomFooter,
|
|
434
|
+
footer: CustomFooter, // replaces the pager; see note 14
|
|
387
435
|
}}
|
|
388
436
|
slotProps={{
|
|
389
|
-
toolbar: {
|
|
437
|
+
toolbar: { showNonHideableColumns: true },
|
|
390
438
|
}}
|
|
391
439
|
/>
|
|
392
440
|
```
|
|
393
441
|
|
|
442
|
+
`GridToolbar` in `slots.toolbar` gets search, Columns, Filters and Summaries (plus Pivot when pivot is configured); they work without any filter/visibility props. Render props: `renderQuickFilter`, `renderColumnsButton`, `renderFilterButton`, `renderAggregationButton`, `renderExportButton`.
|
|
443
|
+
|
|
394
444
|
---
|
|
395
445
|
|
|
396
446
|
## STATE PERSISTENCE
|
|
@@ -399,11 +449,11 @@ import { GridToolbar } from '@opencorestack/opengridx';
|
|
|
399
449
|
import { useGridStateStorage } from '@opencorestack/opengridx';
|
|
400
450
|
|
|
401
451
|
function MyGrid() {
|
|
402
|
-
const { initialState,
|
|
452
|
+
const { initialState, onStateChange, clearState } = useGridStateStorage('my-grid-v1');
|
|
403
453
|
return (
|
|
404
454
|
<DataGrid
|
|
405
455
|
initialState={initialState}
|
|
406
|
-
onStateChange={
|
|
456
|
+
onStateChange={onStateChange}
|
|
407
457
|
rows={rows}
|
|
408
458
|
columns={columns}
|
|
409
459
|
/>
|
|
@@ -411,6 +461,8 @@ function MyGrid() {
|
|
|
411
461
|
}
|
|
412
462
|
```
|
|
413
463
|
|
|
464
|
+
Also accepts `{ key, debounceMs?, include?: (keyof GridState)[], storage? }`. `initialState` is read at mount: when the key changes, remount with `<DataGrid key={key} />`. If storage is blocked, the grid works without persistence.
|
|
465
|
+
|
|
414
466
|
---
|
|
415
467
|
|
|
416
468
|
## EXPORT UTILITIES
|
|
@@ -418,60 +470,85 @@ function MyGrid() {
|
|
|
418
470
|
```tsx
|
|
419
471
|
import {
|
|
420
472
|
exportToCsv,
|
|
421
|
-
exportToExcel, // HTML
|
|
422
|
-
exportToExcelAdvanced, //
|
|
473
|
+
exportToExcel, // HTML table saved as .xls — zero deps
|
|
474
|
+
exportToExcelAdvanced, // real .xlsx — requires: npm install exceljs
|
|
423
475
|
exportToJson,
|
|
476
|
+
exportToPdf, // requires: npm install jspdf jspdf-autotable
|
|
424
477
|
printGrid,
|
|
425
478
|
} from '@opencorestack/opengridx';
|
|
426
479
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
480
|
+
const rows = apiRef.current.getAllFilteredRows(); // what the grid shows, all pages
|
|
481
|
+
const columns = apiRef.current.getVisibleColumns(); // visible columns, display order
|
|
482
|
+
|
|
483
|
+
exportToCsv(rows, columns, { fileName: 'export.csv' }); // UTF-8 BOM (bom: false to omit)
|
|
484
|
+
exportToExcel(rows, columns, { fileName: 'export.xls', sheetName: 'Data' });
|
|
485
|
+
await exportToExcelAdvanced(rows, columns, {
|
|
430
486
|
fileName: 'export.xlsx',
|
|
431
|
-
|
|
487
|
+
sheets: [{ name: 'Data' }, { name: 'Selected', rows: 'selected' }],
|
|
488
|
+
selectedRows: apiRef.current.getSelectedRows(),
|
|
432
489
|
columnStyles: {
|
|
433
490
|
avatar: { embedImage: true, imageWidth: 32, imageHeight: 32 }
|
|
434
491
|
}
|
|
435
492
|
});
|
|
436
493
|
exportToJson(rows, columns, { fileName: 'export.json' });
|
|
437
|
-
|
|
494
|
+
await exportToPdf(rows, columns, { fileName: 'export', title: 'My Report' });
|
|
495
|
+
await printGrid(rows, columns, 'My Report Title');
|
|
438
496
|
```
|
|
439
497
|
|
|
498
|
+
- Every export accepts `selectedRows`, `getRowId` (pass the grid's getRowId so ids match), `aggregationResult` + `aggregationModel` (totals row) and `groupedRows` from `apiRef.current.getGroupedExportRows()`.
|
|
499
|
+
- CSV and exportToExcel neutralise formulas: text starting with = + - @ tab or CR gets a leading `'` (`escapeFormulas: false` opts out).
|
|
500
|
+
- exportToExcel writes HTML: a `.xlsx` fileName is saved as `.xls` with a console warning.
|
|
501
|
+
- Columns with `exportable: false` are left out everywhere; `valueGetter` and `valueFormatter` are applied.
|
|
502
|
+
|
|
440
503
|
---
|
|
441
504
|
|
|
442
505
|
## CSS CUSTOMIZATION
|
|
443
506
|
|
|
444
|
-
All CSS variables use the `--ogx-` prefix.
|
|
507
|
+
All CSS variables use the `--ogx-` prefix and are declared on `:root` by the stylesheet. Override them on a wrapper (or use DataGridThemeProvider, which sets the same variables):
|
|
445
508
|
|
|
446
509
|
```css
|
|
447
510
|
.my-grid-wrapper {
|
|
448
511
|
--ogx-color-primary: #6366f1;
|
|
449
|
-
--ogx-
|
|
450
|
-
--ogx-
|
|
451
|
-
--ogx-
|
|
452
|
-
--ogx-font-size
|
|
453
|
-
--ogx-border-radius: 8px;
|
|
512
|
+
--ogx-grid-header-background: #eef2ff;
|
|
513
|
+
--ogx-grid-row-hover-background: rgba(99, 102, 241, 0.08);
|
|
514
|
+
--ogx-grid-border-color: #e0e7ff;
|
|
515
|
+
--ogx-grid-cell-font-size: 13px;
|
|
516
|
+
--ogx-border-radius-md: 8px;
|
|
454
517
|
}
|
|
455
518
|
```
|
|
456
519
|
|
|
457
|
-
|
|
458
|
-
|
|
520
|
+
Derived tokens (e.g. `--ogx-grid-cell-focus-border`, which defaults to the primary colour) are resolved on `:root`, so override them directly when you change `--ogx-color-primary` on a wrapper.
|
|
521
|
+
|
|
522
|
+
Class names use the `ogx__` BEM prefix, e.g.:
|
|
523
|
+
- `ogx__row`, `ogx__row--selected`, `ogx__row--even`, `ogx__row--odd`
|
|
459
524
|
- `ogx__cell`, `ogx__cell--focused`, `ogx__cell--editing`
|
|
460
525
|
- `ogx__header-cell`, `ogx__header-cell--sorted`
|
|
526
|
+
- Sticky wrappers: `ogx__sticky-top` (header + top-pinned rows), `ogx__sticky-bottom` (bottom-pinned rows + aggregation footer)
|
|
461
527
|
|
|
462
528
|
---
|
|
463
529
|
|
|
464
530
|
## TYPE REFERENCE
|
|
465
531
|
|
|
466
532
|
```ts
|
|
467
|
-
type GridRowModel = Record<string, any> & { id: GridRowId };
|
|
468
533
|
type GridRowId = string | number;
|
|
534
|
+
interface GridRowModel { id: GridRowId; [key: string]: unknown; }
|
|
469
535
|
type GridSortDirection = 'asc' | 'desc' | null;
|
|
470
|
-
type GridPinnedPosition = 'left' | 'right'
|
|
536
|
+
type GridPinnedPosition = 'left' | 'right';
|
|
537
|
+
|
|
538
|
+
type GridFilterOperator =
|
|
539
|
+
| 'contains' | 'equals' | 'startsWith' | 'endsWith' // string
|
|
540
|
+
| 'isEmpty' | 'isNotEmpty' | 'isAnyOf'
|
|
541
|
+
| '=' | '!=' | '>' | '>=' | '<' | '<=' // number
|
|
542
|
+
| 'is' | 'not' | 'after' | 'onOrAfter' | 'before' | 'onOrBefore'; // date / boolean / singleSelect
|
|
471
543
|
|
|
472
544
|
interface GridSortItem { field: string; sort: 'asc' | 'desc'; }
|
|
473
|
-
interface GridFilterItem { field: string; operator:
|
|
474
|
-
interface
|
|
545
|
+
interface GridFilterItem { id?: string | number; field: string; operator: GridFilterOperator; value?: unknown; }
|
|
546
|
+
interface GridFilterGroup { id?: string | number; logicOperator: 'and' | 'or'; items: (GridFilterItem | GridFilterGroup)[]; }
|
|
547
|
+
interface GridFilterModel {
|
|
548
|
+
items?: (GridFilterItem | GridFilterGroup)[];
|
|
549
|
+
logicOperator?: 'and' | 'or';
|
|
550
|
+
quickFilterValues?: string[]; // every term must match some visible, filterable column
|
|
551
|
+
}
|
|
475
552
|
interface GridPaginationModel { page: number; pageSize: number; }
|
|
476
553
|
interface GridColumnPinning { left?: string[]; right?: string[]; }
|
|
477
554
|
interface GridRowPinning { top?: GridRowId[]; bottom?: GridRowId[]; }
|
|
@@ -481,33 +558,53 @@ interface GridRenderCellParams<R = GridRowModel> {
|
|
|
481
558
|
colDef: GridColDef<R>; rowIndex: number; colIndex: number;
|
|
482
559
|
rowMeta?: GridRowMeta; // hierarchy info for tree-data / grouping rows
|
|
483
560
|
}
|
|
561
|
+
interface GridRowMeta {
|
|
562
|
+
hasChildren?: boolean; treeDepth?: number; groupingField?: string; groupingValue?: unknown;
|
|
563
|
+
groupLabel?: string; descendantCount?: number; isExpanded?: boolean;
|
|
564
|
+
isGroupRow?: boolean; isGroupFooter?: boolean;
|
|
565
|
+
}
|
|
484
566
|
|
|
485
|
-
interface GridDataSource {
|
|
486
|
-
getRows: (params: GridGetRowsParams) => Promise<GridGetRowsResponse
|
|
487
|
-
|
|
567
|
+
interface GridDataSource<R = GridRowModel> {
|
|
568
|
+
getRows: (params: GridGetRowsParams) => Promise<GridGetRowsResponse<R>>;
|
|
569
|
+
getAggregations?: (params: Omit<GridGetRowsParams, 'startRow' | 'endRow'>) => Promise<Record<string, unknown>>;
|
|
488
570
|
}
|
|
489
571
|
interface GridGetRowsParams {
|
|
490
|
-
|
|
572
|
+
startRow: number; endRow: number; // endRow exclusive; Number.MAX_SAFE_INTEGER = all rows
|
|
491
573
|
sortModel: GridSortItem[]; filterModel: GridFilterModel;
|
|
574
|
+
groupKeys: string[]; // server tree data: path of the parent being expanded
|
|
575
|
+
aggregationModel?: GridAggregationModel;
|
|
576
|
+
}
|
|
577
|
+
interface GridGetRowsResponse<R = GridRowModel> {
|
|
578
|
+
rows: R[]; rowCount?: number; aggregationResults?: Record<string, unknown>;
|
|
492
579
|
}
|
|
493
|
-
interface GridGetRowsResponse { rows: GridRowModel[]; rowCount: number; }
|
|
494
580
|
```
|
|
495
581
|
|
|
496
582
|
---
|
|
497
583
|
|
|
584
|
+
## KEYBOARD
|
|
585
|
+
|
|
586
|
+
Arrows move between cells (skipping hidden columns); Home/End, Ctrl+Home/End, PageUp/PageDown; Enter edits an editable cell, otherwise acts like a row click (toggles group/tree rows); Tab leaves the grid (while editing it moves to the next editable cell); Shift+Space selects the row (Space toggles the checkbox or detail panel when those cells are focused); Ctrl/Cmd+A selects all; Ctrl/Cmd+C copies the selection; Alt+ArrowRight/Left expands/collapses; Enter/Space on a header sorts (Shift adds to the sort); Alt+ArrowDown or Ctrl+Enter on a header opens the column menu; Alt+ArrowLeft/Right on a header resizes it.
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
498
590
|
## KEY NOTES FOR AI AGENTS
|
|
499
591
|
|
|
500
|
-
1. **Always `return newRow`** from `processRowUpdate` —
|
|
501
|
-
2. **`getRowId` default is `row.id`** — if your data uses a different key, always pass `getRowId`.
|
|
502
|
-
3. **CSS must be imported**:
|
|
503
|
-
4. **
|
|
504
|
-
5. **`dataSource` + `rows={[]}`**:
|
|
505
|
-
6. **`flex` columns**: columns with `flex`
|
|
592
|
+
1. **Always `return newRow`** from `processRowUpdate` — returning nothing calls `onProcessRowUpdateError` and keeps the editor open.
|
|
593
|
+
2. **`getRowId` default is `row.id`** — if your data uses a different key, always pass `getRowId`. The grid does not write an `id` onto your rows, and exports need `getRowId` in their options to match `selectedRows`.
|
|
594
|
+
3. **CSS must be imported**: always add `import '@opencorestack/opengridx/styles'` to the app root. The JS bundle does not load it; dev builds warn once ("The grid stylesheet is not loaded") when it is missing.
|
|
595
|
+
4. **Optional peers**: `exportToExcelAdvanced` needs `exceljs`; `exportToPdf` needs `jspdf` + `jspdf-autotable`.
|
|
596
|
+
5. **`dataSource` + `rows={[]}`**: with a dataSource pass an empty `rows` array — the grid fills it via `dataSource.getRows`.
|
|
597
|
+
6. **`flex` columns**: columns with `flex` share the free space; `width` is ignored on them (use `minWidth` / `maxWidth`). A column with neither `width` nor `flex` acts like `flex: 1`.
|
|
506
598
|
7. **`singleSelect` editing**: for dropdown editors in cells, set `type: 'singleSelect'` and provide `valueOptions`.
|
|
507
|
-
8. **`initialState.columns.columnWidths`**: accepts `Record<string, number>` to restore saved column widths.
|
|
599
|
+
8. **`initialState.columns.columnWidths`**: accepts `Record<string, number>` to restore saved column widths. Every part of `initialState` is optional.
|
|
508
600
|
9. **`pinnedColumns`** and **`columnWidths`** are independent state — pinning does not reset widths.
|
|
509
|
-
10. **Aggregation**: setting `aggregationModel` alone renders a sticky footer with the totals; with `rowGroupingModel` each group row also shows its own aggregates. `null`/`undefined
|
|
510
|
-
11. **GOTCHAS — bounded height**: virtualization only works if the grid's container has a bounded height. A flex child without `min-height: 0` grows to fit every row, so every row is rendered. Pagination hides this, and row grouping turns pagination off.
|
|
601
|
+
10. **Aggregation**: setting `aggregationModel` alone renders a sticky footer with the totals; with `rowGroupingModel` each group row also shows its own aggregates. `null`/`undefined`, blank strings, booleans and arrays are ignored by sum/avg/min/max; min/max of dates return a `Date`; `valueGetter` columns aggregate the computed value; footer totals go through the column's `valueFormatter` (not for count/unique).
|
|
602
|
+
11. **GOTCHAS — bounded height**: virtualization only works if the grid's container has a bounded height. A flex child without `min-height: 0` grows to fit every row, so every row is rendered. Pagination hides this, and row grouping turns pagination off. Without a `height` prop the grid fills its container (3.0.1+), so use `<div style={{ flex: 1, minHeight: 0 }}>` around the grid, or pass a fixed pixel `height`. The list view never virtualizes: paginate it. Dev builds log a warning when this happens.
|
|
511
603
|
12. **Pagination is ignored under row grouping** — a grouped grid is one scrollable, virtualized list.
|
|
512
604
|
13. **`valueFormatter` + `renderCell`**: `renderCell` receives `params.formattedValue` (v2.1+), so a custom renderer can reuse the formatter instead of duplicating it.
|
|
513
|
-
14. **`slots.footer`** replaces the pagination area and receives `aggregationResult`, `rowCount`, `paginationModel`, `
|
|
605
|
+
14. **`slots.footer`** replaces the pagination area and receives `apiRef`, `aggregationModel`, `aggregationResult`, `rowCount` (filtered data rows excluding pinned rows in flat grids — data rows under tree data / grouping; the server total with `paginationMode="server"`), `pagination`, `paginationModel`, `pageSizeOptions` and `onPaginationModelChange`.
|
|
606
|
+
15. **v3: no underscore row fields** — `params.row._hasChildren` / `_treeDepth` / … are `undefined` since 3.0.0; use `params.rowMeta?.hasChildren` etc. Under grouping and tree data `params.row` is the consumer's own object; never mutate it in render callbacks. Migration guide: `docs/migration/v2-to-v3.md`.
|
|
607
|
+
16. **Row limit**: the scroll height is `rows × rowHeight` px and browsers cap element height (Chromium 33,554,432 px), so one scrolling grid reaches ~645,000 rows at 52 px. Page or stream beyond that.
|
|
608
|
+
17. **Server modes without dataSource**: with `paginationMode` / `sortingMode` / `filterMode` = `'server'` the grid shows `rows` as given (no slicing, sorting or filtering). Fetch from the change callbacks and control the models.
|
|
609
|
+
18. **`valueFormatter` / `valueGetter` params are `unknown`-typed**: cast (`(value as number).toFixed(2)`) before calling methods, or TypeScript strict mode rejects it.
|
|
610
|
+
19. **Inline `columns` are reused while shallow-equal**: a new array with the same column objects does not re-run filtering/sorting, but mutating a column object in place is not detected — pass new objects when a definition changes.
|