@opencorestack/opengridx 2.0.2 → 2.0.4
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 +22 -0
- package/dist/index.d.ts +7 -0
- package/dist/opengridx.es.js +2457 -2432
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +16 -16
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +10 -4
- package/docs/components/aggregation-footer.md +1 -1
- package/docs/components/column-group-header.md +2 -9
- package/docs/components/column-resize.md +15 -16
- package/docs/components/column-visibility.md +5 -5
- package/docs/components/datagrid.md +2 -1
- package/docs/components/empty-state.md +1 -1
- package/docs/components/tooltip.md +4 -7
- package/docs/features/aggregation-pivot.md +2 -2
- package/docs/features/cell-spanning.md +1 -1
- package/docs/features/loading-states.md +5 -5
- package/docs/features/selection.md +1 -1
- package/docs/features/sorting-pagination.md +1 -1
- package/docs/features/virtualization.md +43 -10
- package/docs/roadmap.md +1 -1
- package/lib/components/DataGrid/DataGrid.tsx +3 -1
- package/lib/hooks/core/useGridScrollSync.ts +62 -15
- package/lib/hooks/core/useGridVirtualization.ts +4 -1
- package/lib/types/index.ts +7 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
package/docs/API_REFERENCE.md
CHANGED
|
@@ -18,13 +18,14 @@ The main component for displaying and interacting with data.
|
|
|
18
18
|
| `rowHeight` | `number` | `52` | Height of each row in pixels. |
|
|
19
19
|
| `headerHeight` | `number` | `56` | Height of the header row. |
|
|
20
20
|
| `autoHeight` | `boolean` | `false` | Adjust grid height to match row total. |
|
|
21
|
+
| `overscanRowCount` | `number` | `3` | Minimum rows rendered outside the visible viewport. The grid adapts this upward automatically based on scroll velocity — this prop sets the floor. |
|
|
21
22
|
| `loading` | `boolean` | `false` | Shows a loading skeleton overlay. |
|
|
22
23
|
| `checkboxSelection` | `boolean` | `false` | Enable row selection via checkboxes. |
|
|
23
24
|
| `pagination` | `boolean` | `false` | Enable the bottom pagination bar. |
|
|
24
25
|
| `paginationMode` | `'client' \| 'server' \| 'infinite'` | `'client'` | How to handle paging. |
|
|
25
26
|
| `paginationModel` | `GridPaginationModel` | — | Controlled pagination state (`{ page, pageSize }`). |
|
|
26
27
|
| `onPaginationModelChange` | `(model: GridPaginationModel) => void` | — | Fired when page or page size changes. |
|
|
27
|
-
| `pageSizeOptions` | `number[]` | `[10, 25, 50]` | Available page size options. |
|
|
28
|
+
| `pageSizeOptions` | `number[]` | `[10, 25, 50, 100]` | Available page size options. |
|
|
28
29
|
| `rowCount` | `number` | — | Total rows (required for server-side paging). |
|
|
29
30
|
| `height` | `number \| string` | `undefined` | Total height of the grid container. |
|
|
30
31
|
| `density` | `'compact' \| 'standard' \| 'comfortable'` | `'standard'` | Visual row density. |
|
|
@@ -82,8 +83,8 @@ The main component for displaying and interacting with data.
|
|
|
82
83
|
| `pinnedColumns` | `GridColumnPinning` | — | Columns pinned to the left or right viewport edges. See [Column & Row Pinning](#-column--row-pinning). |
|
|
83
84
|
| `onPinnedColumnsChange` | `(model: GridColumnPinning) => void` | — | Fired when column pinning changes. |
|
|
84
85
|
| `pinnedRows` | `GridRowPinning` | — | Row IDs pinned to the top or bottom of the viewport. |
|
|
85
|
-
| `pinCheckboxColumn` | `boolean` | `
|
|
86
|
-
| `pinExpandColumn` | `boolean` | `
|
|
86
|
+
| `pinCheckboxColumn` | `boolean` | `true` | Keeps the checkbox column visible during horizontal scrolling. |
|
|
87
|
+
| `pinExpandColumn` | `boolean` | `true` | Keeps the Master-Detail expansion column visible during horizontal scrolling. |
|
|
87
88
|
|
|
88
89
|
#### Inline Editing
|
|
89
90
|
|
|
@@ -138,7 +139,7 @@ The main component for displaying and interacting with data.
|
|
|
138
139
|
| Prop | Type | Default | Description |
|
|
139
140
|
| :--- | :--- | :--- | :--- |
|
|
140
141
|
| `ariaLabel` | `string` | — | ARIA label for the grid container element. |
|
|
141
|
-
| `noRowsLabel` | `string` | `'No
|
|
142
|
+
| `noRowsLabel` | `string` | `'No Data'` | Message shown in the empty-state overlay when there are no rows. |
|
|
142
143
|
| `className` | `string` | — | Additional CSS class applied to the outermost grid wrapper element. |
|
|
143
144
|
| `style` | `React.CSSProperties` | — | Inline styles applied to the grid wrapper element. |
|
|
144
145
|
|
|
@@ -246,6 +247,8 @@ Access these methods via the `apiRef` prop.
|
|
|
246
247
|
| `getAllColumns()` | `GridColDef[]` | Get all defined columns. |
|
|
247
248
|
| `getAggregationResult()` | `Record<string, unknown> \| null` | Get current aggregation results. |
|
|
248
249
|
| `getAggregationModel()` | `GridAggregationModel \| null` | Get the active aggregation configuration. |
|
|
250
|
+
| `getAllFilteredRows()` | `GridRowModel[]` | Get all filtered+sorted rows regardless of the current pagination window. Use for full-dataset exports. |
|
|
251
|
+
| `getGroupedExportRows()` | `GridGroupedExportRow[] \| null` | Get a flat ordered list reflecting the active row-grouping tree (group-header, leaf, subtotal, grand-total). Returns `null` when row grouping is not active. |
|
|
249
252
|
| `copySelectedRows()` | `Promise<void>` | Copy selected rows to clipboard as TSV. |
|
|
250
253
|
|
|
251
254
|
---
|
|
@@ -593,6 +596,7 @@ interface GridRowMeta {
|
|
|
593
596
|
treeDepth?: number;
|
|
594
597
|
groupingField?: string;
|
|
595
598
|
groupingValue?: unknown;
|
|
599
|
+
groupLabel?: string;
|
|
596
600
|
descendantCount?: number;
|
|
597
601
|
isExpanded?: boolean;
|
|
598
602
|
isGroupRow?: boolean;
|
|
@@ -1245,6 +1249,7 @@ interface PdfExportOptions {
|
|
|
1245
1249
|
aggregationResult?: Record<string, unknown> | null;
|
|
1246
1250
|
aggregationModel?: GridAggregationModel | null;
|
|
1247
1251
|
filterModel?: GridFilterModel | null;
|
|
1252
|
+
groupedRows?: GridGroupedExportRow[];
|
|
1248
1253
|
alternateRowColor?: boolean;
|
|
1249
1254
|
headerBackgroundColor?: string;
|
|
1250
1255
|
headerTextColor?: string;
|
|
@@ -1262,6 +1267,7 @@ interface PdfExportOptions {
|
|
|
1262
1267
|
| `aggregationResult` | `Record<string, unknown>` | — | From `apiRef.current.getAggregationResult()` |
|
|
1263
1268
|
| `aggregationModel` | `GridAggregationModel` | — | From `apiRef.current.getAggregationModel()` |
|
|
1264
1269
|
| `filterModel` | `GridFilterModel` | — | From `apiRef.current.getFilterModel()` |
|
|
1270
|
+
| `groupedRows` | `GridGroupedExportRow[]` | — | Pre-built grouped export rows from `apiRef.current.getGroupedExportRows()`. When provided, the PDF table reflects the grouping structure (group-header, indented leaves, subtotals, grand total). |
|
|
1265
1271
|
| `alternateRowColor` | `boolean` | `true` | Alternating row background shading |
|
|
1266
1272
|
| `headerBackgroundColor` | `string` | `'#4f46e5'` | Column header cell background (hex) |
|
|
1267
1273
|
| `headerTextColor` | `string` | `'#ffffff'` | Column header cell text color (hex) |
|
|
@@ -7,7 +7,7 @@ Internal component that renders the sticky aggregation totals row pinned to the
|
|
|
7
7
|
| Prop | Type | Description |
|
|
8
8
|
| :--- | :--- | :--- |
|
|
9
9
|
| `columns` | `GridColDef[]` | Ordered list of visible column definitions used to lay out cells. |
|
|
10
|
-
| `aggregationModel` | `GridAggregationModel` | Maps field names to function names (`'sum' \| 'avg' \| 'count' \| 'min' \| 'max'`). |
|
|
10
|
+
| `aggregationModel` | `GridAggregationModel` | Maps field names to function names (`'sum' \| 'avg' \| 'count' \| 'min' \| 'max' \| 'unique'`). |
|
|
11
11
|
| `aggregationResult` | `GridAggregationResult` | Computed values keyed by field name, produced by `useAggregation`. |
|
|
12
12
|
| `columnWidths` | `Record<string, number>` | Runtime pixel widths for each field, kept in sync with user resize actions. |
|
|
13
13
|
| `rowHeight` | `number` | Sets `min-height` on the footer row, matching the grid's configured row height. |
|
|
@@ -40,16 +40,9 @@ While the grid uses this component internally, you can customize its appearance
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
-
## 🎨
|
|
44
|
-
Replace the system's group header with a custom one for specialized styling or interactions.
|
|
43
|
+
## 🎨 Customization
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
<DataGrid
|
|
48
|
-
slots={{
|
|
49
|
-
columnGroupHeader: MyCustomGroupHeader
|
|
50
|
-
}}
|
|
51
|
-
/>
|
|
52
|
-
```
|
|
45
|
+
Column group headers are rendered internally; they cannot be replaced via the slots API. Style them through CSS targeting `.ogx__column-group-header`, or use the `columnGroupingModel` to control grouping structure.
|
|
53
46
|
|
|
54
47
|
## 📝 Best Practices
|
|
55
48
|
- **Logical Labels**: Use clear, concise labels for groups like `2024 Performance` or `Contact Info`.
|
|
@@ -22,15 +22,14 @@ const columns: GridColDef[] = [
|
|
|
22
22
|
];
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
###
|
|
26
|
-
|
|
25
|
+
### Disabling Resize Per Column
|
|
26
|
+
There is no single grid-wide prop to disable all column resizing. Set `resizable: false` on each column definition individually:
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/>
|
|
29
|
+
const columns: GridColDef[] = [
|
|
30
|
+
{ field: 'id', headerName: 'ID', resizable: false },
|
|
31
|
+
{ field: 'status', headerName: 'Status', resizable: false },
|
|
32
|
+
];
|
|
34
33
|
```
|
|
35
34
|
|
|
36
35
|
---
|
|
@@ -46,18 +45,18 @@ Disable resizing globally via the `DataGrid` props.
|
|
|
46
45
|
|
|
47
46
|
---
|
|
48
47
|
|
|
49
|
-
## 🎨
|
|
50
|
-
In the DataGrid, you can replace the default handle component for custom styling (e.g., a thicker line or different colors).
|
|
48
|
+
## 🎨 Styling the Resize Handle
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
The resize handle is not replaceable via the slots API. Style it through CSS:
|
|
51
|
+
|
|
52
|
+
```css
|
|
53
|
+
.ogx__resize-handle {
|
|
54
|
+
width: 4px;
|
|
55
|
+
background: #6366f1;
|
|
56
|
+
}
|
|
58
57
|
```
|
|
59
58
|
|
|
60
59
|
## 📝 Best Practices
|
|
61
60
|
- **Define Min Widths**: Always set a reasonable `minWidth` so columns don't accidentally become 0px wide.
|
|
62
|
-
- **Persistence**:
|
|
61
|
+
- **Persistence**: Use `onStateChange` to capture the current `columnWidths` from the state snapshot and save the user's preferred layout for their next visit.
|
|
63
62
|
- **Content Fit**: Remember that large data values might hide behind narrow columns; use tooltips for overflow coverage.
|
|
@@ -45,14 +45,14 @@ You can also render it persistently beside the grid or within a custom modal.
|
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
48
|
-
## 🎨
|
|
49
|
-
|
|
48
|
+
## 🎨 Controlling Visibility Programmatically
|
|
49
|
+
|
|
50
|
+
The column visibility panel is an internal component; it cannot be replaced via the slots API. Use the controlled props to drive visibility from outside:
|
|
50
51
|
|
|
51
52
|
```tsx
|
|
52
53
|
<DataGrid
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}}
|
|
54
|
+
columnVisibilityModel={model}
|
|
55
|
+
onColumnVisibilityModelChange={setModel}
|
|
56
56
|
/>
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -33,8 +33,9 @@ function MyGrid() {
|
|
|
33
33
|
| `disableMultipleRowSelection` | `boolean` | `false` | When `true`, at most one row can be selected at a time. |
|
|
34
34
|
| `pagination` | `boolean` | `false` | Enable/Disable bottom pagination bar. |
|
|
35
35
|
| `paginationModel` | `GridPaginationModel` | — | Controlled pagination state (`{ page, pageSize }`). Omit for uncontrolled; use `initialState` to set the initial page/pageSize. |
|
|
36
|
-
| `pageSizeOptions` | `number[]` | `[10, 25, 50]` | Available page size options. |
|
|
36
|
+
| `pageSizeOptions` | `number[]` | `[10, 25, 50, 100]` | Available page size options. |
|
|
37
37
|
| `initialState` | `GridInitialState` | `undefined` | Initial configuration (sorting, columns, pagination, etc). |
|
|
38
|
+
| `overscanRowCount` | `number` | `3` | Minimum rows rendered outside the visible viewport. The grid adapts this upward automatically based on scroll velocity — this prop sets the floor. |
|
|
38
39
|
|
|
39
40
|
## 🔃 Sorting
|
|
40
41
|
|
|
@@ -52,15 +52,12 @@ You can choose from four different placements: `top`, `bottom`, `left`, or `righ
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
-
## 🎨
|
|
56
|
-
|
|
55
|
+
## 🎨 Customization
|
|
56
|
+
|
|
57
|
+
The internal tooltip component is not replaceable via the slots API. For column-header tooltips, set the `description` field on `GridColDef` — it renders as the native `title` attribute on the header cell (v2.0.0+):
|
|
57
58
|
|
|
58
59
|
```tsx
|
|
59
|
-
|
|
60
|
-
slots={{
|
|
61
|
-
tooltip: GridTooltip
|
|
62
|
-
}}
|
|
63
|
-
/>
|
|
60
|
+
{ field: 'revenue', headerName: 'Revenue', description: 'Total billed revenue for the period' }
|
|
64
61
|
```
|
|
65
62
|
|
|
66
63
|
## 📝 Accessibility
|
|
@@ -25,12 +25,12 @@ Aggregation allows you to calculate summary values (Sum, Avg, etc.) for groups o
|
|
|
25
25
|
You can control where the aggregation results appear:
|
|
26
26
|
- **`footer`**: A sticky row at the bottom of the grid.
|
|
27
27
|
- **`inline`**: Values displayed within group headers (when Row Grouping is active).
|
|
28
|
-
- **`
|
|
28
|
+
- **`null`**: Skip aggregation for this group.
|
|
29
29
|
|
|
30
30
|
```tsx
|
|
31
31
|
<DataGrid
|
|
32
32
|
aggregationModel={model}
|
|
33
|
-
getAggregationPosition={(groupNode) => '
|
|
33
|
+
getAggregationPosition={(groupNode) => groupNode ? 'inline' : 'footer'}
|
|
34
34
|
/>
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -53,7 +53,7 @@ const columns: GridColDef<Row>[] = [
|
|
|
53
53
|
// Disable conflicting features
|
|
54
54
|
sortable: false, // ✅ Disable sorting
|
|
55
55
|
hideable: false, // ✅ Disable column hiding
|
|
56
|
-
|
|
56
|
+
// Column reorder is disabled grid-wide via disableColumnReorder on <DataGrid>
|
|
57
57
|
// Note: Avoid pinning this column
|
|
58
58
|
renderCell: (params) => {
|
|
59
59
|
if (params.row.isTotal) {
|
|
@@ -44,14 +44,14 @@ Unlike infinite scroll which appends data to the end, Lazy Loading fetches speci
|
|
|
44
44
|
Quickly filter the entire dataset across all visible columns. This is managed via the `GridToolbar`.
|
|
45
45
|
|
|
46
46
|
### Basic Setup
|
|
47
|
+
|
|
48
|
+
Add the toolbar and wire a filter model — the toolbar's search bar activates automatically when `onFilterModelChange` is wired:
|
|
49
|
+
|
|
47
50
|
```tsx
|
|
48
51
|
<DataGrid
|
|
49
52
|
slots={{ toolbar: GridToolbar }}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
showQuickFilter: true
|
|
53
|
-
}
|
|
54
|
-
}}
|
|
53
|
+
filterModel={filterModel}
|
|
54
|
+
onFilterModelChange={setFilterModel}
|
|
55
55
|
/>
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -70,7 +70,7 @@ Use `disableMultipleRowSelection` to cap selection to one row at a time. Clickin
|
|
|
70
70
|
| `disableMultipleRowSelection` | `boolean` | `false` | Restricts selection to a single row. |
|
|
71
71
|
| `rowSelectionModel` | `GridRowId[]` | `[]` | Controlled array of selected IDs. |
|
|
72
72
|
| `onRowSelectionModelChange` | `(model: GridRowSelectionModel) => void` | — | Fired when the selection changes. |
|
|
73
|
-
| `pinCheckboxColumn` | `boolean` | `
|
|
73
|
+
| `pinCheckboxColumn` | `boolean` | `true` | Keep the checkbox column visible during horizontal scroll. |
|
|
74
74
|
|
|
75
75
|
---
|
|
76
76
|
|
|
@@ -90,7 +90,7 @@ When using `paginationMode="server"`, you must provide the `rowCount` and handle
|
|
|
90
90
|
| `paginationMode` | `'client' \| 'server' \| 'infinite'` | `'client'` | Location of the pagination logic. |
|
|
91
91
|
| `paginationModel` | `GridPaginationModel` | — | Controlled pagination state (`{ page, pageSize }`). |
|
|
92
92
|
| `onPaginationModelChange` | `(model: GridPaginationModel) => void` | — | Fired when page or page size changes. |
|
|
93
|
-
| `pageSizeOptions` | `number[]` | `[10, 25, 50]` | Options for the rows-per-page selector. |
|
|
93
|
+
| `pageSizeOptions` | `number[]` | `[10, 25, 50, 100]` | Options for the rows-per-page selector. |
|
|
94
94
|
|
|
95
95
|
---
|
|
96
96
|
|
|
@@ -54,6 +54,18 @@ The grid supports both fixed and variable row heights:
|
|
|
54
54
|
/>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
The `density` prop overrides `rowHeight` with preset values:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<DataGrid
|
|
61
|
+
rows={rows}
|
|
62
|
+
columns={columns}
|
|
63
|
+
density="compact" // 32px rows
|
|
64
|
+
// density="standard" // uses rowHeight (default 52px)
|
|
65
|
+
// density="comfortable" // 72px rows
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
57
69
|
### Auto Height
|
|
58
70
|
|
|
59
71
|
Disable virtualization for small datasets:
|
|
@@ -72,16 +84,37 @@ Disable virtualization for small datasets:
|
|
|
72
84
|
|
|
73
85
|
### Overscan Buffer
|
|
74
86
|
|
|
75
|
-
The overscan buffer determines how many extra rows are rendered outside the viewport.
|
|
87
|
+
The overscan buffer determines how many extra rows are rendered outside the viewport. The grid adapts this buffer automatically based on scroll velocity so fast scrolls never produce blank flashes.
|
|
88
|
+
|
|
89
|
+
**Adaptive algorithm (`useGridScrollSync`):**
|
|
90
|
+
- Scroll velocity (px/ms) is measured on every scroll event
|
|
91
|
+
- The overscan tier is chosen from the table below and applied immediately
|
|
92
|
+
- 200 ms after scrolling stops the buffer decays back to `overscanRowCount`
|
|
93
|
+
|
|
94
|
+
| Velocity (px/ms) | Overscan rows |
|
|
95
|
+
|---|---|
|
|
96
|
+
| < 0.5 | 3 |
|
|
97
|
+
| 0.5 – 3 | 5 |
|
|
98
|
+
| 3 – 15 | 12 |
|
|
99
|
+
| 15 – 40 | 20 |
|
|
100
|
+
| > 40 | 30 |
|
|
101
|
+
|
|
102
|
+
**Configuring the floor:**
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// Never render fewer than 5 rows outside the viewport
|
|
106
|
+
<DataGrid
|
|
107
|
+
rows={rows}
|
|
108
|
+
columns={columns}
|
|
109
|
+
overscanRowCount={5}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
76
112
|
|
|
77
|
-
|
|
78
|
-
- Default overscan: **5 rows** (above and below viewport)
|
|
79
|
-
- Total rendered rows: ~(viewport rows + 10)
|
|
113
|
+
`overscanRowCount` (default `3`) sets the minimum — the adaptive algorithm always produces a value ≥ this floor.
|
|
80
114
|
|
|
81
115
|
**Trade-offs:**
|
|
82
|
-
- **Smaller
|
|
83
|
-
- **Larger
|
|
84
|
-
- **Very large overscan (10+)**: Diminishing returns, wasted rendering
|
|
116
|
+
- **Smaller floor (1-3)**: Less idle memory, relies on adaptive headroom for fast scrolls
|
|
117
|
+
- **Larger floor (5-8)**: Pre-renders more rows at rest, trades memory for smoother slow scrolls
|
|
85
118
|
|
|
86
119
|
### Scroll Performance
|
|
87
120
|
|
|
@@ -166,18 +199,18 @@ You should see console logs only for visible rows + overscan buffer.
|
|
|
166
199
|
## Future Improvements
|
|
167
200
|
|
|
168
201
|
- [ ] Column virtualization for grids with 50+ columns
|
|
169
|
-
- [
|
|
202
|
+
- [x] Adaptive overscan based on scroll velocity (`overscanRowCount` prop, v2.0.4)
|
|
170
203
|
- [ ] Virtual scrollbar for extremely large datasets (millions of rows)
|
|
171
204
|
- [ ] Intersection Observer API for better scroll detection
|
|
172
205
|
|
|
173
206
|
## Comparison with MUI X DataGrid
|
|
174
207
|
|
|
175
|
-
| Feature |
|
|
208
|
+
| Feature | OpenGridX | MUI X DataGrid |
|
|
176
209
|
|---------|--------------|----------------|
|
|
177
210
|
| Row virtualization | ✅ Yes | ✅ Yes |
|
|
178
211
|
| Column virtualization | ❌ Planned | ✅ Yes (Pro) |
|
|
179
212
|
| Variable row heights | ✅ Yes | ✅ Yes |
|
|
180
|
-
| Overscan buffer |
|
|
213
|
+
| Overscan buffer | 3–30 rows (adaptive, velocity-based) | 3-8 rows (adaptive) |
|
|
181
214
|
| Max recommended rows | 100,000+ | 100,000+ |
|
|
182
215
|
|
|
183
216
|
## Related Documentation
|
package/docs/roadmap.md
CHANGED
|
@@ -5,7 +5,7 @@ This document tracks the current status of features in `OpenGridX` and outlines
|
|
|
5
5
|
## ✅ Implemented Features
|
|
6
6
|
|
|
7
7
|
### Core
|
|
8
|
-
* **Virtualization**: High-performance row
|
|
8
|
+
* **Virtualization**: High-performance row virtualization for handling large datasets. Column virtualization is planned (currently all columns are rendered).
|
|
9
9
|
* **Sorting**: Client-side and server-side multi-column sorting.
|
|
10
10
|
* **Filtering**: Client-side filtering with support for various operators.
|
|
11
11
|
* **Pagination**: Client-side and server-side pagination with customizable page sizes.
|
|
@@ -118,6 +118,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
118
118
|
noRowsLabel = 'No Data',
|
|
119
119
|
localeText,
|
|
120
120
|
apiRef: propApiRef,
|
|
121
|
+
overscanRowCount = 3,
|
|
121
122
|
} = props;
|
|
122
123
|
|
|
123
124
|
const effectiveNoRowsLabel = localeText?.noRowsLabel ?? noRowsLabel;
|
|
@@ -301,7 +302,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
301
302
|
setRowCount
|
|
302
303
|
} = gridData;
|
|
303
304
|
|
|
304
|
-
const { scrollTop, scrollLeft, handleScroll } = useGridScrollSync({ onRowsScrollEnd });
|
|
305
|
+
const { scrollTop, scrollLeft, overscanRows, handleScroll } = useGridScrollSync({ onRowsScrollEnd, overscanRowCount });
|
|
305
306
|
|
|
306
307
|
useEffect(() => {
|
|
307
308
|
if (propApiRef) {
|
|
@@ -693,6 +694,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
693
694
|
checkboxSelection,
|
|
694
695
|
pinCheckboxColumn,
|
|
695
696
|
pinExpandColumn,
|
|
697
|
+
overscanRows,
|
|
696
698
|
});
|
|
697
699
|
|
|
698
700
|
|
|
@@ -3,42 +3,84 @@ import type { GridRowScrollEndParams } from '../../types';
|
|
|
3
3
|
|
|
4
4
|
export interface UseGridScrollSyncParams {
|
|
5
5
|
onRowsScrollEnd?: (params: GridRowScrollEndParams) => void;
|
|
6
|
+
/** Minimum overscan rows — the adaptive algorithm never goes below this. Defaults to 3. */
|
|
7
|
+
overscanRowCount?: number;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
export interface UseGridScrollSyncResult {
|
|
9
11
|
scrollTop: number;
|
|
10
12
|
scrollLeft: number;
|
|
13
|
+
/** Dynamically computed overscan row count based on current scroll velocity. */
|
|
14
|
+
overscanRows: number;
|
|
11
15
|
handleScroll: (event: React.UIEvent<HTMLDivElement>) => void;
|
|
12
16
|
}
|
|
13
17
|
|
|
18
|
+
/** Maps scroll velocity (px/ms) to an overscan row count. */
|
|
19
|
+
function velocityToOverscan(velocity: number, minOverscan: number): number {
|
|
20
|
+
let base: number;
|
|
21
|
+
if (velocity < 0.5) base = 3;
|
|
22
|
+
else if (velocity < 3) base = 5;
|
|
23
|
+
else if (velocity < 15) base = 12;
|
|
24
|
+
else if (velocity < 40) base = 20;
|
|
25
|
+
else base = 30;
|
|
26
|
+
return Math.max(base, minOverscan);
|
|
27
|
+
}
|
|
28
|
+
|
|
14
29
|
export function useGridScrollSync(params: UseGridScrollSyncParams): UseGridScrollSyncResult {
|
|
15
|
-
const { onRowsScrollEnd } = params;
|
|
30
|
+
const { onRowsScrollEnd, overscanRowCount = 3 } = params;
|
|
16
31
|
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
32
|
+
const pendingRef = useRef({ scrollTop: 0, scrollLeft: 0, velocity: 0 });
|
|
33
|
+
const prevRef = useRef({ scrollTop: 0, scrollLeft: 0, time: 0 });
|
|
34
|
+
const rafRef = useRef<number | null>(null);
|
|
35
|
+
const decayRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
36
|
+
|
|
37
|
+
const [scrollState, setScrollState] = useState({
|
|
38
|
+
scrollTop: 0,
|
|
39
|
+
scrollLeft: 0,
|
|
40
|
+
overscanRows: overscanRowCount,
|
|
41
|
+
});
|
|
20
42
|
|
|
21
43
|
useEffect(() => {
|
|
22
44
|
return () => {
|
|
23
|
-
if (
|
|
45
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
46
|
+
if (decayRef.current !== null) clearTimeout(decayRef.current);
|
|
24
47
|
};
|
|
25
48
|
}, []);
|
|
26
49
|
|
|
27
50
|
const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
|
|
28
51
|
const target = event.currentTarget;
|
|
29
|
-
|
|
52
|
+
const now = performance.now();
|
|
53
|
+
|
|
54
|
+
// Velocity: total positional delta / elapsed time (px/ms)
|
|
55
|
+
const deltaPos = Math.abs(target.scrollTop - prevRef.current.scrollTop)
|
|
56
|
+
+ Math.abs(target.scrollLeft - prevRef.current.scrollLeft);
|
|
57
|
+
const deltaTime = now - prevRef.current.time;
|
|
58
|
+
const velocity = deltaTime > 0 ? deltaPos / deltaTime : 0;
|
|
30
59
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
60
|
+
prevRef.current = { scrollTop: target.scrollTop, scrollLeft: target.scrollLeft, time: now };
|
|
61
|
+
pendingRef.current = { scrollTop: target.scrollTop, scrollLeft: target.scrollLeft, velocity };
|
|
62
|
+
|
|
63
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
64
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
65
|
+
rafRef.current = null;
|
|
66
|
+
const { scrollTop, scrollLeft, velocity: v } = pendingRef.current;
|
|
67
|
+
setScrollState({
|
|
68
|
+
scrollTop,
|
|
69
|
+
scrollLeft,
|
|
70
|
+
overscanRows: velocityToOverscan(v, overscanRowCount),
|
|
71
|
+
});
|
|
35
72
|
});
|
|
36
73
|
|
|
74
|
+
// Decay back to the minimum overscan 200ms after scrolling stops
|
|
75
|
+
if (decayRef.current !== null) clearTimeout(decayRef.current);
|
|
76
|
+
decayRef.current = setTimeout(() => {
|
|
77
|
+
decayRef.current = null;
|
|
78
|
+
setScrollState(prev => ({ ...prev, overscanRows: overscanRowCount }));
|
|
79
|
+
}, 200);
|
|
80
|
+
|
|
37
81
|
if (onRowsScrollEnd) {
|
|
38
82
|
const { scrollTop, scrollHeight, clientHeight } = target;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (scrollHeight - scrollTop - clientHeight < scrollThreshold) {
|
|
83
|
+
if (scrollHeight - scrollTop - clientHeight < 100) {
|
|
42
84
|
onRowsScrollEnd({
|
|
43
85
|
visibleTop: scrollTop,
|
|
44
86
|
visibleBottom: scrollTop + clientHeight,
|
|
@@ -46,7 +88,12 @@ export function useGridScrollSync(params: UseGridScrollSyncParams): UseGridScrol
|
|
|
46
88
|
});
|
|
47
89
|
}
|
|
48
90
|
}
|
|
49
|
-
}, [onRowsScrollEnd]);
|
|
91
|
+
}, [onRowsScrollEnd, overscanRowCount]);
|
|
50
92
|
|
|
51
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
scrollTop: scrollState.scrollTop,
|
|
95
|
+
scrollLeft: scrollState.scrollLeft,
|
|
96
|
+
overscanRows: scrollState.overscanRows,
|
|
97
|
+
handleScroll,
|
|
98
|
+
};
|
|
52
99
|
}
|
|
@@ -14,6 +14,8 @@ export interface UseGridVirtualizationParams<R extends GridRowModel> {
|
|
|
14
14
|
checkboxSelection: boolean;
|
|
15
15
|
pinCheckboxColumn: boolean;
|
|
16
16
|
pinExpandColumn: boolean;
|
|
17
|
+
/** Adaptive overscan row count from useGridScrollSync. Defaults to 5. */
|
|
18
|
+
overscanRows?: number;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface GridVirtualizationResult<R extends GridRowModel = GridRowModel> {
|
|
@@ -57,6 +59,7 @@ export function useGridVirtualization<R extends GridRowModel>(
|
|
|
57
59
|
checkboxSelection,
|
|
58
60
|
pinCheckboxColumn,
|
|
59
61
|
pinExpandColumn,
|
|
62
|
+
overscanRows = 5,
|
|
60
63
|
} = params;
|
|
61
64
|
|
|
62
65
|
return useMemo<GridVirtualizationResult<R>>(() => {
|
|
@@ -120,7 +123,6 @@ export function useGridVirtualization<R extends GridRowModel>(
|
|
|
120
123
|
const scrollbarBuffer = hasHorizontalScroll ? 16 : 0;
|
|
121
124
|
const totalHeight = pinnedTopHeight + unpinnedRowsHeight + pinnedBottomHeight + scrollbarBuffer + 2;
|
|
122
125
|
|
|
123
|
-
const overscanRows = 5;
|
|
124
126
|
let firstRowIndex = 0;
|
|
125
127
|
let lastRowIndex = unpinnedRowsLength - 1;
|
|
126
128
|
let offsetTop = 0;
|
|
@@ -182,5 +184,6 @@ export function useGridVirtualization<R extends GridRowModel>(
|
|
|
182
184
|
checkboxSelection,
|
|
183
185
|
pinCheckboxColumn,
|
|
184
186
|
pinExpandColumn,
|
|
187
|
+
overscanRows,
|
|
185
188
|
]);
|
|
186
189
|
}
|
package/lib/types/index.ts
CHANGED
|
@@ -608,6 +608,13 @@ export interface DataGridProps<R extends GridRowModel = GridRowModel> {
|
|
|
608
608
|
headerHeight?: number;
|
|
609
609
|
/** If true, the grid height will adjust to match the total height of its rows. */
|
|
610
610
|
autoHeight?: boolean;
|
|
611
|
+
/**
|
|
612
|
+
* Minimum number of rows to render outside the visible viewport (overscan buffer).
|
|
613
|
+
* The grid adapts this value upward automatically based on scroll velocity to prevent
|
|
614
|
+
* blank flashes during fast scrolling. This prop sets the floor — the buffer never
|
|
615
|
+
* drops below this value regardless of scroll speed. Defaults to `3`.
|
|
616
|
+
*/
|
|
617
|
+
overscanRowCount?: number;
|
|
611
618
|
|
|
612
619
|
/** Sorting mode: 'client' (default) or 'server'. */
|
|
613
620
|
sortingMode?: 'client' | 'server';
|
package/llms.txt
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencorestack/opengridx",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "OpenGridX: High-performance React data infrastructure. Unlock advanced Row Grouping, Excel Export, and Column Pinning without the usual \"Pro\" gatekeeping. Built for speed, scale, and complete architectural freedom. Fully open, virtualization-ready, and feature-complete.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/opengridx.umd.js",
|