@alaarab/ogrid-mcp 2.4.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/README.md +68 -0
- package/bundled-docs/api/README.md +94 -0
- package/bundled-docs/api/column-def.mdx +379 -0
- package/bundled-docs/api/components-column-chooser.mdx +310 -0
- package/bundled-docs/api/components-column-header-filter.mdx +363 -0
- package/bundled-docs/api/components-datagrid-table.mdx +316 -0
- package/bundled-docs/api/components-pagination-controls.mdx +344 -0
- package/bundled-docs/api/components-sidebar.mdx +427 -0
- package/bundled-docs/api/components-status-bar.mdx +309 -0
- package/bundled-docs/api/grid-api.mdx +299 -0
- package/bundled-docs/api/js-api.mdx +198 -0
- package/bundled-docs/api/ogrid-props.mdx +244 -0
- package/bundled-docs/api/types.mdx +640 -0
- package/bundled-docs/features/cell-references.mdx +225 -0
- package/bundled-docs/features/column-chooser.mdx +279 -0
- package/bundled-docs/features/column-groups.mdx +290 -0
- package/bundled-docs/features/column-pinning.mdx +282 -0
- package/bundled-docs/features/column-reordering.mdx +359 -0
- package/bundled-docs/features/column-types.mdx +181 -0
- package/bundled-docs/features/context-menu.mdx +216 -0
- package/bundled-docs/features/csv-export.mdx +227 -0
- package/bundled-docs/features/editing.mdx +377 -0
- package/bundled-docs/features/filtering.mdx +330 -0
- package/bundled-docs/features/formulas.mdx +381 -0
- package/bundled-docs/features/grid-api.mdx +311 -0
- package/bundled-docs/features/keyboard-navigation.mdx +236 -0
- package/bundled-docs/features/pagination.mdx +245 -0
- package/bundled-docs/features/performance.mdx +433 -0
- package/bundled-docs/features/row-selection.mdx +256 -0
- package/bundled-docs/features/server-side-data.mdx +291 -0
- package/bundled-docs/features/sidebar.mdx +234 -0
- package/bundled-docs/features/sorting.mdx +241 -0
- package/bundled-docs/features/spreadsheet-selection.mdx +201 -0
- package/bundled-docs/features/status-bar.mdx +205 -0
- package/bundled-docs/features/toolbar.mdx +284 -0
- package/bundled-docs/features/virtual-scrolling.mdx +624 -0
- package/bundled-docs/getting-started/installation.mdx +216 -0
- package/bundled-docs/getting-started/overview.mdx +151 -0
- package/bundled-docs/getting-started/quick-start.mdx +425 -0
- package/bundled-docs/getting-started/vanilla-js.mdx +191 -0
- package/bundled-docs/guides/accessibility.mdx +550 -0
- package/bundled-docs/guides/controlled-vs-uncontrolled.mdx +153 -0
- package/bundled-docs/guides/custom-cell-editors.mdx +201 -0
- package/bundled-docs/guides/framework-showcase.mdx +200 -0
- package/bundled-docs/guides/mcp-live-testing.mdx +291 -0
- package/bundled-docs/guides/mcp.mdx +172 -0
- package/bundled-docs/guides/migration-from-ag-grid.mdx +223 -0
- package/bundled-docs/guides/theming.mdx +211 -0
- package/dist/esm/bridge-client.d.ts +87 -0
- package/dist/esm/bridge-client.js +162 -0
- package/dist/esm/index.js +1060 -0
- package/package.json +43 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 20
|
|
3
|
+
title: Performance
|
|
4
|
+
description: Automatic CSS containment, opt-in Web Worker sort/filter, and column virtualization for large datasets
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Performance
|
|
9
|
+
|
|
10
|
+
OGrid is built with performance as a first-class concern. Three complementary features cover the main bottlenecks in large data grids:
|
|
11
|
+
|
|
12
|
+
| Feature | Opt-in? | Benefit |
|
|
13
|
+
|---------|---------|---------|
|
|
14
|
+
| **CSS Containment** | No (automatic) | Isolates cell layout and paint — prevents off-screen style recalculation |
|
|
15
|
+
| **Column Virtualization** | Yes | Renders only visible columns — scales to hundreds of columns |
|
|
16
|
+
| **Web Worker Sort/Filter** | Yes | Offloads sort and filter work to a background thread — keeps the UI thread free |
|
|
17
|
+
|
|
18
|
+
For row virtualization (rendering only visible rows), see [Virtual Scrolling](./virtual-scrolling).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## CSS Containment (Automatic)
|
|
23
|
+
|
|
24
|
+
OGrid applies CSS containment automatically to every grid cell — no configuration needed.
|
|
25
|
+
|
|
26
|
+
```css
|
|
27
|
+
/* Applied automatically to every body cell */
|
|
28
|
+
td.ogrid-cell {
|
|
29
|
+
contain: content; /* isolate layout + paint + style */
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Pinned columns use position: sticky — containment is relaxed to avoid conflicts */
|
|
33
|
+
td.ogrid-cell[data-pinned] {
|
|
34
|
+
contain: none;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The browser uses `contain: content` to skip layout and paint work for cells that are not visible in the viewport. Combined with `content-visibility: auto` on non-virtualized rows, the browser can skip off-screen row layout entirely:
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
/* Non-virtualized rows: browser skips layout for off-screen rows */
|
|
42
|
+
tr[data-row]:not([data-virtual-scroll]) {
|
|
43
|
+
content-visibility: auto;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The `data-virtual-scroll` attribute on the `<table>` element gates this behavior — when row virtualization is active, spacer rows already handle positioning, so `content-visibility` is not applied.
|
|
48
|
+
|
|
49
|
+
**You do not need to configure anything.** These rules are included in the default stylesheet for every framework package.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Web Worker Sort/Filter (Opt-in)
|
|
54
|
+
|
|
55
|
+
Enable background thread sorting and filtering by setting `workerSort: true` on the grid:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<OGrid
|
|
59
|
+
columns={columns}
|
|
60
|
+
data={data}
|
|
61
|
+
getRowId={(r) => r.id}
|
|
62
|
+
workerSort={true}
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
When enabled, sort and filter operations run in an inline Web Worker (created from a `Blob` URL) instead of on the main thread. The UI remains fully interactive while the worker processes the data.
|
|
67
|
+
|
|
68
|
+
### Quick Example
|
|
69
|
+
|
|
70
|
+
<Tabs groupId="framework">
|
|
71
|
+
<TabItem value="react" label="React" default>
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
|
|
75
|
+
interface Row {
|
|
76
|
+
id: number;
|
|
77
|
+
name: string;
|
|
78
|
+
revenue: number;
|
|
79
|
+
region: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const columns: IColumnDef<Row>[] = [
|
|
83
|
+
{ columnId: 'id', name: 'ID', type: 'numeric' },
|
|
84
|
+
{ columnId: 'name', name: 'Name', sortable: true },
|
|
85
|
+
{ columnId: 'revenue', name: 'Revenue', type: 'numeric', sortable: true },
|
|
86
|
+
{ columnId: 'region', name: 'Region', sortable: true },
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
function App() {
|
|
90
|
+
return (
|
|
91
|
+
<OGrid
|
|
92
|
+
columns={columns}
|
|
93
|
+
data={largeDataset} // e.g. 50,000 rows
|
|
94
|
+
getRowId={(r) => r.id}
|
|
95
|
+
workerSort={true}
|
|
96
|
+
statusBar
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
:::tip Switching UI libraries
|
|
103
|
+
The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
|
|
104
|
+
|
|
105
|
+
- **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
|
|
106
|
+
- **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
|
|
107
|
+
- **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
|
|
108
|
+
:::
|
|
109
|
+
|
|
110
|
+
</TabItem>
|
|
111
|
+
<TabItem value="angular" label="Angular">
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
|
|
115
|
+
interface Row {
|
|
116
|
+
id: number;
|
|
117
|
+
name: string;
|
|
118
|
+
revenue: number;
|
|
119
|
+
region: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Component({
|
|
123
|
+
standalone: true,
|
|
124
|
+
imports: [OGridComponent],
|
|
125
|
+
template: `<ogrid [props]="gridProps" />`
|
|
126
|
+
})
|
|
127
|
+
export class GridComponent {
|
|
128
|
+
gridProps = {
|
|
129
|
+
columns: [
|
|
130
|
+
{ columnId: 'id', name: 'ID', type: 'numeric' },
|
|
131
|
+
{ columnId: 'name', name: 'Name', sortable: true },
|
|
132
|
+
{ columnId: 'revenue', name: 'Revenue', type: 'numeric', sortable: true },
|
|
133
|
+
{ columnId: 'region', name: 'Region', sortable: true },
|
|
134
|
+
] as IColumnDef<Row>[],
|
|
135
|
+
data: largeDataset, // e.g. 50,000 rows
|
|
136
|
+
getRowId: (item: Row) => item.id,
|
|
137
|
+
workerSort: true,
|
|
138
|
+
statusBar: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
:::tip Switching UI libraries
|
|
144
|
+
Same component API across Angular packages. To switch, just change the import:
|
|
145
|
+
|
|
146
|
+
- **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
|
|
147
|
+
- **Angular Material**: `from '@alaarab/ogrid-angular-material'`
|
|
148
|
+
- **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
|
|
149
|
+
|
|
150
|
+
All components are standalone — no NgModule required.
|
|
151
|
+
:::
|
|
152
|
+
|
|
153
|
+
</TabItem>
|
|
154
|
+
<TabItem value="vue" label="Vue">
|
|
155
|
+
|
|
156
|
+
```vue
|
|
157
|
+
<script setup lang="ts">
|
|
158
|
+
|
|
159
|
+
interface Row {
|
|
160
|
+
id: number;
|
|
161
|
+
name: string;
|
|
162
|
+
revenue: number;
|
|
163
|
+
region: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const columns: IColumnDef<Row>[] = [
|
|
167
|
+
{ columnId: 'id', name: 'ID', type: 'numeric' },
|
|
168
|
+
{ columnId: 'name', name: 'Name', sortable: true },
|
|
169
|
+
{ columnId: 'revenue', name: 'Revenue', type: 'numeric', sortable: true },
|
|
170
|
+
{ columnId: 'region', name: 'Region', sortable: true },
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
const gridProps = {
|
|
174
|
+
columns,
|
|
175
|
+
data: largeDataset, // e.g. 50,000 rows
|
|
176
|
+
getRowId: (item: Row) => item.id,
|
|
177
|
+
workerSort: true,
|
|
178
|
+
statusBar: true,
|
|
179
|
+
};
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
<template>
|
|
183
|
+
<OGrid :gridProps="gridProps" />
|
|
184
|
+
</template>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
:::tip Switching UI libraries
|
|
188
|
+
Same component API across Vue packages. To switch, just change the import:
|
|
189
|
+
|
|
190
|
+
- **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
|
|
191
|
+
- **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
|
|
192
|
+
- **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
|
|
193
|
+
:::
|
|
194
|
+
|
|
195
|
+
</TabItem>
|
|
196
|
+
<TabItem value="js" label="Vanilla JS">
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
|
|
200
|
+
const grid = new OGrid(document.getElementById('grid'), {
|
|
201
|
+
columns: [
|
|
202
|
+
{ columnId: 'id', name: 'ID', type: 'numeric' },
|
|
203
|
+
{ columnId: 'name', name: 'Name', sortable: true },
|
|
204
|
+
{ columnId: 'revenue', name: 'Revenue', type: 'numeric', sortable: true },
|
|
205
|
+
{ columnId: 'region', name: 'Region', sortable: true },
|
|
206
|
+
],
|
|
207
|
+
data: largeDataset, // e.g. 50,000 rows
|
|
208
|
+
getRowId: (r) => r.id,
|
|
209
|
+
workerSort: true,
|
|
210
|
+
statusBar: true,
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
</TabItem>
|
|
215
|
+
</Tabs>
|
|
216
|
+
|
|
217
|
+
### `'auto'` Mode
|
|
218
|
+
|
|
219
|
+
Set `workerSort: 'auto'` to let the grid decide. The worker is used only when the dataset exceeds **5,000 rows**; smaller datasets use synchronous processing:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<OGrid
|
|
223
|
+
workerSort="auto"
|
|
224
|
+
// ...
|
|
225
|
+
/>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
This is the safest choice if you do not know the data size ahead of time.
|
|
229
|
+
|
|
230
|
+
### How It Works
|
|
231
|
+
|
|
232
|
+
1. Before sorting or filtering, the grid extracts a flat value matrix from the row data.
|
|
233
|
+
2. The matrix is transferred to an inline `Blob` Web Worker via `postMessage`.
|
|
234
|
+
3. The worker performs the sort and filter computation and returns the sorted/filtered row indices.
|
|
235
|
+
4. The grid uses those indices to re-render the visible rows — no data is copied back, only indices.
|
|
236
|
+
|
|
237
|
+
The worker is created once and reused for subsequent sort/filter operations in the same grid instance.
|
|
238
|
+
|
|
239
|
+
### Automatic Fallback
|
|
240
|
+
|
|
241
|
+
The worker is bypassed automatically in situations where it cannot run:
|
|
242
|
+
|
|
243
|
+
| Situation | Behavior |
|
|
244
|
+
|-----------|----------|
|
|
245
|
+
| Custom `compare` function on a column | Falls back to synchronous sort (custom functions cannot cross the worker boundary) |
|
|
246
|
+
| `people` filter type | Falls back to synchronous filter (requires rich object comparison) |
|
|
247
|
+
| `Worker` API unavailable (e.g. old browser or sandboxed iframe) | Falls back to synchronous processing silently |
|
|
248
|
+
|
|
249
|
+
The fallback is transparent — the grid sorts and filters correctly in all cases.
|
|
250
|
+
|
|
251
|
+
### Content Security Policy (CSP)
|
|
252
|
+
|
|
253
|
+
The worker is created from an inline `Blob` URL. If your application enforces a `Content-Security-Policy` header, you must allow `blob:` in the `worker-src` directive:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
Content-Security-Policy: worker-src 'self' blob:;
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Without this directive, the browser will block the worker and the grid will fall back to synchronous processing automatically.
|
|
260
|
+
|
|
261
|
+
:::caution
|
|
262
|
+
If your CSP does not permit `blob:` and you require worker-based processing, contact your security team about the CSP configuration before enabling `workerSort`.
|
|
263
|
+
:::
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Combining All Three Features
|
|
268
|
+
|
|
269
|
+
Row virtualization, column virtualization, and worker sort/filter are fully compatible and can be enabled together for maximum throughput on very wide, very tall datasets:
|
|
270
|
+
|
|
271
|
+
<Tabs groupId="framework">
|
|
272
|
+
<TabItem value="react" label="React" default>
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
|
|
276
|
+
function App() {
|
|
277
|
+
return (
|
|
278
|
+
<OGrid
|
|
279
|
+
columns={columns} // e.g. 100 columns
|
|
280
|
+
data={data} // e.g. 100,000 rows
|
|
281
|
+
getRowId={(r) => r.id}
|
|
282
|
+
virtualScroll={{
|
|
283
|
+
enabled: true, // row virtualization: renders ~30 rows instead of 100,000
|
|
284
|
+
rowHeight: 36,
|
|
285
|
+
overscan: 5,
|
|
286
|
+
columns: true, // column virtualization: renders only visible columns
|
|
287
|
+
columnOverscan: 2,
|
|
288
|
+
}}
|
|
289
|
+
workerSort={true} // keeps the UI thread free during sort/filter
|
|
290
|
+
statusBar
|
|
291
|
+
/>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
:::tip Switching UI libraries
|
|
297
|
+
The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
|
|
298
|
+
|
|
299
|
+
- **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
|
|
300
|
+
- **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
|
|
301
|
+
- **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
|
|
302
|
+
:::
|
|
303
|
+
|
|
304
|
+
</TabItem>
|
|
305
|
+
<TabItem value="angular" label="Angular">
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
gridProps = {
|
|
309
|
+
columns, // e.g. 100 columns
|
|
310
|
+
data: largeData, // e.g. 100,000 rows
|
|
311
|
+
getRowId: (item: Row) => item.id,
|
|
312
|
+
virtualScroll: {
|
|
313
|
+
enabled: true,
|
|
314
|
+
rowHeight: 36,
|
|
315
|
+
overscan: 5,
|
|
316
|
+
columns: true,
|
|
317
|
+
columnOverscan: 2,
|
|
318
|
+
},
|
|
319
|
+
workerSort: true,
|
|
320
|
+
statusBar: true,
|
|
321
|
+
};
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
:::tip Switching UI libraries
|
|
325
|
+
Same component API across Angular packages. To switch, just change the import:
|
|
326
|
+
|
|
327
|
+
- **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
|
|
328
|
+
- **Angular Material**: `from '@alaarab/ogrid-angular-material'`
|
|
329
|
+
- **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
|
|
330
|
+
|
|
331
|
+
All components are standalone — no NgModule required.
|
|
332
|
+
:::
|
|
333
|
+
|
|
334
|
+
</TabItem>
|
|
335
|
+
<TabItem value="vue" label="Vue">
|
|
336
|
+
|
|
337
|
+
```vue
|
|
338
|
+
<script setup lang="ts">
|
|
339
|
+
const gridProps = {
|
|
340
|
+
columns, // e.g. 100 columns
|
|
341
|
+
data: largeData, // e.g. 100,000 rows
|
|
342
|
+
getRowId: (item: Row) => item.id,
|
|
343
|
+
virtualScroll: {
|
|
344
|
+
enabled: true,
|
|
345
|
+
rowHeight: 36,
|
|
346
|
+
overscan: 5,
|
|
347
|
+
columns: true,
|
|
348
|
+
columnOverscan: 2,
|
|
349
|
+
},
|
|
350
|
+
workerSort: true,
|
|
351
|
+
statusBar: true,
|
|
352
|
+
};
|
|
353
|
+
</script>
|
|
354
|
+
|
|
355
|
+
<template>
|
|
356
|
+
<OGrid :gridProps="gridProps" />
|
|
357
|
+
</template>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
:::tip Switching UI libraries
|
|
361
|
+
Same component API across Vue packages. To switch, just change the import:
|
|
362
|
+
|
|
363
|
+
- **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
|
|
364
|
+
- **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
|
|
365
|
+
- **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
|
|
366
|
+
:::
|
|
367
|
+
|
|
368
|
+
</TabItem>
|
|
369
|
+
<TabItem value="js" label="Vanilla JS">
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
|
|
373
|
+
const grid = new OGrid(document.getElementById('grid'), {
|
|
374
|
+
columns, // e.g. 100 columns
|
|
375
|
+
data: largeData, // e.g. 100,000 rows
|
|
376
|
+
getRowId: (r) => r.id,
|
|
377
|
+
virtualScroll: {
|
|
378
|
+
enabled: true,
|
|
379
|
+
rowHeight: 36,
|
|
380
|
+
overscan: 5,
|
|
381
|
+
columns: true,
|
|
382
|
+
columnOverscan: 2,
|
|
383
|
+
},
|
|
384
|
+
workerSort: true,
|
|
385
|
+
statusBar: true,
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
</TabItem>
|
|
390
|
+
</Tabs>
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Props Reference
|
|
395
|
+
|
|
396
|
+
### `workerSort`
|
|
397
|
+
|
|
398
|
+
| Value | Behavior |
|
|
399
|
+
|-------|----------|
|
|
400
|
+
| `false` (default) | Synchronous sort/filter on the main thread |
|
|
401
|
+
| `true` | Always use Web Worker for sort and filter |
|
|
402
|
+
| `'auto'` | Use Web Worker only when `data.length > 5000` |
|
|
403
|
+
|
|
404
|
+
### `virtualScroll` (Performance Fields)
|
|
405
|
+
|
|
406
|
+
| Field | Type | Default | Description |
|
|
407
|
+
|-------|------|---------|-------------|
|
|
408
|
+
| `columns` | `boolean` | `false` | Enable column virtualization alongside row virtualization |
|
|
409
|
+
| `columnOverscan` | `number` | `2` | Extra columns rendered beyond the visible horizontal range on each side |
|
|
410
|
+
|
|
411
|
+
For the full `virtualScroll` props table, see [Virtual Scrolling — Props Reference](./virtual-scrolling#props-reference).
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## When to Use Each Feature
|
|
416
|
+
|
|
417
|
+
| Dataset Size | Recommendation |
|
|
418
|
+
|---|---|
|
|
419
|
+
| < 1,000 rows, < 20 columns | No performance features needed. CSS containment applies automatically. |
|
|
420
|
+
| 1,000 – 10,000 rows | Enable row virtualization (`virtualScroll: { enabled: true, rowHeight: 36 }`). |
|
|
421
|
+
| > 10,000 rows | Row virtualization + `workerSort: 'auto'` or `workerSort: true`. |
|
|
422
|
+
| > 30 columns with horizontal scroll | Add `columns: true` to the `virtualScroll` config. |
|
|
423
|
+
| All of the above | Combine all three: row + column virtualization and `workerSort: true`. |
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Related
|
|
428
|
+
|
|
429
|
+
- [Virtual Scrolling](./virtual-scrolling) -- row virtualization, programmatic scrolling, and the full `virtualScroll` API
|
|
430
|
+
- [Sorting](./sorting) -- configure sortable columns and custom comparators
|
|
431
|
+
- [Filtering](./filtering) -- filter types and server-side filtering
|
|
432
|
+
- [Server-Side Data](./server-side-data) -- move sort and filter to the server entirely
|
|
433
|
+
- [Column Pinning](./column-pinning) -- pinned columns always render regardless of column virtualization
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 7
|
|
3
|
+
title: Row Selection
|
|
4
|
+
description: Single and multi-row selection with checkboxes, keyboard support, and imperative API.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Row Selection
|
|
9
|
+
|
|
10
|
+
Select entire rows with checkboxes (multiple mode) or radio-button-style selection (single mode). Access selected rows programmatically via the grid API ref.
|
|
11
|
+
|
|
12
|
+
## Live Demo
|
|
13
|
+
|
|
14
|
+
<RowSelectionDemo />
|
|
15
|
+
|
|
16
|
+
:::tip Framework-Specific Styling
|
|
17
|
+
The live demo above shows **React Radix UI** styling (lightweight default). To see how row selection looks in your framework's design system, click the framework buttons above the demo to open online demo:
|
|
18
|
+
- **React** — Radix UI checkboxes
|
|
19
|
+
- **Angular** — Angular Material checkboxes
|
|
20
|
+
- **Vue** — Vuetify checkboxes
|
|
21
|
+
- **JS** — Vanilla JS default theme
|
|
22
|
+
|
|
23
|
+
Each framework renders with its native checkbox components, so the styling matches your design system.
|
|
24
|
+
:::
|
|
25
|
+
|
|
26
|
+
## Quick Example
|
|
27
|
+
|
|
28
|
+
<Tabs groupId="framework">
|
|
29
|
+
<TabItem value="react" label="React" default>
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
|
|
33
|
+
function App() {
|
|
34
|
+
const gridRef = useRef<IOGridApi<Person>>(null);
|
|
35
|
+
|
|
36
|
+
const handleExport = () => {
|
|
37
|
+
const selectedIds = gridRef.current?.getSelectedRows() ?? [];
|
|
38
|
+
console.log('Selected:', selectedIds);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
<button onClick={handleExport}>Export Selected</button>
|
|
44
|
+
<OGrid
|
|
45
|
+
ref={gridRef}
|
|
46
|
+
columns={columns}
|
|
47
|
+
data={people}
|
|
48
|
+
getRowId={(item) => item.id}
|
|
49
|
+
rowSelection="multiple"
|
|
50
|
+
onSelectionChange={(event) => {
|
|
51
|
+
console.log(`${event.selectedItems.length} rows selected`);
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
:::tip Switching UI libraries
|
|
60
|
+
The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
|
|
61
|
+
|
|
62
|
+
- **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
|
|
63
|
+
- **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
|
|
64
|
+
- **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
|
|
65
|
+
:::
|
|
66
|
+
|
|
67
|
+
</TabItem>
|
|
68
|
+
<TabItem value="angular" label="Angular">
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
|
|
72
|
+
@Component({
|
|
73
|
+
standalone: true,
|
|
74
|
+
imports: [OGridComponent],
|
|
75
|
+
template: `
|
|
76
|
+
<button (click)="handleExport()">Export Selected</button>
|
|
77
|
+
<ogrid [props]="gridProps" />
|
|
78
|
+
`
|
|
79
|
+
})
|
|
80
|
+
export class GridComponent {
|
|
81
|
+
constructor(private gridService: OGridService) {}
|
|
82
|
+
|
|
83
|
+
handleExport() {
|
|
84
|
+
const selectedIds = this.gridService.getSelectedRows();
|
|
85
|
+
console.log('Selected:', selectedIds);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
gridProps = {
|
|
89
|
+
columns,
|
|
90
|
+
data: people,
|
|
91
|
+
getRowId: (item: Person) => item.id,
|
|
92
|
+
rowSelection: 'multiple' as const,
|
|
93
|
+
onSelectionChange: (event: any) => {
|
|
94
|
+
console.log(`${event.selectedItems.length} rows selected`);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
:::tip Switching UI libraries
|
|
101
|
+
Same component API across Angular packages. To switch, just change the import:
|
|
102
|
+
|
|
103
|
+
- **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
|
|
104
|
+
- **Angular Material**: `from '@alaarab/ogrid-angular-material'`
|
|
105
|
+
- **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
|
|
106
|
+
|
|
107
|
+
All components are standalone — no NgModule required.
|
|
108
|
+
:::
|
|
109
|
+
|
|
110
|
+
</TabItem>
|
|
111
|
+
<TabItem value="vue" label="Vue">
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<script setup lang="ts">
|
|
115
|
+
|
|
116
|
+
const gridProps = {
|
|
117
|
+
columns,
|
|
118
|
+
data: people,
|
|
119
|
+
getRowId: (item) => item.id,
|
|
120
|
+
rowSelection: 'multiple',
|
|
121
|
+
onSelectionChange: (event) => {
|
|
122
|
+
console.log(`${event.selectedItems.length} rows selected`);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<OGrid :gridProps="gridProps" />
|
|
129
|
+
</template>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
:::tip Switching UI libraries
|
|
133
|
+
Same component API across Vue packages. To switch, just change the import:
|
|
134
|
+
|
|
135
|
+
- **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
|
|
136
|
+
- **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
|
|
137
|
+
- **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
|
|
138
|
+
:::
|
|
139
|
+
|
|
140
|
+
</TabItem>
|
|
141
|
+
<TabItem value="js" label="Vanilla JS">
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
|
|
145
|
+
const grid = new OGrid(document.getElementById('grid'), {
|
|
146
|
+
columns: columns,
|
|
147
|
+
data: people,
|
|
148
|
+
getRowId: (item) => item.id,
|
|
149
|
+
rowSelection: 'multiple',
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
grid.on('selectionChange', ({ selectedRows }) => {
|
|
153
|
+
console.log(`${selectedRows.length} rows selected`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Programmatic access
|
|
157
|
+
document.getElementById('export-btn').addEventListener('click', () => {
|
|
158
|
+
const selected = grid.api.getSelectedRows();
|
|
159
|
+
console.log('Selected:', selected);
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
</TabItem>
|
|
164
|
+
</Tabs>
|
|
165
|
+
|
|
166
|
+
## How It Works
|
|
167
|
+
|
|
168
|
+
Set `rowSelection` on the `OGrid` component to enable row selection:
|
|
169
|
+
|
|
170
|
+
| Mode | Behavior |
|
|
171
|
+
|------|----------|
|
|
172
|
+
| `'none'` | No row selection (default). |
|
|
173
|
+
| `'single'` | One row at a time. Clicking a row selects it and deselects the previous. |
|
|
174
|
+
| `'multiple'` | Checkbox column appears. Click checkboxes to toggle. Shift+click for range select. Header checkbox toggles all. |
|
|
175
|
+
|
|
176
|
+
:::info Row Selection vs Cell Selection
|
|
177
|
+
**Row selection** (this page) selects entire rows using checkboxes. The header checkbox selects or deselects **all rows** at once.
|
|
178
|
+
|
|
179
|
+
This is separate from **cell selection** ([Spreadsheet Selection](./spreadsheet-selection)), which selects individual cells using click/drag. Cell selection's **Ctrl+A** shortcut selects all **cells** in the grid, not rows.
|
|
180
|
+
|
|
181
|
+
You can use both features together -- row selection and cell selection are independent.
|
|
182
|
+
:::
|
|
183
|
+
|
|
184
|
+
### Multiple Selection
|
|
185
|
+
|
|
186
|
+
In `'multiple'` mode, a checkbox column is prepended to the grid:
|
|
187
|
+
|
|
188
|
+
- Click a checkbox to toggle that row.
|
|
189
|
+
- Click the header checkbox to select or deselect all visible rows.
|
|
190
|
+
- Hold **Shift** and click a checkbox to select a range of rows from the last clicked row.
|
|
191
|
+
|
|
192
|
+
### Controlled Mode
|
|
193
|
+
|
|
194
|
+
For full control, pass `selectedRows` and `onSelectionChange`:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
function App() {
|
|
198
|
+
const [selectedRows, setSelectedRows] = useState<Set<RowId>>(new Set());
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<OGrid
|
|
202
|
+
columns={columns}
|
|
203
|
+
data={people}
|
|
204
|
+
getRowId={(item) => item.id}
|
|
205
|
+
rowSelection="multiple"
|
|
206
|
+
selectedRows={selectedRows}
|
|
207
|
+
onSelectionChange={(event) => {
|
|
208
|
+
setSelectedRows(new Set(event.selectedRowIds));
|
|
209
|
+
}}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Imperative API
|
|
216
|
+
|
|
217
|
+
Use the grid ref to programmatically control selection:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
const gridRef = useRef<IOGridApi<Person>>(null);
|
|
221
|
+
|
|
222
|
+
// Get selected row IDs
|
|
223
|
+
gridRef.current?.getSelectedRows();
|
|
224
|
+
|
|
225
|
+
// Set selection programmatically
|
|
226
|
+
gridRef.current?.setSelectedRows([1, 2, 3]);
|
|
227
|
+
|
|
228
|
+
// Select all rows (row selection, not cells)
|
|
229
|
+
gridRef.current?.selectAll();
|
|
230
|
+
|
|
231
|
+
// Deselect all rows
|
|
232
|
+
gridRef.current?.deselectAll();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Props
|
|
236
|
+
|
|
237
|
+
| Prop | Type | Default | Description |
|
|
238
|
+
|------|------|---------|-------------|
|
|
239
|
+
| `rowSelection` | `'none' \| 'single' \| 'multiple'` | `'none'` | Row selection mode. |
|
|
240
|
+
| `selectedRows` | `Set<RowId>` | -- | Controlled set of selected row IDs. |
|
|
241
|
+
| `onSelectionChange` | `(event: IRowSelectionChangeEvent<T>) => void` | -- | Called when selection changes. Event contains `selectedRowIds` and `selectedItems`. |
|
|
242
|
+
|
|
243
|
+
## API Methods
|
|
244
|
+
|
|
245
|
+
| Method | Signature | Description |
|
|
246
|
+
|--------|-----------|-------------|
|
|
247
|
+
| `getSelectedRows` | `() => RowId[]` | Returns currently selected row IDs. |
|
|
248
|
+
| `setSelectedRows` | `(rowIds: RowId[]) => void` | Sets selected rows programmatically. |
|
|
249
|
+
| `selectAll` | `() => void` | Selects all rows. |
|
|
250
|
+
| `deselectAll` | `() => void` | Deselects all rows. |
|
|
251
|
+
|
|
252
|
+
## Related
|
|
253
|
+
|
|
254
|
+
- [Spreadsheet Selection](./spreadsheet-selection) -- cell-level selection
|
|
255
|
+
- [Sorting](./sorting) -- sort rows before selection
|
|
256
|
+
- [Filtering](./filtering) -- filter rows before selection
|