@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.
Files changed (52) hide show
  1. package/README.md +68 -0
  2. package/bundled-docs/api/README.md +94 -0
  3. package/bundled-docs/api/column-def.mdx +379 -0
  4. package/bundled-docs/api/components-column-chooser.mdx +310 -0
  5. package/bundled-docs/api/components-column-header-filter.mdx +363 -0
  6. package/bundled-docs/api/components-datagrid-table.mdx +316 -0
  7. package/bundled-docs/api/components-pagination-controls.mdx +344 -0
  8. package/bundled-docs/api/components-sidebar.mdx +427 -0
  9. package/bundled-docs/api/components-status-bar.mdx +309 -0
  10. package/bundled-docs/api/grid-api.mdx +299 -0
  11. package/bundled-docs/api/js-api.mdx +198 -0
  12. package/bundled-docs/api/ogrid-props.mdx +244 -0
  13. package/bundled-docs/api/types.mdx +640 -0
  14. package/bundled-docs/features/cell-references.mdx +225 -0
  15. package/bundled-docs/features/column-chooser.mdx +279 -0
  16. package/bundled-docs/features/column-groups.mdx +290 -0
  17. package/bundled-docs/features/column-pinning.mdx +282 -0
  18. package/bundled-docs/features/column-reordering.mdx +359 -0
  19. package/bundled-docs/features/column-types.mdx +181 -0
  20. package/bundled-docs/features/context-menu.mdx +216 -0
  21. package/bundled-docs/features/csv-export.mdx +227 -0
  22. package/bundled-docs/features/editing.mdx +377 -0
  23. package/bundled-docs/features/filtering.mdx +330 -0
  24. package/bundled-docs/features/formulas.mdx +381 -0
  25. package/bundled-docs/features/grid-api.mdx +311 -0
  26. package/bundled-docs/features/keyboard-navigation.mdx +236 -0
  27. package/bundled-docs/features/pagination.mdx +245 -0
  28. package/bundled-docs/features/performance.mdx +433 -0
  29. package/bundled-docs/features/row-selection.mdx +256 -0
  30. package/bundled-docs/features/server-side-data.mdx +291 -0
  31. package/bundled-docs/features/sidebar.mdx +234 -0
  32. package/bundled-docs/features/sorting.mdx +241 -0
  33. package/bundled-docs/features/spreadsheet-selection.mdx +201 -0
  34. package/bundled-docs/features/status-bar.mdx +205 -0
  35. package/bundled-docs/features/toolbar.mdx +284 -0
  36. package/bundled-docs/features/virtual-scrolling.mdx +624 -0
  37. package/bundled-docs/getting-started/installation.mdx +216 -0
  38. package/bundled-docs/getting-started/overview.mdx +151 -0
  39. package/bundled-docs/getting-started/quick-start.mdx +425 -0
  40. package/bundled-docs/getting-started/vanilla-js.mdx +191 -0
  41. package/bundled-docs/guides/accessibility.mdx +550 -0
  42. package/bundled-docs/guides/controlled-vs-uncontrolled.mdx +153 -0
  43. package/bundled-docs/guides/custom-cell-editors.mdx +201 -0
  44. package/bundled-docs/guides/framework-showcase.mdx +200 -0
  45. package/bundled-docs/guides/mcp-live-testing.mdx +291 -0
  46. package/bundled-docs/guides/mcp.mdx +172 -0
  47. package/bundled-docs/guides/migration-from-ag-grid.mdx +223 -0
  48. package/bundled-docs/guides/theming.mdx +211 -0
  49. package/dist/esm/bridge-client.d.ts +87 -0
  50. package/dist/esm/bridge-client.js +162 -0
  51. package/dist/esm/index.js +1060 -0
  52. package/package.json +43 -0
