@opencorestack/opengridx 2.0.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +408 -3
- package/README.md +93 -72
- package/dist/index.d.ts +611 -121
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +7992 -5650
- 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 -161
- 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 +45 -22
- package/docs/components/datagrid.md +14 -12
- 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 +100 -36
- 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 +147 -41
- 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 +56 -19
- package/docs/features/pinning.md +23 -5
- package/docs/features/selection.md +50 -10
- package/docs/features/sorting-pagination.md +66 -14
- package/docs/features/state-persistence.md +78 -10
- package/docs/features/toolbar-customization.md +26 -17
- package/docs/features/tree-data-grouping.md +44 -9
- package/docs/features/virtualization.md +64 -15
- package/docs/migration/v1-to-v2.md +115 -0
- package/docs/migration/v2-to-v3.md +1222 -0
- package/docs/performance.md +124 -0
- package/docs/roadmap.md +16 -10
- package/lib/components/Cell/Cell.css +13 -1
- package/lib/components/Cell/Cell.tsx +130 -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 +11 -0
- 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 +84 -12
- package/lib/components/DataGrid/DataGrid.tsx +606 -872
- package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
- package/lib/components/DataGrid/GridEmptyState.tsx +7 -1
- package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
- package/lib/components/DataGrid/GridListView.tsx +166 -23
- 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 +65 -11
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
- package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +48 -25
- 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 +3 -3
- package/lib/components/Header/Header.tsx +179 -160
- package/lib/components/ListView/ListView.css +25 -0
- package/lib/components/ListView/ListViewRow.tsx +79 -10
- package/lib/components/Pagination/Pagination.tsx +15 -7
- package/lib/components/Row/Row.tsx +185 -75
- 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 +15 -3
- 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 +284 -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 +123 -0
- 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 +452 -250
- 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 +20 -7
- 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 +86 -90
- 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 +335 -214
- package/lib/hooks/useRowReorder.ts +62 -31
- package/lib/hooks/useTreeData.ts +330 -262
- package/lib/index.ts +30 -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 +311 -108
- package/lib/utils/aggregation/index.ts +157 -0
- 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 +344 -121
- 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 +78 -0
- 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 +272 -166
- package/package.json +18 -6
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
- 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/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 -137
- 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.0
|
|
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,152 +60,164 @@ 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
|
|
71
|
-
| autoHeight | boolean | false | Fit height to rows
|
|
74
|
+
| height | number/string | — | Grid height. No default. The grid 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) |
|
|
77
|
+
| ariaLabel | string | — | Accessible name for the grid |
|
|
78
|
+
| localeText | GridLocaleText | — | Override pager / no-rows strings: paginationRowsPerPage, paginationOf(from,to,count), paginationPage(page,count), noRowsLabel |
|
|
72
79
|
| rowHeight | number | 52 | Row height in pixels |
|
|
73
80
|
| headerHeight | number | 56 | Header height in pixels |
|
|
74
|
-
| loading | boolean | false |
|
|
75
|
-
| noRowsLabel | string |
|
|
76
|
-
| 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 |
|
|
77
84
|
|
|
78
85
|
### Selection
|
|
79
86
|
| Prop | Type | Description |
|
|
80
87
|
|----------------------------|-------------------------------|-------------------------------------|
|
|
81
|
-
| checkboxSelection | boolean | Enable checkbox column
|
|
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 |
|
|
90
|
+
| pinCheckboxColumn | boolean (default true) | Pin the checkbox column left |
|
|
82
91
|
| rowSelectionModel | GridRowId[] | Controlled selection |
|
|
83
|
-
| 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) |
|
|
84
93
|
| disableRowSelectionOnClick | boolean | Prevent click toggling selection |
|
|
94
|
+
| disableClipboardCopy | boolean | Turn off the grid's Ctrl/Cmd+C copy (copySelectedRows still works) |
|
|
85
95
|
|
|
86
96
|
### Sorting
|
|
87
97
|
| Prop | Type | Description |
|
|
88
98
|
|-------------------|-----------------------------|----------------------------------|
|
|
89
99
|
| sortModel | GridSortItem[] | Controlled sort state |
|
|
100
|
+
| multiSort | boolean | Plain click appends to sortModel (Shift+click always does) |
|
|
90
101
|
| onSortModelChange | (model: GridSortItem[]) => void | Sort change callback |
|
|
91
|
-
| sortingMode | 'client' / 'server' |
|
|
92
|
-
| disableColumnSorting | boolean | Disable sorting for all columns |
|
|
102
|
+
| sortingMode | 'client' / 'server' | 'server': rows are shown in the order given |
|
|
93
103
|
|
|
94
104
|
### Filtering
|
|
95
105
|
| Prop | Type | Description |
|
|
96
106
|
|---------------------|--------------------------------|-----------------------------------|
|
|
97
|
-
| filterModel | GridFilterModel | Controlled filter state
|
|
107
|
+
| filterModel | GridFilterModel | Controlled filter state (optional: the grid keeps its own filter model otherwise) |
|
|
98
108
|
| onFilterModelChange | (model: GridFilterModel) => void | Filter change callback |
|
|
99
|
-
| filterMode | 'client' / 'server' |
|
|
100
|
-
| disableColumnFilter | boolean | Disable filter for all columns |
|
|
109
|
+
| filterMode | 'client' / 'server' | 'server': rows are shown unfiltered as given |
|
|
101
110
|
|
|
102
111
|
### Pagination
|
|
103
112
|
| Prop | Type | Description |
|
|
104
113
|
|-------------------------|-----------------------------------------|----------------------------------|
|
|
105
|
-
| pagination | boolean |
|
|
106
|
-
| paginationModel | GridPaginationModel | {page, pageSize} controlled
|
|
107
|
-
| onPaginationModelChange | (model: GridPaginationModel) => void | Pagination change callback
|
|
108
|
-
| pageSizeOptions | number[]
|
|
109
|
-
| paginationMode | 'client' / 'server' / 'infinite' |
|
|
110
|
-
| 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) |
|
|
111
120
|
|
|
112
121
|
### Column Features
|
|
113
122
|
| Prop | Type | Description |
|
|
114
123
|
|---------------------------|---------------------------|-------------------------------------------|
|
|
115
|
-
| pinnedColumns | GridColumnPinning | {left: string[], right: string[]}
|
|
124
|
+
| pinnedColumns | GridColumnPinning | {left: string[], right: string[]}; rendered in that order |
|
|
116
125
|
| onPinnedColumnsChange | (model) => void | Pin change callback |
|
|
117
126
|
| columnVisibilityModel | Record<string, boolean> | {field: false} to hide |
|
|
118
127
|
| onColumnVisibilityModelChange | (model) => void | Visibility change callback |
|
|
119
128
|
| columnOrder | string[] | Controlled column order |
|
|
120
|
-
| 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) |
|
|
121
131
|
| disableColumnReorder | boolean | Disable drag-to-reorder |
|
|
122
|
-
|
|
|
123
|
-
| columnGroupingModel | GridColumnGroupingModel | Multi-level column header groups |
|
|
132
|
+
| columnGroupingModel | GridColumnGroupingModel | Multi-level column header groups; drag-reorder stays within a group |
|
|
124
133
|
|
|
125
134
|
### Row Features
|
|
126
135
|
| Prop | Type | Description |
|
|
127
136
|
|-------------------|---------------------------|-------------------------------------------|
|
|
128
|
-
| pinnedRows | GridRowPinning | {top: id[], bottom: id[]}
|
|
129
|
-
| rowReordering | boolean | Enable drag-and-drop row reordering
|
|
130
|
-
| onRowOrderChange | (
|
|
131
|
-
| getRowId | (row: R) => GridRowId | Custom row ID accessor (default: row.id)
|
|
132
|
-
| getRowClassName | (params) => string | Dynamic CSS class per row |
|
|
133
|
-
| getRowHeight | (params) => number | Dynamic row height |
|
|
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` |
|
|
134
141
|
|
|
135
142
|
### Cell Editing
|
|
136
143
|
| Prop | Type | Description |
|
|
137
144
|
|-------------------------|----------------------------|-----------------------------------------|
|
|
138
|
-
| processRowUpdate | (newRow, oldRow) => R
|
|
139
|
-
| onProcessRowUpdateError | (error) => void |
|
|
140
|
-
| 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 |
|
|
141
148
|
|
|
142
149
|
### Detail Panels (Master-Detail)
|
|
143
150
|
| Prop | Type | Description |
|
|
144
151
|
|-------------------------|----------------------------|-----------------------------------------|
|
|
145
|
-
| getDetailPanelContent | (params) => ReactNode
|
|
146
|
-
|
|
|
147
|
-
|
|
|
148
|
-
|
|
|
152
|
+
| getDetailPanelContent | (params: {row, id, rowIndex}) => ReactNode | Called for expanded data rows only (never group rows) |
|
|
153
|
+
| pinExpandColumn | boolean (default true) | Pin the detail-panel expand column |
|
|
154
|
+
| getDetailPanelHeight | (params) => number / 'auto'| px, or 'auto' = measured content height. Default 200 |
|
|
155
|
+
| detailPanelExpandedRowIds | Set<GridRowId> | Controlled expanded rows |
|
|
156
|
+
| onDetailPanelExpandedRowIdsChange | (ids: Set<GridRowId>) => void | Expand/collapse callback |
|
|
149
157
|
|
|
150
158
|
### Grouping & Aggregation
|
|
151
159
|
| Prop | Type | Description |
|
|
152
160
|
|------------------------|---------------------------|------------------------------------------|
|
|
153
|
-
| rowGroupingModel | string[] | Fields to group by
|
|
154
|
-
| aggregationModel | GridAggregationModel | {field: 'sum'/'avg'/'min'/'max'/'count'} |
|
|
155
|
-
|
|
|
156
|
-
|
|
|
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 |
|
|
157
167
|
|
|
158
168
|
### Tree Data
|
|
159
169
|
| Prop | Type | Description |
|
|
160
170
|
|---------------------------|----------------------------|--------------------------------------|
|
|
161
171
|
| treeData | boolean | Enable tree data mode |
|
|
162
|
-
| getTreeDataPath | (row) => string[] |
|
|
163
|
-
| 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`.
|
|
164
176
|
|
|
165
177
|
### Pivot
|
|
166
178
|
| Prop | Type | Description |
|
|
167
179
|
|----------------|----------------|-----------------------------------------------|
|
|
168
|
-
| pivotMode | boolean | Enable pivot view
|
|
169
|
-
| pivotModel | GridPivotModel | {rowFields, columnFields, valueFields}
|
|
170
|
-
| 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.
|
|
171
185
|
|
|
172
186
|
### Server-Side
|
|
173
187
|
| Prop | Type | Description |
|
|
174
188
|
|------------|-----------------|----------------------------------------------------|
|
|
175
|
-
| dataSource | GridDataSource | Adapter for server-side fetching (see
|
|
189
|
+
| dataSource | GridDataSource | Adapter for server-side fetching (see SERVER-SIDE DATA SOURCE) |
|
|
176
190
|
|
|
177
191
|
### Styling & Customization
|
|
178
192
|
| Prop | Type | Description |
|
|
179
193
|
|--------------|-----------------------|------------------------------------------|
|
|
180
|
-
| slots |
|
|
181
|
-
| slotProps |
|
|
182
|
-
|
|
|
183
|
-
|
|
|
194
|
+
| slots | { toolbar, pagination, noRowsOverlay, loadingOverlay, footer } | Replace built-in parts |
|
|
195
|
+
| slotProps | { toolbar, pagination, noRowsOverlay, loadingOverlay, footer } | Extra props for those slots |
|
|
196
|
+
| className | string | Extra CSS class on the root element |
|
|
197
|
+
| style | React.CSSProperties | Inline styles on the root element (use DataGridThemeProvider for theming) |
|
|
198
|
+
|
|
199
|
+
There are no getRowClassName / getCellClassName / theme props: use GridColDef.cellClassName (string or function) and DataGridThemeProvider.
|
|
184
200
|
|
|
185
201
|
### Callbacks
|
|
186
202
|
| Prop | Type | Description |
|
|
187
203
|
|-------------------|-----------------------|-------------------------------|
|
|
188
|
-
| onRowClick | (params) => void
|
|
189
|
-
| onCellClick | (params) => void
|
|
190
|
-
| onRowDoubleClick | (params) => void | Row double-click
|
|
191
|
-
| onRowsScrollEnd | (params) => void |
|
|
192
|
-
| 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 |
|
|
193
209
|
|
|
194
210
|
### State Persistence
|
|
195
211
|
| Prop | Type | Description |
|
|
196
212
|
|--------------|------------------|----------------------------------------------|
|
|
197
|
-
| initialState | GridInitialState |
|
|
198
|
-
| apiRef |
|
|
213
|
+
| initialState | GridInitialState | Starting sorting, filter, pagination, columns (widths, order, pinning, visibility) and density. Read at mount only |
|
|
214
|
+
| apiRef | MutableRefObject<GridApi> | Imperative API handle from useGridApiRef() |
|
|
199
215
|
|
|
200
216
|
### List View
|
|
201
217
|
| Prop | Type | Description |
|
|
202
218
|
|----------------|--------------------|---------------------------------------------|
|
|
203
|
-
| listView | boolean | Render as
|
|
204
|
-
| 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 |
|
|
220
|
+
| listViewColumn | { field, renderCell } | Defines the card renderer |
|
|
205
221
|
|
|
206
222
|
---
|
|
207
223
|
|
|
@@ -211,31 +227,37 @@ export default function App() {
|
|
|
211
227
|
|-------------------|--------------------------------------------------------|--------------------------------------------|
|
|
212
228
|
| field | string | **Required.** Key in row data |
|
|
213
229
|
| headerName | string | Column label |
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
|
|
|
221
|
-
|
|
|
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) |
|
|
222
239
|
| renderHeader | (params: GridRenderHeaderParams) => ReactNode | Custom header renderer |
|
|
223
|
-
| renderEditCell | (params:
|
|
224
|
-
| valueGetter | ({row, field, value}) =>
|
|
225
|
-
|
|
|
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) |
|
|
226
244
|
| cellClassName | string / (params) => string | CSS class on cells |
|
|
227
245
|
| headerClassName | string | CSS class on header cell |
|
|
228
|
-
| colSpan | number / (params) => number | Cell column span
|
|
229
|
-
| rowSpan | number / (params) => number | Cell row span
|
|
230
|
-
| align | 'left'/'center'/'right'
|
|
231
|
-
| 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 |
|
|
232
250
|
| sortable | boolean (default true) | Enable sorting |
|
|
233
|
-
| filterable | boolean (default true) | Enable filtering
|
|
234
|
-
| resizable | boolean (default true) | Enable resize handle
|
|
235
|
-
| hideable | boolean (default true) | Allow hiding via toolbar
|
|
236
|
-
| pinnable | boolean (default true) | Allow pinning via column menu
|
|
237
|
-
| 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 |
|
|
238
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 |
|
|
239
261
|
|
|
240
262
|
---
|
|
241
263
|
|
|
@@ -244,37 +266,45 @@ export default function App() {
|
|
|
244
266
|
```tsx
|
|
245
267
|
import { useGridApiRef } from '@opencorestack/opengridx';
|
|
246
268
|
const apiRef = useGridApiRef();
|
|
247
|
-
<DataGrid apiRef={apiRef}
|
|
269
|
+
<DataGrid apiRef={apiRef} rows={rows} columns={columns} />
|
|
248
270
|
|
|
249
271
|
// Row access
|
|
250
|
-
apiRef.current.getRow(id)
|
|
251
|
-
apiRef.current.getAllRows()
|
|
252
|
-
apiRef.current.getVisibleRows()
|
|
272
|
+
apiRef.current.getRow(id) // → 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)
|
|
253
277
|
|
|
254
278
|
// Column access
|
|
255
|
-
apiRef.current.getColumn(field)
|
|
256
|
-
apiRef.current.getAllColumns()
|
|
257
|
-
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)
|
|
258
282
|
|
|
259
283
|
// Selection
|
|
260
|
-
apiRef.current.selectRow(id, isSelected?)
|
|
261
|
-
apiRef.current.selectRows(ids, isSelected?) // bulk select
|
|
262
|
-
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[]
|
|
263
287
|
|
|
264
|
-
// Sort / Filter
|
|
288
|
+
// Sort / Filter (setters update the grid and fire the change callbacks)
|
|
265
289
|
apiRef.current.sortColumn(field, 'asc' | 'desc' | null)
|
|
266
|
-
apiRef.current.getSortModel()
|
|
290
|
+
apiRef.current.getSortModel() // → GridSortItem[]
|
|
267
291
|
apiRef.current.setFilterModel(model)
|
|
292
|
+
apiRef.current.getFilterModel() // → GridFilterModel
|
|
268
293
|
|
|
269
294
|
// Pagination
|
|
270
295
|
apiRef.current.setPage(page)
|
|
271
296
|
apiRef.current.setPageSize(size)
|
|
272
297
|
|
|
298
|
+
// Scroll
|
|
299
|
+
apiRef.current.scrollToIndexes({ rowIndex, colIndex })
|
|
300
|
+
|
|
273
301
|
// Clipboard
|
|
274
|
-
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
|
|
275
304
|
|
|
276
|
-
// Aggregation
|
|
277
|
-
apiRef.current.getAggregationResult()
|
|
305
|
+
// Aggregation
|
|
306
|
+
apiRef.current.getAggregationResult() // → Record<string, unknown> | null (null in pivot mode)
|
|
307
|
+
apiRef.current.getAggregationModel() // → GridAggregationModel | null
|
|
278
308
|
```
|
|
279
309
|
|
|
280
310
|
---
|
|
@@ -282,10 +312,13 @@ apiRef.current.getAggregationResult() // → GridAggregationResult | null
|
|
|
282
312
|
## CELL EDITING — FULL EXAMPLE
|
|
283
313
|
|
|
284
314
|
```tsx
|
|
285
|
-
|
|
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);
|
|
286
319
|
|
|
287
|
-
const processRowUpdate = (newRow
|
|
288
|
-
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)));
|
|
289
322
|
return newRow; // Must return the committed row
|
|
290
323
|
};
|
|
291
324
|
|
|
@@ -302,41 +335,57 @@ const processRowUpdate = (newRow, oldRow) => {
|
|
|
302
335
|
/>
|
|
303
336
|
```
|
|
304
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
|
+
|
|
305
340
|
---
|
|
306
341
|
|
|
307
342
|
## SERVER-SIDE DATA SOURCE
|
|
308
343
|
|
|
344
|
+
Pass a `dataSource` object to DataGrid; there is no separate data-source hook to import.
|
|
345
|
+
|
|
309
346
|
```tsx
|
|
310
|
-
import {
|
|
347
|
+
import { DataGrid, type GridDataSource } from '@opencorestack/opengridx';
|
|
311
348
|
|
|
312
349
|
const dataSource: GridDataSource = {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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());
|
|
316
362
|
},
|
|
317
|
-
// Optional: lazy-load children for server-side tree data
|
|
318
|
-
getChildren: async ({ row }) => {
|
|
319
|
-
return fetch(`/api/rows/${row.id}/children`).then(r => r.json());
|
|
320
|
-
}
|
|
321
363
|
};
|
|
322
364
|
|
|
323
365
|
<DataGrid
|
|
324
366
|
columns={columns}
|
|
325
|
-
rows={[]} //
|
|
367
|
+
rows={[]} // data comes from dataSource
|
|
326
368
|
dataSource={dataSource}
|
|
369
|
+
pagination
|
|
327
370
|
paginationMode="server"
|
|
328
371
|
sortingMode="server"
|
|
329
372
|
filterMode="server"
|
|
330
|
-
rowCount={1000}
|
|
331
373
|
/>
|
|
332
374
|
```
|
|
333
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
|
+
|
|
334
383
|
---
|
|
335
384
|
|
|
336
385
|
## THEMING
|
|
337
386
|
|
|
338
387
|
```tsx
|
|
339
|
-
import { DataGridThemeProvider, darkTheme } from '@opencorestack/opengridx';
|
|
388
|
+
import { DataGridThemeProvider, darkTheme, type GridTheme } from '@opencorestack/opengridx';
|
|
340
389
|
|
|
341
390
|
// Built-in themes: darkTheme, roseTheme, emeraldTheme, amberTheme, compactTheme
|
|
342
391
|
<DataGridThemeProvider theme={darkTheme}>
|
|
@@ -344,25 +393,27 @@ import { DataGridThemeProvider, darkTheme } from '@opencorestack/opengridx';
|
|
|
344
393
|
</DataGridThemeProvider>
|
|
345
394
|
|
|
346
395
|
// Custom theme override:
|
|
347
|
-
const myTheme = {
|
|
396
|
+
const myTheme: GridTheme = {
|
|
397
|
+
mode: 'light', // base palette the provider pins ('light' default, or 'dark')
|
|
348
398
|
colors: {
|
|
349
399
|
primary: '#6366f1',
|
|
350
400
|
},
|
|
351
401
|
grid: {
|
|
352
|
-
headerBackground: '#
|
|
353
|
-
rowHoverBackground: '#
|
|
402
|
+
headerBackground: '#eef2ff',
|
|
403
|
+
rowHoverBackground: '#f5f3ff',
|
|
404
|
+
rowHeightStandard: '40px', // theme heights size rows/header; rowHeight/headerHeight props win
|
|
354
405
|
},
|
|
355
406
|
overlays: {
|
|
356
|
-
background: '#
|
|
357
|
-
border: '#
|
|
358
|
-
}
|
|
407
|
+
background: '#ffffff',
|
|
408
|
+
border: '#e0e7ff',
|
|
409
|
+
},
|
|
359
410
|
};
|
|
360
411
|
<DataGridThemeProvider theme={myTheme}>
|
|
361
|
-
<DataGrid
|
|
412
|
+
<DataGrid rows={rows} columns={columns} />
|
|
362
413
|
</DataGridThemeProvider>
|
|
363
414
|
|
|
364
|
-
//
|
|
365
|
-
// `.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.
|
|
366
417
|
```
|
|
367
418
|
|
|
368
419
|
---
|
|
@@ -372,21 +423,24 @@ const myTheme = {
|
|
|
372
423
|
```tsx
|
|
373
424
|
import { GridToolbar } from '@opencorestack/opengridx';
|
|
374
425
|
|
|
375
|
-
// Replace built-in components:
|
|
376
426
|
<DataGrid
|
|
427
|
+
rows={rows}
|
|
428
|
+
columns={columns}
|
|
377
429
|
slots={{
|
|
378
|
-
toolbar:
|
|
379
|
-
pagination: MyPagination,
|
|
430
|
+
toolbar: GridToolbar, // or your own component
|
|
431
|
+
pagination: MyPagination, // props: page, pageSize, rowCount, pageSizeOptions, onPageChange, onPageSizeChange, localeText
|
|
380
432
|
noRowsOverlay: EmptyState,
|
|
381
433
|
loadingOverlay: Spinner,
|
|
382
|
-
footer: CustomFooter,
|
|
434
|
+
footer: CustomFooter, // replaces the pager; see note 14
|
|
383
435
|
}}
|
|
384
436
|
slotProps={{
|
|
385
|
-
toolbar: {
|
|
437
|
+
toolbar: { showNonHideableColumns: true },
|
|
386
438
|
}}
|
|
387
439
|
/>
|
|
388
440
|
```
|
|
389
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
|
+
|
|
390
444
|
---
|
|
391
445
|
|
|
392
446
|
## STATE PERSISTENCE
|
|
@@ -395,11 +449,11 @@ import { GridToolbar } from '@opencorestack/opengridx';
|
|
|
395
449
|
import { useGridStateStorage } from '@opencorestack/opengridx';
|
|
396
450
|
|
|
397
451
|
function MyGrid() {
|
|
398
|
-
const { initialState,
|
|
452
|
+
const { initialState, onStateChange, clearState } = useGridStateStorage('my-grid-v1');
|
|
399
453
|
return (
|
|
400
454
|
<DataGrid
|
|
401
455
|
initialState={initialState}
|
|
402
|
-
onStateChange={
|
|
456
|
+
onStateChange={onStateChange}
|
|
403
457
|
rows={rows}
|
|
404
458
|
columns={columns}
|
|
405
459
|
/>
|
|
@@ -407,6 +461,8 @@ function MyGrid() {
|
|
|
407
461
|
}
|
|
408
462
|
```
|
|
409
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
|
+
|
|
410
466
|
---
|
|
411
467
|
|
|
412
468
|
## EXPORT UTILITIES
|
|
@@ -414,91 +470,141 @@ function MyGrid() {
|
|
|
414
470
|
```tsx
|
|
415
471
|
import {
|
|
416
472
|
exportToCsv,
|
|
417
|
-
exportToExcel, // HTML
|
|
418
|
-
exportToExcelAdvanced, //
|
|
473
|
+
exportToExcel, // HTML table saved as .xls — zero deps
|
|
474
|
+
exportToExcelAdvanced, // real .xlsx — requires: npm install exceljs
|
|
419
475
|
exportToJson,
|
|
476
|
+
exportToPdf, // requires: npm install jspdf jspdf-autotable
|
|
420
477
|
printGrid,
|
|
421
478
|
} from '@opencorestack/opengridx';
|
|
422
479
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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, {
|
|
426
486
|
fileName: 'export.xlsx',
|
|
427
|
-
|
|
487
|
+
sheets: [{ name: 'Data' }, { name: 'Selected', rows: 'selected' }],
|
|
488
|
+
selectedRows: apiRef.current.getSelectedRows(),
|
|
428
489
|
columnStyles: {
|
|
429
490
|
avatar: { embedImage: true, imageWidth: 32, imageHeight: 32 }
|
|
430
491
|
}
|
|
431
492
|
});
|
|
432
493
|
exportToJson(rows, columns, { fileName: 'export.json' });
|
|
433
|
-
|
|
494
|
+
await exportToPdf(rows, columns, { fileName: 'export', title: 'My Report' });
|
|
495
|
+
await printGrid(rows, columns, 'My Report Title');
|
|
434
496
|
```
|
|
435
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
|
+
|
|
436
503
|
---
|
|
437
504
|
|
|
438
505
|
## CSS CUSTOMIZATION
|
|
439
506
|
|
|
440
|
-
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):
|
|
441
508
|
|
|
442
509
|
```css
|
|
443
510
|
.my-grid-wrapper {
|
|
444
511
|
--ogx-color-primary: #6366f1;
|
|
445
|
-
--ogx-
|
|
446
|
-
--ogx-
|
|
447
|
-
--ogx-
|
|
448
|
-
--ogx-font-size
|
|
449
|
-
--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;
|
|
450
517
|
}
|
|
451
518
|
```
|
|
452
519
|
|
|
453
|
-
|
|
454
|
-
|
|
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`
|
|
455
524
|
- `ogx__cell`, `ogx__cell--focused`, `ogx__cell--editing`
|
|
456
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)
|
|
457
527
|
|
|
458
528
|
---
|
|
459
529
|
|
|
460
530
|
## TYPE REFERENCE
|
|
461
531
|
|
|
462
532
|
```ts
|
|
463
|
-
type GridRowModel = Record<string, any> & { id: GridRowId };
|
|
464
533
|
type GridRowId = string | number;
|
|
534
|
+
interface GridRowModel { id: GridRowId; [key: string]: unknown; }
|
|
465
535
|
type GridSortDirection = 'asc' | 'desc' | null;
|
|
466
|
-
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
|
|
467
543
|
|
|
468
544
|
interface GridSortItem { field: string; sort: 'asc' | 'desc'; }
|
|
469
|
-
interface GridFilterItem { field: string; operator:
|
|
470
|
-
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
|
+
}
|
|
471
552
|
interface GridPaginationModel { page: number; pageSize: number; }
|
|
472
553
|
interface GridColumnPinning { left?: string[]; right?: string[]; }
|
|
473
554
|
interface GridRowPinning { top?: GridRowId[]; bottom?: GridRowId[]; }
|
|
474
555
|
|
|
475
556
|
interface GridRenderCellParams<R = GridRowModel> {
|
|
476
|
-
value:
|
|
557
|
+
value: unknown; formattedValue?: string; row: R; field: string;
|
|
477
558
|
colDef: GridColDef<R>; rowIndex: number; colIndex: number;
|
|
559
|
+
rowMeta?: GridRowMeta; // hierarchy info for tree-data / grouping rows
|
|
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;
|
|
478
565
|
}
|
|
479
566
|
|
|
480
|
-
interface GridDataSource {
|
|
481
|
-
getRows: (params: GridGetRowsParams) => Promise<GridGetRowsResponse
|
|
482
|
-
|
|
567
|
+
interface GridDataSource<R = GridRowModel> {
|
|
568
|
+
getRows: (params: GridGetRowsParams) => Promise<GridGetRowsResponse<R>>;
|
|
569
|
+
getAggregations?: (params: Omit<GridGetRowsParams, 'startRow' | 'endRow'>) => Promise<Record<string, unknown>>;
|
|
483
570
|
}
|
|
484
571
|
interface GridGetRowsParams {
|
|
485
|
-
|
|
572
|
+
startRow: number; endRow: number; // endRow exclusive; Number.MAX_SAFE_INTEGER = all rows
|
|
486
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>;
|
|
487
579
|
}
|
|
488
|
-
interface GridGetRowsResponse { rows: GridRowModel[]; rowCount: number; }
|
|
489
580
|
```
|
|
490
581
|
|
|
491
582
|
---
|
|
492
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
|
+
|
|
493
590
|
## KEY NOTES FOR AI AGENTS
|
|
494
591
|
|
|
495
|
-
1. **Always `return newRow`** from `processRowUpdate` —
|
|
496
|
-
2. **`getRowId` default is `row.id`** — if your data uses a different key, always pass `getRowId`.
|
|
497
|
-
3. **CSS must be imported**:
|
|
498
|
-
4. **
|
|
499
|
-
5. **`dataSource` + `rows={[]}`**:
|
|
500
|
-
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`.
|
|
501
598
|
7. **`singleSelect` editing**: for dropdown editors in cells, set `type: 'singleSelect'` and provide `valueOptions`.
|
|
502
|
-
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.
|
|
503
600
|
9. **`pinnedColumns`** and **`columnWidths`** are independent state — pinning does not reset widths.
|
|
504
|
-
10. **Aggregation
|
|
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. Use `<div style={{ flex: 1, minHeight: 0 }}>` around `height="100%"`, or pass a fixed pixel `height`. Dev builds log a warning when this happens.
|
|
603
|
+
12. **Pagination is ignored under row grouping** — a grouped grid is one scrollable, virtualized list.
|
|
604
|
+
13. **`valueFormatter` + `renderCell`**: `renderCell` receives `params.formattedValue` (v2.1+), so a custom renderer can reuse the formatter instead of duplicating it.
|
|
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.
|