@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
OpenGridX provides a robust filtering system with support for both client-side and server-side operations.
|
|
4
4
|
|
|
5
5
|
## đ Overview
|
|
6
|
-
- **Quick Filter**: Search across all columns
|
|
6
|
+
- **Quick Filter**: Search across all visible, filterable columns at once.
|
|
7
7
|
- **Column Filters**: Specific operators for different data types (string, number, date, etc.).
|
|
8
8
|
- **Multi-Filter Groups**: Support for `AND`/`OR` logic operators.
|
|
9
9
|
- **Server-Side Filtering**: Offload complex queries to your backend.
|
|
@@ -25,11 +25,24 @@ Simply define your columns with `filterable: true` (which is the default).
|
|
|
25
25
|
/>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
The filter model can be controlled (`filterModel` + `onFilterModelChange`) or left to the grid. Uncontrolled, the grid keeps its own filter model: the toolbar search and filter panel (`slots={{ toolbar: GridToolbar }}`) and `apiRef.current.setFilterModel()` change it, `onFilterModelChange` reports each change, and `initialState.filter.filterModel` sets the starting filter (which is how [state persistence](./state-persistence.md) restores it).
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<DataGrid
|
|
32
|
+
rows={rows}
|
|
33
|
+
columns={columns}
|
|
34
|
+
slots={{ toolbar: GridToolbar }} // search + filters work without any filter props
|
|
35
|
+
initialState={{ filter: { filterModel: { items: [{ field: 'age', operator: '>', value: 30 }] } } }}
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
28
39
|
### Server-Side Filtering
|
|
29
|
-
Set `filterMode="server"` and handle the request in your `dataSource`.
|
|
40
|
+
Set `filterMode="server"` and handle the request in your `dataSource`. The grid does not re-filter the rows it receives. Without a `dataSource`, fetch the filtered rows yourself from `onFilterModelChange` and pass them as `rows`.
|
|
30
41
|
|
|
31
42
|
```tsx
|
|
32
43
|
<DataGrid
|
|
44
|
+
rows={[]}
|
|
45
|
+
columns={columns}
|
|
33
46
|
filterMode="server"
|
|
34
47
|
onFilterModelChange={(model) => console.log('Current Filters:', model)}
|
|
35
48
|
dataSource={{
|
|
@@ -62,12 +75,35 @@ interface GridFilterItem {
|
|
|
62
75
|
```
|
|
63
76
|
|
|
64
77
|
### Supported Operators
|
|
78
|
+
|
|
79
|
+
The filter panel offers these operators per column `type` (the first one is the default):
|
|
80
|
+
|
|
65
81
|
| Type | Operators |
|
|
66
82
|
| :--- | :--- |
|
|
67
|
-
| **String** | `contains`, `equals`, `startsWith`, `endsWith`, `isEmpty`, `isNotEmpty
|
|
68
|
-
| **Number** |
|
|
69
|
-
| **Date** | `is`, `not`, `isEmpty`, `isNotEmpty` |
|
|
83
|
+
| **String** (default) | `contains`, `equals`, `startsWith`, `endsWith`, `isEmpty`, `isNotEmpty` |
|
|
84
|
+
| **Number** | `=`, `!=`, `>`, `>=`, `<`, `<=`, `isEmpty`, `isNotEmpty` |
|
|
85
|
+
| **Date** | `is`, `not`, `after`, `onOrAfter`, `before`, `onOrBefore`, `isEmpty`, `isNotEmpty` |
|
|
70
86
|
| **Boolean** | `is` |
|
|
87
|
+
| **singleSelect** | `isAnyOf`, `is`, `not` |
|
|
88
|
+
|
|
89
|
+
Every operator in `GridFilterOperator` also works in a programmatic `filterModel` on any column; the panel shows such an operator as it is.
|
|
90
|
+
|
|
91
|
+
### How values are matched
|
|
92
|
+
|
|
93
|
+
- **Empty values do not filter.** An item whose `value` is `undefined`, `null`, a blank string or an empty array is ignored, so picking an operator before typing (or clearing the input) leaves the grid unfiltered. `isEmpty` / `isNotEmpty` need no value.
|
|
94
|
+
- **Computed columns.** Cells are read through the column's `valueGetter`, so a computed column filters by the value it displays.
|
|
95
|
+
- **Text operators** (`contains`, `equals`, `startsWith`, `endsWith`, `is`, `not`) compare case-insensitively.
|
|
96
|
+
- **Number operators** compare numerically and accept numeric strings. A blank cell is empty, not `0`: it never matches `=`, `>`, `<` âĻ and always matches `!=`.
|
|
97
|
+
- **Date operators** compare local calendar days. Cells and values can be `Date` objects, epoch milliseconds or date strings; a `'YYYY-MM-DD'` string is read as a local date (not UTC midnight). `not` matches empty cells; the other date operators never do.
|
|
98
|
+
- **`isAnyOf`** takes an array of allowed values. A single value is treated as a one-element list; an empty array does not filter.
|
|
99
|
+
|
|
100
|
+
### Quick Filter
|
|
101
|
+
|
|
102
|
+
`quickFilterValues` is a list of terms. A row matches when **every** term is found (case-insensitive substring) in at least one searched column.
|
|
103
|
+
|
|
104
|
+
- Only **visible** columns with `filterable !== false` are searched. The row `id` and fields that are not columns are not searched.
|
|
105
|
+
- Each column is searched by its value after `valueGetter` and, when the column has a `valueFormatter`, by the formatted text as well. `Date` values are searched as `YYYY-MM-DD`; objects are skipped.
|
|
106
|
+
- The toolbar search box splits what the user types on whitespace, so `john london` becomes `['john', 'london']` and matches a row with first name John and city London.
|
|
71
107
|
|
|
72
108
|
---
|
|
73
109
|
|
|
@@ -76,10 +112,12 @@ The filter panel is accessible through the built-in `GridToolbar`. To use your o
|
|
|
76
112
|
|
|
77
113
|
```tsx
|
|
78
114
|
<DataGrid
|
|
115
|
+
rows={rows}
|
|
116
|
+
columns={columns}
|
|
79
117
|
slots={{
|
|
80
118
|
toolbar: MyCustomToolbar
|
|
81
119
|
}}
|
|
82
120
|
/>
|
|
83
121
|
```
|
|
84
122
|
|
|
85
|
-
The `FilterPanel` component is also exported from `@opencorestack/opengridx` if you need to embed it inside a custom layout.
|
|
123
|
+
The `FilterPanel` component is also exported from `@opencorestack/opengridx` if you need to embed it inside a custom layout. See [`<FilterPanel />`](../components/filter-panel.md) for how it edits the model.
|
|
@@ -5,83 +5,91 @@ The OpenGridX DataGrid supports infinite scrolling (lazy loading), allowing you
|
|
|
5
5
|
## Concept
|
|
6
6
|
|
|
7
7
|
When infinite scroll is enabled, the grid:
|
|
8
|
-
1.
|
|
9
|
-
2. Uses `paginationModel`
|
|
8
|
+
1. Shows no pager and never slices the loaded rows (`pagination` is ignored).
|
|
9
|
+
2. Uses `paginationModel.page` and `pageSize` to decide how many rows should be loaded: `(page + 1) * pageSize`.
|
|
10
10
|
3. Triggers `onRowsScrollEnd` when the user scrolls near the bottom of the grid, signaling that more data should be fetched.
|
|
11
|
-
4.
|
|
11
|
+
4. **Appends** each response to the rows already loaded. Your `getRows` only returns the requested range.
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
To enable infinite scrolling:
|
|
16
16
|
|
|
17
|
-
1.
|
|
17
|
+
1. Pass a `dataSource` whose `getRows` returns the rows in `[startRow, endRow)`.
|
|
18
18
|
2. Set `paginationMode="infinite"`.
|
|
19
|
-
3.
|
|
20
|
-
4.
|
|
19
|
+
3. Control `paginationModel` and advance `page` in `onRowsScrollEnd`. The grid does not advance the page on its own.
|
|
20
|
+
4. Set `sortingMode="server"` / `filterMode="server"` if `getRows` applies `sortModel` / `filterModel`.
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
|
-
import {
|
|
23
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
24
|
+
import {
|
|
25
|
+
DataGrid,
|
|
26
|
+
type GridColDef,
|
|
27
|
+
type GridDataSource,
|
|
28
|
+
type GridPaginationModel,
|
|
29
|
+
} from '@opencorestack/opengridx';
|
|
30
|
+
import '@opencorestack/opengridx/styles';
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
type Person = {
|
|
33
|
+
id: number;
|
|
34
|
+
name: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const columns: GridColDef<Person>[] = [
|
|
38
|
+
{ field: 'id', headerName: 'ID', width: 90 },
|
|
39
|
+
{ field: 'name', headerName: 'Name', width: 200, sortable: true },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// With a dataSource the grid owns the loaded rows; `rows` only seeds the store.
|
|
43
|
+
const NO_ROWS: Person[] = [];
|
|
44
|
+
|
|
45
|
+
declare function fetchPeople(params: {
|
|
46
|
+
startRow: number;
|
|
47
|
+
endRow: number;
|
|
48
|
+
sort?: { field: string; sort: 'asc' | 'desc' };
|
|
49
|
+
}): Promise<{ rows: Person[]; total: number }>;
|
|
26
50
|
|
|
27
51
|
export default function InfiniteScrollDemo() {
|
|
28
|
-
const [paginationModel, setPaginationModel] = useState({
|
|
52
|
+
const [paginationModel, setPaginationModel] = useState<GridPaginationModel>({
|
|
29
53
|
page: 0,
|
|
30
|
-
pageSize: 50
|
|
54
|
+
pageSize: 50,
|
|
31
55
|
});
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// Fetch NEXT batch of rows from server
|
|
40
|
-
const response = await mockApi.getRows(params);
|
|
41
|
-
|
|
42
|
-
// CRITICAL: Append new rows to existing rows
|
|
43
|
-
// (Or ensure response.rows contains ONLY new rows and parent handles appending)
|
|
44
|
-
return response;
|
|
45
|
-
} finally {
|
|
46
|
-
setLoading(false);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
56
|
+
|
|
57
|
+
const dataSource = useMemo<GridDataSource<Person>>(() => ({
|
|
58
|
+
getRows: async ({ startRow, endRow, sortModel }) => {
|
|
59
|
+
// Return ONLY the requested range; the grid appends it to what is loaded.
|
|
60
|
+
const { rows, total } = await fetchPeople({ startRow, endRow, sort: sortModel[0] });
|
|
61
|
+
return { rows, rowCount: total };
|
|
62
|
+
},
|
|
49
63
|
}), []);
|
|
50
64
|
|
|
51
65
|
const handleScrollEnd = useCallback(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setPaginationModel(prev => ({
|
|
55
|
-
...prev,
|
|
56
|
-
page: prev.page + 1
|
|
57
|
-
}));
|
|
58
|
-
}
|
|
59
|
-
}, [loading]);
|
|
66
|
+
setPaginationModel(prev => ({ ...prev, page: prev.page + 1 }));
|
|
67
|
+
}, []);
|
|
60
68
|
|
|
61
69
|
return (
|
|
62
70
|
<DataGrid
|
|
63
|
-
rows={
|
|
71
|
+
rows={NO_ROWS}
|
|
64
72
|
columns={columns}
|
|
65
73
|
dataSource={dataSource}
|
|
66
|
-
|
|
67
|
-
// KEY PROPS FOR INFINITE SCROLL
|
|
68
|
-
pagination={false}
|
|
69
74
|
paginationMode="infinite"
|
|
70
75
|
sortingMode="server"
|
|
71
|
-
|
|
72
76
|
paginationModel={paginationModel}
|
|
73
77
|
onPaginationModelChange={setPaginationModel}
|
|
74
|
-
|
|
75
78
|
onRowsScrollEnd={handleScrollEnd}
|
|
79
|
+
height={400}
|
|
76
80
|
/>
|
|
77
81
|
);
|
|
78
82
|
}
|
|
79
83
|
```
|
|
80
84
|
|
|
85
|
+
The grid needs a bounded height (here `height={400}`) and the stylesheet import; without either the viewport never scrolls, every loaded row renders, and `onRowsScrollEnd` keeps firing until the whole dataset is loaded.
|
|
86
|
+
|
|
81
87
|
## Implementation Details
|
|
82
88
|
|
|
83
|
-
* **`onRowsScrollEnd`**:
|
|
84
|
-
* **
|
|
85
|
-
* **
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
* **`onRowsScrollEnd`**: Fired once when the end of the rows comes within 100px of the viewport bottom (`scrollHeight - scrollTop - clientHeight < 100`). It is re-armed when the user scrolls out of that zone or the row count changes, so one scroll gesture loads one page. It also fires after the rows change when the end is already in view, so a first page that does not fill the viewport keeps loading until it does. Horizontal scrolling never fires it.
|
|
90
|
+
* **Loading placeholders**: the skeleton rows shown while the next page loads are drawn after the last loaded row, only when the user has scrolled to the end of the data.
|
|
91
|
+
* **Ranges, not pages**: each request asks for `[rows loaded so far, (page + 1) * pageSize)`. Advancing the page twice within the 300 ms debounce, or while a request is in flight, still loads every row in between: one request runs at a time and, when it lands, the grid requests whatever the current page still needs. A response shorter than requested is treated as the end of the data until the page advances again.
|
|
92
|
+
* **Appending**: rows are appended; a row whose id (`getRowId`) is already loaded is skipped, so overlapping pages never show a row twice.
|
|
93
|
+
* **Restarting**: a sort, filter, page-size or `dataSource` (`getRows`) change replaces the rows with a new request from row 0 and reports page 0 through `onPaginationModelChange`. Setting a smaller page yourself reloads the list up to that page.
|
|
94
|
+
* **No pager**: `paginationMode="infinite"` ignores `pagination`; no pagination bar is shown and loaded rows are never sliced.
|
|
95
|
+
* **Stale responses**: a response for a list that has since restarted is discarded.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Keyboard Navigation & Accessibility
|
|
2
|
+
|
|
3
|
+
The grid follows the [WAI-ARIA data grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/): it is a single Tab stop, and the arrow keys move a focused cell inside it. This page lists the keys, explains how focus behaves, and documents the ARIA structure the grid exposes (v3.0).
|
|
4
|
+
|
|
5
|
+
## Focus model
|
|
6
|
+
|
|
7
|
+
- **One Tab stop.** The grid viewport (`role="grid"`) is in the page Tab order. Tabbing in puts focus on the last focused cell, or on the first cell (the select-all header checkbox with `checkboxSelection`, the first column header when there are no rows). While focus is inside the grid the viewport leaves the Tab order, so **Tab** and **Shift+Tab** leave the grid in one press.
|
|
8
|
+
- **Tab is not captured** outside edit mode. It moves to the next focusable element in the page, which may be a control you render in a cell (`renderCell`) or inside an expanded detail panel.
|
|
9
|
+
- **Focus is remembered.** Leaving the grid (Tab, clicking elsewhere, switching window) keeps the focused cell; coming back restores it. The focus ring is only drawn while the grid has focus and the keyboard is in use.
|
|
10
|
+
- **Focus survives virtualization.** When the focused row scrolls out of the render window, focus stays on the grid so keys keep working; the cell gets DOM focus back when it is rendered again. The grid never takes focus back from an element outside it.
|
|
11
|
+
- **Rows and columns that disappear.** If the focused row is removed or filtered out, focus moves to the row now at the same position; if the focused column is hidden, to the first visible data column; with no rows left, to the header.
|
|
12
|
+
- **Your own controls keep their keys.** Keys pressed in text inputs, textareas, selects and editable content rendered in cells, and anything inside a detail panel, are left to that element. On a button, link or checkbox rendered in a cell, **Enter** and **Space** activate it and the arrow keys still move grid focus.
|
|
13
|
+
|
|
14
|
+
## Keys
|
|
15
|
+
|
|
16
|
+
### Cells
|
|
17
|
+
|
|
18
|
+
| Key | Action |
|
|
19
|
+
| :--- | :--- |
|
|
20
|
+
| **Arrow keys** | Move one cell. Left/Right wrap to the previous/next row. Up from the first row moves to the header. Merged (spanned) cells count as one cell. |
|
|
21
|
+
| **Home** / **End** | First / last column of the row. |
|
|
22
|
+
| **Ctrl+Home** / **Ctrl+End** (Cmd on macOS) | First column of the header row / last column of the last row. |
|
|
23
|
+
| **PageUp** / **PageDown** | Up / down one page (`pageSize` with pagination, otherwise 10 rows). |
|
|
24
|
+
| **Enter** | Editable cell: open the editor. Other cells of a row with children (group row or tree-data parent): expand or collapse it. Other cells: same as clicking the row â fires `onRowClick` and toggles selection unless `disableRowSelectionOnClick`. |
|
|
25
|
+
| **Alt+ArrowRight** / **Alt+ArrowLeft** | Tree data / row grouping: expand / collapse the focused row. |
|
|
26
|
+
| **Shift+Space** | Select or deselect the focused row (when rows can be selected: `checkboxSelection`, or click selection not disabled). Synthetic group rows are not selectable. |
|
|
27
|
+
| **Space** | On the row checkbox: toggle the row. On the detail-panel toggle: expand or collapse the panel. Elsewhere it does nothing (it never scrolls the grid). |
|
|
28
|
+
| **Ctrl+A** (Cmd+A) | Select every row, when several rows can be selected. |
|
|
29
|
+
| **Ctrl+C** (Cmd+C) | Copy the selected rows (see [Clipboard](clipboard.md)). |
|
|
30
|
+
|
|
31
|
+
Arrow, Home/End, PageUp/PageDown and Space are always consumed, so the viewport does not scroll natively when focus cannot move further.
|
|
32
|
+
|
|
33
|
+
### While editing
|
|
34
|
+
|
|
35
|
+
| Key | Action |
|
|
36
|
+
| :--- | :--- |
|
|
37
|
+
| **Enter** | Commit. |
|
|
38
|
+
| **Escape** | Cancel. |
|
|
39
|
+
| **Tab** / **Shift+Tab** | Commit and open the next / previous editable cell (checkbox, expand and reorder columns are skipped). With no editable cell left, focus leaves the grid and the edit commits. |
|
|
40
|
+
|
|
41
|
+
When an edit ends, focus returns to the edited cell unless you moved it elsewhere.
|
|
42
|
+
|
|
43
|
+
### Column headers
|
|
44
|
+
|
|
45
|
+
| Key | Action |
|
|
46
|
+
| :--- | :--- |
|
|
47
|
+
| **Enter** / **Space** | Sort the column (asc â desc â none), replacing the sort model; with `multiSort`, or with **Shift** held, the column is added to (or updated or removed from) the sort model instead, as a header click or shift-click does. On the select-all header: select or clear all rows. |
|
|
48
|
+
| **Alt+ArrowDown** or **Ctrl+Enter** (Cmd+Enter) | Open the column menu. |
|
|
49
|
+
| **Alt+ArrowRight** / **Alt+ArrowLeft** | Widen / narrow the column by 10px (with **Shift**: 50px). Not for `resizable: false` columns. |
|
|
50
|
+
| **ArrowDown** | Move to the first row. |
|
|
51
|
+
|
|
52
|
+
In the column menu, **ArrowUp** / **ArrowDown** / **Home** / **End** move between items, **Enter** / **Space** choose one, and **Escape** or **Tab** closes the menu. Focus returns to the column header.
|
|
53
|
+
|
|
54
|
+
### List view
|
|
55
|
+
|
|
56
|
+
In `listView`, focus moves between whole rows instead of cells:
|
|
57
|
+
|
|
58
|
+
| Key | Action |
|
|
59
|
+
| :--- | :--- |
|
|
60
|
+
| **Tab** | The list is one Tab stop: it lands on the last focused row (the first row at first). Row checkboxes are not separate Tab stops. |
|
|
61
|
+
| **ArrowDown** / **ArrowUp** | Next / previous row. |
|
|
62
|
+
| **Home** / **End** | First / last row. |
|
|
63
|
+
| **PageDown** / **PageUp** | Ten rows down / up. |
|
|
64
|
+
| **Enter** / **Space** | A row with children (tree-data parent, group row): expand or collapse it. Any other row: same as clicking it (`onRowClick`, and click selection unless `disableRowSelectionOnClick`). |
|
|
65
|
+
| **Alt+ArrowRight** / **Alt+ArrowLeft** | Expand / collapse the focused parent row. |
|
|
66
|
+
| **Shift+Space** | Select or deselect the focused row, when rows can be selected. |
|
|
67
|
+
|
|
68
|
+
Keys pressed in content your `renderCell` puts in a row (inputs, buttons) are left to that content.
|
|
69
|
+
|
|
70
|
+
## ARIA structure
|
|
71
|
+
|
|
72
|
+
| Element | Attributes |
|
|
73
|
+
| :--- | :--- |
|
|
74
|
+
| Viewport | `role="grid"`, `aria-rowcount`, `aria-colcount`, `aria-multiselectable` (true when several rows can be selected), `aria-busy`. |
|
|
75
|
+
| Header rows | `role="row"` with `aria-rowindex`: column-group rows first, then the column header row. |
|
|
76
|
+
| Data rows | `role="row"`, `aria-rowindex` counted across the whole dataset (page 2 continues after page 1; bottom-pinned rows come after every page), `aria-selected`. Tree-data and row-grouping rows add `aria-level`; rows with children add `aria-expanded`. |
|
|
77
|
+
| Cells and headers | `aria-colindex`, identical for a header and the body cells below it. The system columns (reorder handle, detail-panel toggle, checkbox) come first. |
|
|
78
|
+
| Sorted headers | Only the primary sort column has `aria-sort="ascending"` / `"descending"`; with several sort keys every sorted header also gets an `aria-description` such as "Sorted descending, sort priority 2 of 2". |
|
|
79
|
+
| Detail panel | A `role="row"` with one `role="gridcell"` spanning every column (`aria-colspan`). The expand cell has `aria-expanded` and, while open, `aria-controls` pointing at the panel. |
|
|
80
|
+
|
|
81
|
+
`aria-rowcount` counts the header rows plus top-pinned, centre and bottom-pinned rows; under server-side pagination it uses `rowCount`. `aria-colcount` counts the rendered columns: visible data columns (including the `groupingColDef` column and generated pivot columns) plus the system columns.
|
|
82
|
+
|
|
83
|
+
`GridCellParams.colIndex` / `GridRenderCellParams.colIndex` (and `renderHeader`'s `colIndex`) are the zero-based position of the column among the **visible data columns in render order** (left-pinned, unpinned, right-pinned). They do not change with horizontal scrolling. `aria-colindex` is `colIndex + 1 +` the number of system columns.
|
|
@@ -6,9 +6,16 @@ Switch from a multi-column grid to a single-column detailed list. Perfect for mo
|
|
|
6
6
|
|
|
7
7
|
## đī¸ Usage
|
|
8
8
|
|
|
9
|
-
Enable List View by passing the `listView` prop. You must also provide a `listViewColumn` object which defines how each "Card" should be rendered.
|
|
9
|
+
Enable List View by passing the `listView` prop. You must also provide a `listViewColumn` object which defines how each "Card" should be rendered. Without `listViewColumn` the grid view is shown instead and a dev warning is logged.
|
|
10
|
+
|
|
11
|
+
List view honours `loading`, `slots.loadingOverlay`, `slots.noRowsOverlay` and `slots.footer` like the grid view: while loading it shows a progress bar (or your loading overlay) rather than the empty state, and `slots.footer` replaces its pagination controls.
|
|
10
12
|
|
|
11
13
|
```tsx
|
|
14
|
+
import { DataGrid, type GridListViewColDef } from '@opencorestack/opengridx';
|
|
15
|
+
|
|
16
|
+
// Your own row type: an interface or a type alias both work (v3+).
|
|
17
|
+
interface Employee { id: number; name: string; role: string; status: string; avatar: string }
|
|
18
|
+
|
|
12
19
|
const listColDef: GridListViewColDef<Employee> = {
|
|
13
20
|
field: 'card',
|
|
14
21
|
renderCell: (params) => <MyEmployeeCard row={params.row} />
|
|
@@ -32,10 +39,24 @@ When `listView` is active:
|
|
|
32
39
|
- **Card Containers**: Each row maintains its standard selection and hover behaviors, but visually acts as a card.
|
|
33
40
|
|
|
34
41
|
### Custom Card Rendering
|
|
35
|
-
The `renderCell` function in `listViewColumn` receives all standard `GridRenderCellParams`, allowing you to build rich, complex UIs for each item
|
|
42
|
+
The `renderCell` function in `listViewColumn` receives all standard `GridRenderCellParams`, allowing you to build rich, complex UIs for each item:
|
|
43
|
+
|
|
44
|
+
| Param | Value |
|
|
45
|
+
| :--- | :--- |
|
|
46
|
+
| `row` | The consumer's row object. |
|
|
47
|
+
| `field` | `listViewColumn.field`. |
|
|
48
|
+
| `value` | The cell value for that field: the grid column with the same `field` reads it through its `valueGetter`; otherwise `row[field]` (so a synthetic field such as `'card'` gives `undefined`). |
|
|
49
|
+
| `formattedValue` | That column's `valueFormatter` result, or `String(value)` (`''` for null/undefined). |
|
|
50
|
+
| `colDef` | The grid column with the same `field`, or `listViewColumn` itself. |
|
|
51
|
+
| `rowMeta` | Tree-data / row-grouping metadata (`treeDepth`, `hasChildren`, âĻ), `undefined` for flat rows. |
|
|
52
|
+
| `rowIndex` | Index of the row among the rendered rows (pinned rows first). |
|
|
53
|
+
|
|
54
|
+
`renderCell` only ever receives real rows: while an infinite-scroll page loads, no placeholder rows are passed to it.
|
|
55
|
+
|
|
56
|
+
Under row grouping (and for auto-created tree parents) `renderCell` is also called for group rows (`rowMeta.isGroupRow`). Returning `undefined` for one shows its `rowMeta.groupLabel`. Group rows have no selection checkbox, and the `N items` summary counts data rows only, as in the grid view (v3.0+). A `renderCell` that throws shows an error in that row only, and a throwing `valueGetter` / `valueFormatter` of the grid column reads as `undefined` / the unformatted value (v3.0+).
|
|
36
57
|
|
|
37
58
|
```tsx
|
|
38
|
-
function MyEmployeeCard({ row }) {
|
|
59
|
+
function MyEmployeeCard({ row }: { row: Employee }) {
|
|
39
60
|
return (
|
|
40
61
|
<div className="employee-card">
|
|
41
62
|
<Avatar src={row.avatar} />
|
|
@@ -51,15 +72,47 @@ function MyEmployeeCard({ row }) {
|
|
|
51
72
|
|
|
52
73
|
---
|
|
53
74
|
|
|
75
|
+
## đ Pagination and infinite scroll
|
|
76
|
+
|
|
77
|
+
List view pages exactly like the grid view: with `pagination`, one page of the (unpinned) rows is shown between any pinned rows, also under tree data. The summary line above the list reads `N items ¡ page X of Y`, where under server pagination (`paginationMode="server"` with a `dataSource`) `N` and the page count come from the server's `rowCount`.
|
|
78
|
+
|
|
79
|
+
`onRowsScrollEnd` fires when the list is scrolled to within 100px of its bottom, so the [infinite scroll](./infinite-scroll.md) pattern works in list view too. As in the grid view, it fires once per arrival: it fires again only after the list leaves that zone or the row count changes (for example, when the next page has loaded).
|
|
80
|
+
|
|
81
|
+
The list view is **not virtualized**: it renders every row it shows (one page with `pagination`, otherwise all rows). Use pagination for large datasets: rendering tens of thousands of cards freezes the page, and in development the grid warns when the list view renders more than 2,000 items (v3.0.1+). `slots.footer` is rendered below the list (in place of its pagination controls); the aggregation footer row is not shown in list view.
|
|
82
|
+
|
|
83
|
+
## âŋ Accessibility
|
|
84
|
+
|
|
85
|
+
The list is a `role="grid"` whose rows carry an `aria-rowindex` over the whole data set (row 11 on the second page of 10) and whose `aria-rowcount` is the total number of rows. There is no header row in list view. Tree-data and grouped rows also carry `aria-level` and, for parents, `aria-expanded`; they are indented by depth, and parents show a chevron that expands or collapses them on click (the chevron is not a Tab stop).
|
|
86
|
+
|
|
87
|
+
### Keyboard (v3.0)
|
|
88
|
+
|
|
89
|
+
Rows are focusable with a roving tab stop:
|
|
90
|
+
|
|
91
|
+
| Key | Action |
|
|
92
|
+
| :--- | :--- |
|
|
93
|
+
| **Tab** | The list is one Tab stop: it lands on the last focused row (the first row at first). Row checkboxes are not separate Tab stops. |
|
|
94
|
+
| **ArrowDown** / **ArrowUp** | Next / previous row. |
|
|
95
|
+
| **Home** / **End** | First / last row. |
|
|
96
|
+
| **PageDown** / **PageUp** | Ten rows down / up. |
|
|
97
|
+
| **Enter** / **Space** | A row with children (tree-data parent, group row): expand or collapse it. Any other row: same as clicking it (`onRowClick`, and click selection unless `disableRowSelectionOnClick`). |
|
|
98
|
+
| **Alt+ArrowRight** / **Alt+ArrowLeft** | Expand / collapse the focused parent row. |
|
|
99
|
+
| **Shift+Space** | Select or deselect the focused row, when rows can be selected. |
|
|
100
|
+
|
|
101
|
+
Keys pressed in content your `renderCell` puts in a row (inputs, buttons) are left to that content.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
54
105
|
## đą Responsive Toggle
|
|
55
106
|
|
|
56
107
|
A common pattern is to toggle List View based on screen width:
|
|
57
108
|
|
|
58
109
|
```tsx
|
|
59
|
-
const isMobile = window.innerWidth < 768;
|
|
110
|
+
const isMobile = window.innerWidth < 768; // read once; use a resize listener or matchMedia to follow changes
|
|
60
111
|
|
|
61
112
|
<DataGrid
|
|
113
|
+
rows={rows}
|
|
114
|
+
columns={columns}
|
|
62
115
|
listView={isMobile}
|
|
63
|
-
|
|
116
|
+
listViewColumn={listColDef}
|
|
64
117
|
/>
|
|
65
118
|
```
|
|
@@ -6,17 +6,22 @@ OpenGridX provides multiple ways to handle data loading, from initial shimmers t
|
|
|
6
6
|
|
|
7
7
|
## đĻ´ Skeleton Loading
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
While `loading` is true and there are no rows to show, the grid body shows ten animated skeleton rows laid out with the current columns.
|
|
10
10
|
|
|
11
11
|
### Usage
|
|
12
12
|
```tsx
|
|
13
|
-
<DataGrid
|
|
13
|
+
<DataGrid rows={[]} columns={columns} loading />
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
### Features
|
|
17
|
-
- **
|
|
17
|
+
- **Column-aware**: Skeleton cells use the current column widths, plus the checkbox, detail-panel and drag-handle columns when those are enabled. With no columns yet, placeholder columns fill the width.
|
|
18
18
|
- **Shimmer Animation**: Uses a CSS-optimized animation for a premium feel.
|
|
19
|
-
- **
|
|
19
|
+
- **Custom overlay**: Pass `slots.loadingOverlay` to render your own component instead of the skeleton rows.
|
|
20
|
+
|
|
21
|
+
### Loading with rows already shown
|
|
22
|
+
When `loading` is true (or a data source request is running) and the grid already has rows, for example during a reload or a server-side sort, the rows stay visible and usable and a thin progress bar (`role="progressbar"`, class `ogx__loading-bar`) runs along the top of the grid. Pass `slots.loadingOverlay` to show your own indicator centred over the rows instead. Infinite scroll keeps its own indicator (skeleton rows at the bottom).
|
|
23
|
+
|
|
24
|
+
List view follows the same rules: while loading it shows a progress bar or `slots.loadingOverlay`, never the "No rows" state.
|
|
20
25
|
|
|
21
26
|
---
|
|
22
27
|
|
|
@@ -24,45 +29,38 @@ The grid features a smart skeleton loader that automatically calculates the numb
|
|
|
24
29
|
|
|
25
30
|
For large datasets, use the `paginationMode="infinite"` to load data as the user scrolls.
|
|
26
31
|
|
|
27
|
-
- **Trigger**:
|
|
32
|
+
- **Trigger**: `onRowsScrollEnd` fires when the user scrolls near the bottom; advance `paginationModel.page` there and the `dataSource` loads the next rows.
|
|
28
33
|
- **Feedback**: Displays skeleton rows at the bottom while new data is being fetched.
|
|
29
34
|
- **Guide**: See [Infinite Scroll Guide](./infinite-scroll.md) for full implementation details.
|
|
30
35
|
|
|
31
36
|
---
|
|
32
37
|
|
|
33
|
-
## đ¤ Lazy Loading (Viewport)
|
|
34
|
-
|
|
35
|
-
Unlike infinite scroll which appends data to the end, Lazy Loading fetches specific chunks based on the user's current scroll position (viewport).
|
|
36
|
-
|
|
37
|
-
- **Benefit**: Best for datasets with millions of records where users might "jump" to the middle of the scrollbar.
|
|
38
|
-
- **Implementation**: Managed via the `GridDataSource` and `rowCount` props.
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
38
|
## đ Global Search (Quick Filter)
|
|
43
39
|
|
|
44
|
-
Quickly filter the
|
|
40
|
+
Quickly filter the rows across all visible, filterable columns. The search box is part of `GridToolbar`.
|
|
45
41
|
|
|
46
42
|
### Basic Setup
|
|
47
43
|
|
|
48
|
-
Add the toolbar
|
|
44
|
+
Add the toolbar. The search box filters the grid on its own; pass `filterModel` / `onFilterModelChange` only when you want to control or observe the filter:
|
|
49
45
|
|
|
50
46
|
```tsx
|
|
51
47
|
<DataGrid
|
|
48
|
+
rows={rows}
|
|
49
|
+
columns={columns}
|
|
52
50
|
slots={{ toolbar: GridToolbar }}
|
|
53
|
-
filterModel={filterModel}
|
|
54
|
-
onFilterModelChange={setFilterModel}
|
|
55
51
|
/>
|
|
56
52
|
```
|
|
57
53
|
|
|
58
54
|
### Controlled Search
|
|
59
|
-
You can also control the search value externally via the `filterModel`.
|
|
55
|
+
You can also control the search value externally via the `filterModel`. Each entry is one term, and a row matches when every term is found in some visible column (the toolbar box splits typed text on whitespace the same way).
|
|
60
56
|
|
|
61
57
|
```tsx
|
|
62
58
|
<DataGrid
|
|
59
|
+
rows={rows}
|
|
60
|
+
columns={columns}
|
|
63
61
|
filterModel={{
|
|
64
62
|
items: [],
|
|
65
|
-
quickFilterValues: ['
|
|
63
|
+
quickFilterValues: ['john', 'london']
|
|
66
64
|
}}
|
|
67
65
|
/>
|
|
68
66
|
```
|
|
@@ -6,7 +6,7 @@ Display supplementary information for a row in an expandable panel without leavi
|
|
|
6
6
|
|
|
7
7
|
## đ ī¸ Basic Implementation
|
|
8
8
|
|
|
9
|
-
To enable detail panels,
|
|
9
|
+
To enable detail panels, provide two main props: `getDetailPanelContent` and `getDetailPanelHeight`.
|
|
10
10
|
|
|
11
11
|
```tsx
|
|
12
12
|
<DataGrid
|
|
@@ -14,14 +14,16 @@ To enable detail panels, provided two main props: `getDetailPanelContent` and `g
|
|
|
14
14
|
columns={columns}
|
|
15
15
|
getDetailPanelContent={(params) => (
|
|
16
16
|
<div style={{ padding: '20px' }}>
|
|
17
|
-
<h3>Details for {params.row.name}</h3>
|
|
18
|
-
<p>{params.row.description}</p>
|
|
17
|
+
<h3>Details for {String(params.row.name)}</h3>
|
|
18
|
+
<p>{String(params.row.description)}</p>
|
|
19
19
|
</div>
|
|
20
20
|
)}
|
|
21
21
|
getDetailPanelHeight={() => 'auto'} // or a fixed number
|
|
22
22
|
/>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
`getDetailPanelContent` and `getDetailPanelHeight` are called only for **expanded** rows, while they render (v3.0+). A collapsed row costs nothing, and the callbacks may read fields that only data rows have. Synthetic hierarchy rows (row-grouping group rows and generated tree-data parents) have no detail panel: they keep an empty cell in the expand column, are never passed to the callbacks, and their ids in `detailPanelExpandedRowIds` are ignored. Tree-data parents that are real rows do get a panel. If `getDetailPanelContent` throws, that panel shows an error marker and the rest of the grid keeps working.
|
|
26
|
+
|
|
25
27
|
---
|
|
26
28
|
|
|
27
29
|
## đšī¸ Controlled Expansion
|
|
@@ -29,7 +31,9 @@ To enable detail panels, provided two main props: `getDetailPanelContent` and `g
|
|
|
29
31
|
You can manage which rows are expanded by using the `detailPanelExpandedRowIds` and `onDetailPanelExpandedRowIdsChange` props.
|
|
30
32
|
|
|
31
33
|
```tsx
|
|
32
|
-
|
|
34
|
+
import type { GridRowId } from '@opencorestack/opengridx';
|
|
35
|
+
|
|
36
|
+
const [expandedIds, setExpandedIds] = useState<Set<GridRowId>>(new Set());
|
|
33
37
|
|
|
34
38
|
<DataGrid
|
|
35
39
|
detailPanelExpandedRowIds={expandedIds}
|
|
@@ -43,4 +47,13 @@ const [expandedIds, setExpandedIds] = useState(new Set());
|
|
|
43
47
|
|
|
44
48
|
- **Expansion Column**: A `+` icon is automatically added to the start of the row.
|
|
45
49
|
- **Pinning**: The expansion column can be pinned using `pinExpandColumn={true}`. This ensures the expansion trigger is always visible even when scrolling horizontally.
|
|
46
|
-
- **
|
|
50
|
+
- **Height**: `getDetailPanelHeight` returns a pixel height (a fixed-height scroll box; `0` renders a 0px panel) or `'auto'`. Without the prop every panel is 200px. An `'auto'` panel is measured when it renders and whenever its content resizes, and the rows below are laid out at the measured height. Until a panel has rendered once, 200px is reserved for it.
|
|
51
|
+
- **Virtualization**: The grid engine accounts for variable row heights when detail panels are open, including panels on pinned rows (keyboard navigation and `scrollToIndexes` keep rows clear of an expanded bottom-pinned panel).
|
|
52
|
+
|
|
53
|
+
## â¨ī¸ Keyboard & Accessibility
|
|
54
|
+
|
|
55
|
+
- **Space** or **Enter** on the expand cell toggles the panel (v3.0: Enter too).
|
|
56
|
+
- Keys typed into content you render in the panel (inputs, selects, buttons) are left to that content: the grid does not react to them, so typing a space no longer collapses the panel and arrow keys move the caret.
|
|
57
|
+
- **Tab** is not captured by the grid, so it reaches focusable content inside an open panel.
|
|
58
|
+
- Keyboard scrolling brings the row itself into view, not the row plus its panel, so moving onto a row with a panel taller than the viewport keeps the row visible.
|
|
59
|
+
- The panel is exposed as a `role="row"` containing one `role="gridcell"` that spans every column; the expand cell has `aria-expanded` and, while open, `aria-controls` pointing at the panel. The `+`/`â` button inside the cell is not a separate Tab stop.
|