@@ -0,0 +1,640 @@
1
+ ---
2
+ sidebar_position: 5
3
+ title: Types
4
+ ---
5
+
6
+ # Types
7
+
8
+ Reference for all shared types exported from `@alaarab/ogrid-core`. These types are also re-exported from all UI packages (`@alaarab/ogrid-react-radix`, `@alaarab/ogrid-react-fluent`, `@alaarab/ogrid-react-material`).
9
+
10
+ ## RowId
11
+
12
+ Unique identifier for a row. Used throughout the grid for row selection, tracking, and the `getRowId` callback.
13
+
14
+ ```typescript
15
+ type RowId = string | number;
16
+ ```
17
+
18
+ ## FilterValue
19
+
20
+ A discriminated union representing a single filter value. Each variant has a `type` tag and a `value` field.
21
+
22
+ ```typescript
23
+ type FilterValue =
24
+ | { type: 'text'; value: string }
25
+ | { type: 'multiSelect'; value: string[] }
26
+ | { type: 'people'; value: UserLike }
27
+ | { type: 'date'; value: IDateFilterValue };
28
+ ```
29
+
30
+ | Filter Type | `type` Tag | `value` Shape | Example |
31
+ |------------|------------|---------------|---------|
32
+ | Text | `'text'` | `string` | `{ type: 'text', value: 'Alice' }` |
33
+ | Multi-select | `'multiSelect'` | `string[]` | `{ type: 'multiSelect', value: ['Active', 'Pending'] }` |
34
+ | People | `'people'` | `UserLike` | `{ type: 'people', value: { displayName: 'Alice', email: 'alice@example.com' } }` |
35
+ | Date range | `'date'` | `IDateFilterValue` | `{ type: 'date', value: { from: '2024-01-01', to: '2024-12-31' } }` |
36
+
37
+ ## IDateFilterValue
38
+
39
+ A date range for date column filters. Both fields are optional ISO `YYYY-MM-DD` strings.
40
+
41
+ ```typescript
42
+ interface IDateFilterValue {
43
+ from?: string;
44
+ to?: string;
45
+ }
46
+ ```
47
+
48
+ | Name | Type | Description |
49
+ |------|------|-------------|
50
+ | `from` | `string` | Start date (inclusive). ISO `YYYY-MM-DD` format. |
51
+ | `to` | `string` | End date (inclusive). ISO `YYYY-MM-DD` format. |
52
+
53
+ ## IFilters
54
+
55
+ A record of active filters, keyed by field name. Each value is a `FilterValue` or `undefined` (when a filter has been cleared).
56
+
57
+ ```typescript
58
+ interface IFilters {
59
+ [field: string]: FilterValue | undefined;
60
+ }
61
+ ```
62
+
63
+ ### Example
64
+
65
+ ```typescript
66
+ const filters: IFilters = {
67
+ name: { type: 'text', value: 'Alice' },
68
+ status: { type: 'multiSelect', value: ['Active', 'Pending'] },
69
+ owner: { type: 'people', value: { displayName: 'Bob', email: 'bob@example.com' } },
70
+ startDate: { type: 'date', value: { from: '2024-01-01' } },
71
+ department: undefined, // cleared filter
72
+ };
73
+ ```
74
+
75
+ ## RowSelectionMode
76
+
77
+ Controls how row selection behaves in the grid.
78
+
79
+ ```typescript
80
+ type RowSelectionMode = 'none' | 'single' | 'multiple';
81
+ ```
82
+
83
+ | Value | Description |
84
+ |-------|-------------|
85
+ | `'none'` | Row selection is disabled. No checkbox column is shown. |
86
+ | `'single'` | Only one row can be selected at a time. Clicking a row replaces the previous selection. |
87
+ | `'multiple'` | Multiple rows can be selected. A header checkbox toggles select/deselect all. Shift+click selects a range. |
88
+
89
+ ## ISelectionRange
90
+
91
+ Describes a rectangular range of cells selected in the grid. Used internally by cell selection and passed to aggregation utilities.
92
+
93
+ ```typescript
94
+ interface ISelectionRange {
95
+ startRow: number;
96
+ startCol: number;
97
+ endRow: number;
98
+ endCol: number;
99
+ }
100
+ ```
101
+
102
+ | Name | Type | Description |
103
+ |------|------|-------------|
104
+ | `startRow` | `number` | Row index where the selection began. |
105
+ | `startCol` | `number` | Column index where the selection began. |
106
+ | `endRow` | `number` | Row index where the selection ended. |
107
+ | `endCol` | `number` | Column index where the selection ended. |
108
+
109
+ The range is inclusive. `startRow` may be greater than `endRow` (and likewise for columns) when the user selects from bottom-right to top-left; the grid normalizes the range internally.
110
+
111
+ ## IActiveCell
112
+
113
+ Identifies the currently active (focused) cell in the grid.
114
+
115
+ ```typescript
116
+ interface IActiveCell {
117
+ rowIndex: number;
118
+ columnIndex: number;
119
+ }
120
+ ```
121
+
122
+ | Name | Type | Description |
123
+ |------|------|-------------|
124
+ | `rowIndex` | `number` | Zero-based row index of the active cell. |
125
+ | `columnIndex` | `number` | Zero-based column index of the active cell. |
126
+
127
+ ## UserLike
128
+
129
+ Represents a person/user. Used by `people` type column filters and the `searchPeople` / `getUserByEmail` methods on `IDataSource`.
130
+
131
+ ```typescript
132
+ interface UserLike {
133
+ id?: string;
134
+ displayName: string;
135
+ email: string;
136
+ photo?: string;
137
+ }
138
+ ```
139
+
140
+ | Name | Type | Required | Description |
141
+ |------|------|----------|-------------|
142
+ | `id` | `string` | No | Unique identifier for the user. |
143
+ | `displayName` | `string` | Yes | The user's display name, shown in filter chips and people cells. |
144
+ | `email` | `string` | Yes | The user's email address. Used as the filter value and for user resolution. |
145
+ | `photo` | `string` | No | URL to the user's profile photo. Displayed as an avatar in people filters. |
146
+
147
+ ## IStatusBarProps
148
+
149
+ Configuration for the grid status bar, which displays counts and aggregation information at the bottom of the grid.
150
+
151
+ ```typescript
152
+ interface IStatusBarProps {
153
+ totalCount: number;
154
+ filteredCount?: number;
155
+ selectedCount?: number;
156
+ panels?: StatusBarPanel[];
157
+ aggregation?: {
158
+ sum: number;
159
+ avg: number;
160
+ min: number;
161
+ max: number;
162
+ count: number;
163
+ } | null;
164
+ }
165
+ ```
166
+
167
+ | Name | Type | Default | Description |
168
+ |------|------|---------|-------------|
169
+ | `totalCount` | `number` | **Required** | Total number of items in the data set. |
170
+ | `filteredCount` | `number` | `undefined` | Number of items after filtering. When different from `totalCount`, the status bar shows "X of Y items". |
171
+ | `selectedCount` | `number` | `undefined` | Number of selected rows. Shown when row selection is enabled. |
172
+ | `panels` | `StatusBarPanel[]` | `undefined` | Custom status bar panels for additional content. |
173
+ | `aggregation` | `{ sum, avg, min, max, count } \| null` | `undefined` | Aggregation values computed from the selected cell range. Displayed when the user selects a range containing numeric cells. |
174
+
175
+ ### Aggregation Object
176
+
177
+ When cells are selected and contain numeric values, the status bar can display aggregation values.
178
+
179
+ | Name | Type | Description |
180
+ |------|------|-------------|
181
+ | `sum` | `number` | Sum of all numeric values in the selection. |
182
+ | `avg` | `number` | Average of all numeric values in the selection. |
183
+ | `min` | `number` | Minimum numeric value in the selection. |
184
+ | `max` | `number` | Maximum numeric value in the selection. |
185
+ | `count` | `number` | Count of numeric values in the selection. |
186
+
187
+ ### Usage
188
+
189
+ Pass `true` for a default status bar, or an object for full control:
190
+
191
+ ```typescript
192
+ // Default status bar (shows item counts automatically)
193
+ <OGrid statusBar={true} ... />
194
+
195
+ // Custom status bar with aggregation
196
+ <OGrid
197
+ statusBar={{
198
+ totalCount: data.length,
199
+ filteredCount: filteredData.length,
200
+ selectedCount: selectedRows.size,
201
+ aggregation: { sum: 1500, avg: 375, min: 100, max: 600, count: 4 },
202
+ }}
203
+ ...
204
+ />
205
+ ```
206
+
207
+ ## ISideBarDef
208
+
209
+ Configuration for the grid side bar panel.
210
+
211
+ ```typescript
212
+ interface ISideBarDef {
213
+ panels?: ('columns' | 'filters')[];
214
+ defaultPanel?: 'columns' | 'filters';
215
+ position?: 'left' | 'right';
216
+ }
217
+ ```
218
+
219
+ | Name | Type | Default | Description |
220
+ |------|------|---------|-------------|
221
+ | `panels` | `('columns' \| 'filters')[]` | `['columns', 'filters']` | Which panels to include in the side bar. `'columns'` shows a column chooser. `'filters'` shows filter controls. |
222
+ | `defaultPanel` | `'columns' \| 'filters'` | `undefined` | Which panel is open by default. When not set, the side bar starts collapsed. |
223
+ | `position` | `'left' \| 'right'` | `'right'` | Which side of the grid the side bar appears on. |
224
+
225
+ ### Usage
226
+
227
+ ```typescript
228
+ // Default side bar
229
+ <OGrid sideBar={true} ... />
230
+
231
+ // Custom side bar on the left, showing only column chooser
232
+ <OGrid
233
+ sideBar={{
234
+ panels: ['columns'],
235
+ position: 'left',
236
+ defaultPanel: 'columns',
237
+ }}
238
+ ...
239
+ />
240
+ ```
241
+
242
+ ## IRowSelectionChangeEvent&lt;T&gt;
243
+
244
+ Event object passed to the `onSelectionChange` callback when row selection changes.
245
+
246
+ ```typescript
247
+ interface IRowSelectionChangeEvent<T> {
248
+ selectedRowIds: RowId[];
249
+ selectedItems: T[];
250
+ }
251
+ ```
252
+
253
+ | Name | Type | Description |
254
+ |------|------|-------------|
255
+ | `selectedRowIds` | `RowId[]` | Array of IDs for all currently selected rows. |
256
+ | `selectedItems` | `T[]` | Array of full row data objects for all currently selected rows. |
257
+
258
+ ### Usage
259
+
260
+ ```typescript
261
+ <OGrid<Employee>
262
+ columns={columns}
263
+ data={data}
264
+ getRowId={(e) => e.id}
265
+ rowSelection="multiple"
266
+ onSelectionChange={(event) => {
267
+ console.log('Selected IDs:', event.selectedRowIds);
268
+ console.log('Selected items:', event.selectedItems);
269
+ }}
270
+ />
271
+ ```
272
+
273
+ ## IDataSource&lt;T&gt;
274
+
275
+ Server-side data source interface. When you pass a `dataSource` to `OGrid` instead of a `data` array, the grid calls these methods to fetch paginated data and filter options.
276
+
277
+ ```typescript
278
+ interface IDataSource<T> {
279
+ fetchPage(params: IFetchParams): Promise<IPageResult<T>>;
280
+ fetchFilterOptions?(field: string): Promise<string[]>;
281
+ searchPeople?(query: string): Promise<UserLike[]>;
282
+ getUserByEmail?(email: string): Promise<UserLike | undefined>;
283
+ }
284
+ ```
285
+
286
+ | Method | Required | Description |
287
+ |--------|----------|-------------|
288
+ | `fetchPage` | Yes | Fetch a page of data from the server. Called when the user changes page, page size, sort, or filters. |
289
+ | `fetchFilterOptions` | No | Fetch filter options for a multi-select filter column. Called when a multi-select filter is opened. |
290
+ | `searchPeople` | No | Search for people by name or email. Called when a user types in a people filter search box. |
291
+ | `getUserByEmail` | No | Resolve a user by email address. Called when restoring a saved people filter. |
292
+
293
+ ### IFetchParams
294
+
295
+ Parameters passed to `fetchPage`:
296
+
297
+ ```typescript
298
+ interface IFetchParams {
299
+ page: number;
300
+ pageSize: number;
301
+ sort?: { field: string; direction: 'asc' | 'desc' };
302
+ filters: IFilters;
303
+ }
304
+ ```
305
+
306
+ | Field | Type | Description |
307
+ |-------|------|-------------|
308
+ | `page` | `number` | Current page number (1-indexed). |
309
+ | `pageSize` | `number` | Number of items per page. |
310
+ | `sort` | `{ field, direction }` | Active sort (field name + direction). Omit if no sort. |
311
+ | `filters` | `IFilters` | Active filters (field → FilterValue). |
312
+
313
+ ### IPageResult&lt;T&gt;
314
+
315
+ Response shape from `fetchPage`:
316
+
317
+ ```typescript
318
+ interface IPageResult<T> {
319
+ items: T[];
320
+ totalCount: number;
321
+ }
322
+ ```
323
+
324
+ | Field | Type | Description |
325
+ |-------|------|-------------|
326
+ | `items` | `T[]` | Array of data items for the current page. |
327
+ | `totalCount` | `number` | Total number of items across all pages (used for pagination). |
328
+
329
+ ### Usage Example
330
+
331
+ ```typescript
332
+
333
+ interface Product {
334
+ id: number;
335
+ name: string;
336
+ category: string;
337
+ price: number;
338
+ }
339
+
340
+ const dataSource: IDataSource<Product> = {
341
+ async fetchPage(params: IFetchParams): Promise<IPageResult<Product>> {
342
+ // Build query string from params
343
+ const queryParams = new URLSearchParams({
344
+ page: params.page.toString(),
345
+ pageSize: params.pageSize.toString(),
346
+ sort: params.sort ? `${params.sort.field}:${params.sort.direction}` : '',
347
+ filters: JSON.stringify(params.filters),
348
+ });
349
+
350
+ const response = await fetch(`/api/products?${queryParams}`);
351
+ const data = await response.json();
352
+
353
+ return {
354
+ items: data.items,
355
+ totalCount: data.totalCount,
356
+ };
357
+ },
358
+
359
+ async fetchFilterOptions(field: string): Promise<string[]> {
360
+ const response = await fetch(`/api/products/filter-options/${field}`);
361
+ return response.json();
362
+ },
363
+
364
+ async searchPeople(query: string): Promise<UserLike[]> {
365
+ const response = await fetch(`/api/users/search?q=${encodeURIComponent(query)}`);
366
+ return response.json();
367
+ },
368
+
369
+ async getUserByEmail(email: string): Promise<UserLike | undefined> {
370
+ const response = await fetch(`/api/users/by-email/${encodeURIComponent(email)}`);
371
+ if (!response.ok) return undefined;
372
+ return response.json();
373
+ },
374
+ };
375
+
376
+ // Use with OGrid
377
+ <OGrid
378
+ dataSource={dataSource}
379
+ columns={columns}
380
+ getRowId={(item) => item.id}
381
+ />
382
+ ```
383
+
384
+ ## ICellEditorProps&lt;T&gt;
385
+
386
+ Props passed to custom cell editor components (React) or rendered by the inline cell editor.
387
+
388
+ ```typescript
389
+ interface ICellEditorProps<T> {
390
+ value: unknown;
391
+ onValueChange: (value: unknown) => void;
392
+ onCommit: () => void;
393
+ onCancel: () => void;
394
+ item: T;
395
+ column: IColumnDef<T>;
396
+ cellEditorParams?: CellEditorParams;
397
+ }
398
+ ```
399
+
400
+ | Field | Type | Description |
401
+ |-------|------|-------------|
402
+ | `value` | `unknown` | Current cell value. |
403
+ | `onValueChange` | `(value: unknown) => void` | Callback to update the value (doesn't commit yet). |
404
+ | `onCommit` | `() => void` | Callback to commit the edit and close the editor. |
405
+ | `onCancel` | `() => void` | Callback to cancel the edit and close the editor. |
406
+ | `item` | `T` | Full row data object. |
407
+ | `column` | `IColumnDef<T>` | Column definition. |
408
+ | `cellEditorParams` | `CellEditorParams` | Optional params passed to the editor (e.g., `values` for a select editor). |
409
+
410
+ ### CellEditorParams
411
+
412
+ Parameters for built-in cell editors:
413
+
414
+ ```typescript
415
+ interface CellEditorParams {
416
+ values?: unknown[];
417
+ formatValue?: (value: unknown) => string;
418
+ }
419
+ ```
420
+
421
+ | Field | Type | Description |
422
+ |-------|------|-------------|
423
+ | `values` | `unknown[]` | Array of allowed values for select/richSelect editors. |
424
+ | `formatValue` | `(value: unknown) => string` | Custom formatter for values in rich select editor. |
425
+
426
+ ### Usage Example (React)
427
+
428
+ ```typescript
429
+
430
+ interface Product {
431
+ id: number;
432
+ name: string;
433
+ category: string;
434
+ }
435
+
436
+ function CategoryEditor(props: ICellEditorProps<Product>) {
437
+ const { value, onValueChange, onCommit, onCancel, cellEditorParams } = props;
438
+
439
+ const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
440
+ onValueChange(e.target.value);
441
+ };
442
+
443
+ const handleKeyDown = (e: React.KeyboardEvent) => {
444
+ if (e.key === 'Enter') onCommit();
445
+ if (e.key === 'Escape') onCancel();
446
+ };
447
+
448
+ return (
449
+ <select
450
+ value={value as string}
451
+ onChange={handleChange}
452
+ onKeyDown={handleKeyDown}
453
+ autoFocus
454
+ >
455
+ {cellEditorParams?.values?.map((v) => (
456
+ <option key={v as string} value={v as string}>
457
+ {v as string}
458
+ </option>
459
+ ))}
460
+ </select>
461
+ );
462
+ }
463
+
464
+ // Use in column definition
465
+ const columns: IColumnDef<Product>[] = [
466
+ {
467
+ columnId: 'category',
468
+ name: 'Category',
469
+ editable: true,
470
+ cellEditor: CategoryEditor,
471
+ cellEditorParams: {
472
+ values: ['Electronics', 'Clothing', 'Books'],
473
+ },
474
+ },
475
+ ];
476
+ ```
477
+
478
+ ## ICellValueChangedEvent&lt;T&gt;
479
+
480
+ Event object passed to the `onCellValueChanged` callback when a cell value is committed after editing.
481
+
482
+ ```typescript
483
+ interface ICellValueChangedEvent<T> {
484
+ item: T;
485
+ columnId: string;
486
+ oldValue: unknown;
487
+ newValue: unknown;
488
+ rowIndex: number;
489
+ }
490
+ ```
491
+
492
+ | Field | Type | Description |
493
+ |-------|------|-------------|
494
+ | `item` | `T` | Full row data object (with the new value already applied). |
495
+ | `columnId` | `string` | ID of the column that was edited. |
496
+ | `oldValue` | `unknown` | Value before the edit. |
497
+ | `newValue` | `unknown` | Value after the edit. |
498
+ | `rowIndex` | `number` | Zero-based row index. |
499
+
500
+ ### Usage Example
501
+
502
+ ```typescript
503
+ <OGrid<Product>
504
+ columns={columns}
505
+ data={data}
506
+ getRowId={(item) => item.id}
507
+ editable={true}
508
+ onCellValueChanged={(event) => {
509
+ console.log(`Cell edited: ${event.columnId}`);
510
+ console.log(`Old value: ${event.oldValue}`);
511
+ console.log(`New value: ${event.newValue}`);
512
+ console.log('Updated item:', event.item);
513
+
514
+ // Sync changes to the server
515
+ updateProduct(event.item);
516
+ }}
517
+ />
518
+ ```
519
+
520
+ ## IGridColumnState
521
+
522
+ Column state object returned by `IOGridApi.getColumnState()` and accepted by `applyColumnState()`. All fields are JSON-serializable for persistence (e.g., localStorage).
523
+
524
+ ```typescript
525
+ interface IGridColumnState {
526
+ visibleColumns: string[];
527
+ sort?: { field: string; direction: 'asc' | 'desc' };
528
+ columnOrder?: string[];
529
+ columnWidths?: Record<string, number>;
530
+ filters?: IFilters;
531
+ pinnedColumns?: Record<string, 'left' | 'right'>;
532
+ }
533
+ ```
534
+
535
+ | Field | Type | Description |
536
+ |-------|------|-------------|
537
+ | `visibleColumns` | `string[]` | Array of visible column IDs. |
538
+ | `sort` | `{ field, direction }` | Active sort (field name + direction). |
539
+ | `columnOrder` | `string[]` | Column display order (array of column IDs). |
540
+ | `columnWidths` | `Record<string, number>` | Column widths in pixels (column ID → width). |
541
+ | `filters` | `IFilters` | Active filters (field → FilterValue). |
542
+ | `pinnedColumns` | `Record<string, 'left' \| 'right'>` | Pinned columns (column ID → position). |
543
+
544
+ ### Usage Example
545
+
546
+ ```typescript
547
+
548
+ function MyGrid() {
549
+ const apiRef = useRef<IOGridApi<Product>>(null);
550
+
551
+ const saveState = () => {
552
+ const state = apiRef.current?.getColumnState();
553
+ if (state) {
554
+ localStorage.setItem('gridState', JSON.stringify(state));
555
+ }
556
+ };
557
+
558
+ const restoreState = () => {
559
+ const saved = localStorage.getItem('gridState');
560
+ if (saved) {
561
+ const state = JSON.parse(saved);
562
+ apiRef.current?.applyColumnState(state);
563
+ }
564
+ };
565
+
566
+ return (
567
+ <>
568
+ <button onClick={saveState}>Save Grid State</button>
569
+ <button onClick={restoreState}>Restore Grid State</button>
570
+ <OGrid ref={apiRef} {...props} />
571
+ </>
572
+ );
573
+ }
574
+ ```
575
+
576
+ ## IVirtualScrollConfig
577
+
578
+ Configuration for virtual scrolling (rendering only visible rows for large datasets).
579
+
580
+ ```typescript
581
+ interface IVirtualScrollConfig {
582
+ enabled?: boolean;
583
+ rowHeight?: number;
584
+ overscan?: number;
585
+ }
586
+ ```
587
+
588
+ | Field | Type | Default | Description |
589
+ |-------|------|---------|-------------|
590
+ | `enabled` | `boolean` | `false` | Enable virtual scrolling. Must be `true` to activate. |
591
+ | `rowHeight` | `number` | `36` | Fixed row height in pixels. All rows must have the same height. Required when enabled. |
592
+ | `overscan` | `number` | `5` | Number of extra rows to render above/below the visible area (for smoother scrolling). |
593
+
594
+ ### Usage Example
595
+
596
+ ```typescript
597
+ <OGrid
598
+ data={largeDataset} // 10,000+ rows
599
+ columns={columns}
600
+ getRowId={(item) => item.id}
601
+ virtualScroll={{
602
+ enabled: true, // required to activate virtual scrolling
603
+ rowHeight: 40,
604
+ overscan: 10,
605
+ }}
606
+ />
607
+ ```
608
+
609
+ ## Type Imports
610
+
611
+ All types can be imported from any OGrid package:
612
+
613
+ ```typescript
614
+ // From the core package (useful in shared code)
615
+ RowId,
616
+ FilterValue,
617
+ IFilters,
618
+ RowSelectionMode,
619
+ ISelectionRange,
620
+ IActiveCell,
621
+ UserLike,
622
+ IStatusBarProps,
623
+ ISideBarDef,
624
+ IRowSelectionChangeEvent,
625
+ IColumnDef,
626
+ IColumnGroupDef,
627
+ IDataSource,
628
+ IOGridApi,
629
+ IOGridProps,
630
+ ICellEditorProps,
631
+ ICellValueChangedEvent,
632
+ IGridColumnState,
633
+ IVirtualScrollConfig,
634
+ IFetchParams,
635
+ IPageResult,
636
+ CellEditorParams,
637
+ } from '@alaarab/ogrid-react-radix';
638
+
639
+ // From UI packages (re-exports everything from core)
640
+ ```