@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,381 @@
1
+ ---
2
+ sidebar_position: 5
3
+ title: Formulas
4
+ description: Excel-like formula support with 93 built-in functions, live recalculation, and dependency tracking
5
+ ---
6
+
7
+
8
+ # Formulas
9
+
10
+ OGrid includes a lightweight, custom-built formula engine with 93 built-in functions, automatic dependency tracking, and live recalculation. Type `=SUM(A1:A5)` into any editable cell and watch it evaluate instantly.
11
+
12
+ The engine is **tree-shakeable** — no formula code is loaded unless you enable it with the `formulas` prop. It is MIT-licensed with zero external dependencies.
13
+
14
+ ## Live Demo
15
+
16
+ <FormulasDemo />
17
+
18
+ :::tip Try it in your framework
19
+ The demo above uses Radix UI for styling. To see this feature with your framework's design system (Fluent UI, Material UI, Vuetify, PrimeNG, etc.), click **"Open in online demo"** below the demo.
20
+ :::
21
+
22
+ ## Quick Example
23
+
24
+ <Tabs groupId="framework">
25
+ <TabItem value="react" label="React" default>
26
+
27
+ ```tsx
28
+
29
+ const columns: IColumnDef<Row>[] = [
30
+ { columnId: 'a', name: 'A', type: 'numeric', editable: true },
31
+ { columnId: 'b', name: 'B', type: 'numeric', editable: true },
32
+ { columnId: 'c', name: 'C', type: 'numeric', editable: true },
33
+ ];
34
+
35
+ function App() {
36
+ const [data, setData] = useState(initialData);
37
+
38
+ const handleChange = (e: ICellValueChangedEvent<Row>) => {
39
+ setData(prev => prev.map(row =>
40
+ row.id === e.item.id ? { ...row, [e.columnId]: e.newValue } : row
41
+ ));
42
+ };
43
+
44
+ return (
45
+ <OGrid
46
+ columns={columns}
47
+ data={data}
48
+ getRowId={(r) => r.id}
49
+ editable
50
+ formulas
51
+ onCellValueChanged={handleChange}
52
+ initialFormulas={[
53
+ { col: 2, row: 0, formula: '=A1+B1' },
54
+ { col: 2, row: 1, formula: '=SUM(A1:A2)' },
55
+ ]}
56
+ />
57
+ );
58
+ }
59
+ ```
60
+
61
+ :::tip Switching UI libraries
62
+ The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
63
+
64
+ - **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
65
+ - **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
66
+ - **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
67
+ :::
68
+
69
+ </TabItem>
70
+ <TabItem value="angular" label="Angular">
71
+
72
+ ```typescript
73
+
74
+ @Component({
75
+ standalone: true,
76
+ imports: [OGridComponent],
77
+ providers: [FormulaEngineService],
78
+ template: `<ogrid [props]="gridProps" />`
79
+ })
80
+ export class FormulaGridComponent {
81
+ constructor(private formulaEngine: FormulaEngineService<Row>) {
82
+ this.formulaEngine.configure({ formulas: true });
83
+ }
84
+
85
+ gridProps = {
86
+ columns: [
87
+ { columnId: 'a', name: 'A', type: 'numeric', editable: true },
88
+ { columnId: 'b', name: 'B', type: 'numeric', editable: true },
89
+ { columnId: 'c', name: 'C', type: 'numeric', editable: true },
90
+ ] as IColumnDef<Row>[],
91
+ data: initialData,
92
+ getRowId: (r: Row) => r.id,
93
+ editable: true,
94
+ };
95
+ }
96
+ ```
97
+
98
+ :::tip Switching UI libraries
99
+ Same component API across Angular packages. To switch, just change the import:
100
+
101
+ - **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
102
+ - **Angular Material**: `from '@alaarab/ogrid-angular-material'`
103
+ - **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
104
+
105
+ All components are standalone — no NgModule required.
106
+ :::
107
+
108
+ </TabItem>
109
+ <TabItem value="vue" label="Vue">
110
+
111
+ ```vue
112
+ <script setup lang="ts">
113
+
114
+ const columns: IColumnDef<Row>[] = [
115
+ { columnId: 'a', name: 'A', type: 'numeric', editable: true },
116
+ { columnId: 'b', name: 'B', type: 'numeric', editable: true },
117
+ { columnId: 'c', name: 'C', type: 'numeric', editable: true },
118
+ ];
119
+
120
+ const data = ref(initialData);
121
+ const formulas = ref(true);
122
+ const items = ref(data.value);
123
+ const flatColumns = ref(columns);
124
+
125
+ const formulaEngine = useFormulaEngine({
126
+ formulas,
127
+ items,
128
+ flatColumns,
129
+ initialFormulas: [
130
+ { col: 2, row: 0, formula: '=A1+B1' },
131
+ ],
132
+ });
133
+ </script>
134
+
135
+ <template>
136
+ <OGrid :columns="columns" :data="data" :getRowId="(r) => r.id" editable />
137
+ </template>
138
+ ```
139
+
140
+ :::tip Switching UI libraries
141
+ Same component API across Vue packages. To switch, just change the import:
142
+
143
+ - **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
144
+ - **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
145
+ - **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
146
+ :::
147
+
148
+ </TabItem>
149
+ <TabItem value="js" label="Vanilla JS">
150
+
151
+ ```js
152
+
153
+ const grid = new OGrid(document.getElementById('grid'), {
154
+ columns: [
155
+ { columnId: 'a', name: 'A', type: 'numeric', editable: true },
156
+ { columnId: 'b', name: 'B', type: 'numeric', editable: true },
157
+ { columnId: 'c', name: 'C', type: 'numeric', editable: true },
158
+ ],
159
+ data: initialData,
160
+ getRowId: (r) => r.id,
161
+ editable: true,
162
+ });
163
+
164
+ // Use FormulaEngineState for formula support
165
+
166
+ const formulaState = new FormulaEngineState({ formulas: true });
167
+ formulaState.setFormula(2, 0, '=A1+B1', grid.getApi().getDataAccessor());
168
+ ```
169
+
170
+ </TabItem>
171
+ </Tabs>
172
+
173
+ ## How It Works
174
+
175
+ ### Enabling Formulas
176
+
177
+ Set `formulas={true}` on the `<OGrid>` component. This opt-in prop lazily creates a `FormulaEngine` instance — grids without formulas pay zero cost.
178
+
179
+ ### Entering Formulas
180
+
181
+ Type any value starting with `=` into an editable cell. The formula engine intercepts the edit, parses the expression, evaluates it, and displays the result. The formula string is stored separately from the data — your `data[]` array is never modified by formulas.
182
+
183
+ ### Dependency Tracking
184
+
185
+ The engine builds a dependency graph. When cell A1 changes, all formulas that reference A1 are automatically recalculated in topological order. Circular references are detected and produce a `#CIRC!` error.
186
+
187
+ ### Initial Formulas
188
+
189
+ Pass `initialFormulas` to pre-load formulas when the grid mounts:
190
+
191
+ ```tsx
192
+ <OGrid
193
+ formulas
194
+ initialFormulas={[
195
+ { col: 0, row: 3, formula: '=SUM(A1:A3)' },
196
+ { col: 1, row: 3, formula: '=AVERAGE(B1:B3)' },
197
+ ]}
198
+ />
199
+ ```
200
+
201
+ Coordinates use 0-based column index (position in flat columns array) and 0-based row index (position in data array).
202
+
203
+ ### Error Handling
204
+
205
+ Formula errors display in red text automatically using the `--ogrid-formula-error-color` CSS variable:
206
+
207
+ | Error | Meaning |
208
+ |-------|---------|
209
+ | `#REF!` | Invalid cell reference |
210
+ | `#DIV/0!` | Division by zero |
211
+ | `#VALUE!` | Wrong value type |
212
+ | `#NAME?` | Unknown function name |
213
+ | `#CIRC!` | Circular reference detected |
214
+ | `#N/A` | No match found (VLOOKUP, MATCH) |
215
+ | `#NUM!` | Invalid numeric value (e.g., LARGE with k out of range) |
216
+ | `#ERROR!` | General formula error |
217
+
218
+ ### Formula-Aware Features
219
+
220
+ When `formulas` is enabled, these features become formula-aware automatically:
221
+
222
+ - **Clipboard** — Copy copies the formula string (e.g., `=SUM(A1:A5)`), not the computed value. Pasting a string starting with `=` creates a new formula.
223
+ - **Fill Handle** — Dragging a formula cell adjusts relative references. `=A1+B1` dragged down becomes `=A2+B2`. Absolute references (`$A$1`) are preserved.
224
+ - **CSV Export** — Pass `exportMode: 'formulas'` to export formula strings instead of computed values.
225
+
226
+ ## Built-in Functions (93)
227
+
228
+ ### Math (30)
229
+ `SUM`, `AVERAGE`, `MIN`, `MAX`, `COUNT`, `COUNTA`, `ROUND`, `ROUNDUP`, `ROUNDDOWN`, `INT`, `TRUNC`, `ABS`, `CEILING`, `FLOOR`, `MOD`, `POWER`, `SQRT`, `PRODUCT`, `SUMPRODUCT`, `MEDIAN`, `LARGE`, `SMALL`, `RANK`, `SIGN`, `LOG`, `LN`, `EXP`, `PI`, `RAND`, `RANDBETWEEN`
230
+
231
+ ### Logical (10)
232
+ `IF`, `AND`, `OR`, `NOT`, `XOR`, `IFERROR`, `IFNA`, `IFS`, `SWITCH`, `CHOOSE`
233
+
234
+ ### Text (22)
235
+ `CONCATENATE`, `CONCAT`, `UPPER`, `LOWER`, `TRIM`, `LEFT`, `RIGHT`, `MID`, `LEN`, `SUBSTITUTE`, `FIND`, `SEARCH`, `REPLACE`, `REPT`, `EXACT`, `PROPER`, `CLEAN`, `CHAR`, `CODE`, `TEXT`, `VALUE`, `TEXTJOIN`
236
+
237
+ ### Lookup (5)
238
+ `VLOOKUP`, `HLOOKUP`, `XLOOKUP`, `INDEX`, `MATCH`
239
+
240
+ ### Date (14)
241
+ `TODAY`, `NOW`, `YEAR`, `MONTH`, `DAY`, `DATE`, `DATEDIF`, `EDATE`, `EOMONTH`, `WEEKDAY`, `HOUR`, `MINUTE`, `SECOND`, `NETWORKDAYS`
242
+
243
+ ### Statistics (6)
244
+ `SUMIF`, `COUNTIF`, `AVERAGEIF`, `SUMIFS`, `COUNTIFS`, `AVERAGEIFS`
245
+
246
+ ### Information (6)
247
+ `ISBLANK`, `ISNUMBER`, `ISTEXT`, `ISERROR`, `ISNA`, `TYPE`
248
+
249
+ ### Custom Functions
250
+
251
+ Register custom functions at runtime:
252
+
253
+ ```tsx
254
+ <OGrid
255
+ formulas
256
+ formulaFunctions={{
257
+ DOUBLE: {
258
+ name: 'DOUBLE',
259
+ minArgs: 1,
260
+ maxArgs: 1,
261
+ evaluate: (args) => Number(args[0]) * 2,
262
+ },
263
+ }}
264
+ />
265
+ ```
266
+
267
+ Then use `=DOUBLE(A1)` in any cell.
268
+
269
+ ## Named Ranges
270
+
271
+ Define human-readable names for cell references and ranges:
272
+
273
+ ```tsx
274
+ <OGrid
275
+ formulas
276
+ namedRanges={{
277
+ Revenue: 'A1:A10',
278
+ TaxRate: 'B1',
279
+ }}
280
+ />
281
+ ```
282
+
283
+ Then use them in formulas: `=SUM(Revenue)` or `=A1*TaxRate`.
284
+
285
+ The `FormulaEngine` also supports runtime management:
286
+
287
+ ```tsx
288
+ // Define at runtime via the grid API
289
+ engine.defineNamedRange('Profit', 'C1:C10');
290
+
291
+ // Remove a named range
292
+ engine.removeNamedRange('Profit');
293
+
294
+ // Get all named ranges
295
+ const ranges = engine.getNamedRanges(); // Map<string, string>
296
+ ```
297
+
298
+ Named ranges are **case-insensitive** — `Revenue`, `revenue`, and `REVENUE` all resolve to the same range.
299
+
300
+ ## Formula Auditing
301
+
302
+ Trace formula dependencies for debugging and visualization:
303
+
304
+ ```tsx
305
+ // Get all cells that a formula depends on (deep, transitive)
306
+ const precedents = engine.getPrecedents(col, row);
307
+ // → [{ cellKey, col, row, formula?, value }]
308
+
309
+ // Get all cells that depend on a cell (deep, transitive)
310
+ const dependents = engine.getDependents(col, row);
311
+
312
+ // Get full audit trail (target + precedents + dependents)
313
+ const trail = engine.getAuditTrail(col, row);
314
+ // → { target: IAuditEntry, precedents: IAuditEntry[], dependents: IAuditEntry[] }
315
+ ```
316
+
317
+ Each `IAuditEntry` includes: `cellKey`, `col`, `row`, `formula` (if the cell has one), and `value`.
318
+
319
+ ## Cross-Sheet References
320
+
321
+ Reference cells in other sheets using Excel-style syntax:
322
+
323
+ ```tsx
324
+ // Register sheet accessors for cross-sheet formulas
325
+ <OGrid
326
+ formulas
327
+ sheets={{
328
+ Sheet2: sheet2Accessor,
329
+ 'Sales Data': salesAccessor,
330
+ }}
331
+ />
332
+ ```
333
+
334
+ Then use in formulas:
335
+ - `=Sheet2!A1` — single cell from Sheet2
336
+ - `=SUM(Sheet2!A1:A10)` — range from Sheet2
337
+ - `='Sales Data'!B5` — quoted sheet name (for names with spaces)
338
+
339
+ The `FormulaEngine` also supports runtime registration:
340
+
341
+ ```tsx
342
+ engine.registerSheet('Sheet2', accessor);
343
+ engine.unregisterSheet('Sheet2');
344
+ ```
345
+
346
+ Referencing an unregistered sheet produces a `#REF!` error.
347
+
348
+ ## Props Reference
349
+
350
+ | Prop | Type | Default | Description |
351
+ |------|------|---------|-------------|
352
+ | `formulas` | `boolean` | `false` | Enable formula support (tree-shakeable) |
353
+ | `initialFormulas` | `Array<{ col, row, formula }>` | -- | Pre-load formulas on mount |
354
+ | `onFormulaRecalc` | `(result: IRecalcResult) => void` | -- | Called after each recalculation with updated cells |
355
+ | `formulaFunctions` | `Record<string, IFormulaFunction>` | -- | Custom functions to register |
356
+ | `namedRanges` | `Record<string, string>` | -- | Named ranges: name → cell/range ref string |
357
+ | `sheets` | `Record<string, IGridDataAccessor>` | -- | Sheet accessors for cross-sheet references |
358
+
359
+ ## Cell Reference Syntax
360
+
361
+ | Syntax | Example | Meaning |
362
+ |--------|---------|---------|
363
+ | Relative | `A1` | Column A, Row 1 — adjusts during fill |
364
+ | Absolute column | `$A1` | Column A locked, row adjusts |
365
+ | Absolute row | `A$1` | Column adjusts, row 1 locked |
366
+ | Fully absolute | `$A$1` | Both locked — never adjusts |
367
+ | Range | `A1:B5` | Rectangular range from A1 to B5 |
368
+ | Named range | `Revenue` | Resolves to the defined cell/range |
369
+ | Cross-sheet | `Sheet2!A1` | Cell A1 from Sheet2 |
370
+ | Quoted sheet | `'Sales Data'!A1` | Cell from a sheet with spaces in name |
371
+
372
+ ## Related
373
+
374
+ - [Editing & Clipboard](./editing) — cell editing, copy/paste, fill handle, undo/redo
375
+ - [Cell References](./cell-references) — Excel-style column letters and name box
376
+ - [CSV Export](./csv-export) — export grid data including formula strings
377
+ - [Status Bar](./status-bar) — aggregations on selected cells
378
+
379
+ ## Community
380
+
381
+ Have questions or want to discuss formulas? Join the [OGrid Community Discord](https://discord.gg/KMajyx9j4m).
@@ -0,0 +1,311 @@
1
+ ---
2
+ sidebar_position: 18
3
+ title: Grid API
4
+ description: Imperative API ref for programmatic control of the grid
5
+ ---
6
+
7
+
8
+ # Grid API
9
+
10
+ The `IOGridApi` ref gives you imperative control over the grid: set data, manage loading state, save and restore column state, control filters, and manage row selection -- all from outside the grid component.
11
+
12
+ ## Live Demo
13
+
14
+ <GridApiDemo />
15
+
16
+ :::tip Try it in your framework
17
+ The demo above uses Radix UI for styling. To see this feature with your framework's design system (Fluent UI, Material UI, Vuetify, PrimeNG, etc.), click **"Open in online demo"** below the demo.
18
+ :::
19
+
20
+ ## Quick Example
21
+
22
+ <Tabs groupId="framework">
23
+ <TabItem value="react" label="React" default>
24
+
25
+ ```tsx
26
+
27
+ interface Row {
28
+ id: number;
29
+ name: string;
30
+ department: string;
31
+ salary: number;
32
+ }
33
+
34
+ function App() {
35
+ const gridRef = useRef<IOGridApi<Row>>(null);
36
+
37
+ const handleRefresh = async () => {
38
+ gridRef.current?.setLoading(true);
39
+ const data = await fetchEmployees();
40
+ gridRef.current?.setRowData(data);
41
+ gridRef.current?.setLoading(false);
42
+ };
43
+
44
+ return (
45
+ <>
46
+ <button onClick={handleRefresh}>Refresh</button>
47
+ <OGrid
48
+ ref={gridRef}
49
+ columns={columns}
50
+ data={initialData}
51
+ getRowId={(r) => r.id}
52
+ />
53
+ </>
54
+ );
55
+ }
56
+ ```
57
+
58
+ :::tip Switching UI libraries
59
+ The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
60
+
61
+ - **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
62
+ - **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
63
+ - **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
64
+ :::
65
+
66
+ </TabItem>
67
+ <TabItem value="angular" label="Angular">
68
+
69
+ ```typescript
70
+
71
+ interface Row {
72
+ id: number;
73
+ name: string;
74
+ department: string;
75
+ salary: number;
76
+ }
77
+
78
+ @Component({
79
+ standalone: true,
80
+ imports: [OGridComponent],
81
+ template: `
82
+ <button (click)="handleRefresh()">Refresh</button>
83
+ <ogrid [props]="gridProps" />
84
+ `
85
+ })
86
+ export class GridComponent {
87
+ gridProps = {
88
+ columns: columns,
89
+ data: initialData,
90
+ getRowId: (r: Row) => r.id,
91
+ };
92
+
93
+ async handleRefresh() {
94
+ // Use the grid API via ViewChild or service
95
+ const data = await fetchEmployees();
96
+ this.gridProps = { ...this.gridProps, data };
97
+ }
98
+ }
99
+ ```
100
+
101
+ :::tip Switching UI libraries
102
+ Same component API across Angular packages. To switch, just change the import:
103
+
104
+ - **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
105
+ - **Angular Material**: `from '@alaarab/ogrid-angular-material'`
106
+ - **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
107
+
108
+ All components are standalone — no NgModule required.
109
+ :::
110
+
111
+ </TabItem>
112
+ <TabItem value="vue" label="Vue">
113
+
114
+ ```vue
115
+ <script setup lang="ts">
116
+
117
+ interface Row {
118
+ id: number;
119
+ name: string;
120
+ department: string;
121
+ salary: number;
122
+ }
123
+
124
+ const data = ref<Row[]>(initialData);
125
+
126
+ const gridProps = {
127
+ columns,
128
+ data: data.value,
129
+ getRowId: (r: Row) => r.id,
130
+ };
131
+
132
+ async function handleRefresh() {
133
+ const fresh = await fetchEmployees();
134
+ data.value = fresh;
135
+ }
136
+ </script>
137
+
138
+ <template>
139
+ <button @click="handleRefresh">Refresh</button>
140
+ <OGrid :gridProps="gridProps" />
141
+ </template>
142
+ ```
143
+
144
+ :::tip Switching UI libraries
145
+ Same component API across Vue packages. To switch, just change the import:
146
+
147
+ - **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
148
+ - **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
149
+ - **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
150
+ :::
151
+
152
+ </TabItem>
153
+ <TabItem value="js" label="Vanilla JS">
154
+
155
+ ```js
156
+
157
+ const grid = new OGrid(document.getElementById('grid'), {
158
+ columns: columns,
159
+ data: initialData,
160
+ getRowId: (r) => r.id,
161
+ });
162
+
163
+ // Use the grid API directly
164
+ async function handleRefresh() {
165
+ grid.api.setLoading(true);
166
+ const data = await fetchEmployees();
167
+ grid.api.setRowData(data);
168
+ grid.api.setLoading(false);
169
+ }
170
+ ```
171
+
172
+ </TabItem>
173
+ </Tabs>
174
+
175
+ ## How It Works
176
+
177
+ Pass a `ref` to the `OGrid` component. The ref exposes the `IOGridApi<T>` interface with methods for programmatic grid control.
178
+
179
+ ### API Methods
180
+
181
+ | Method | Signature | Description |
182
+ |---|---|---|
183
+ | `setRowData` | `(data: T[]) => void` | Replace all row data (client-side only; no-op with `dataSource`) |
184
+ | `setLoading` | `(loading: boolean) => void` | Show or hide the loading overlay |
185
+ | `getDisplayedRows` | `() => T[]` | Get the currently displayed (filtered, sorted, paginated) rows |
186
+ | `refreshData` | `() => void` | Re-trigger a data fetch (server-side only; no-op for client-side) |
187
+ | `getColumnState` | `() => IGridColumnState` | Get current column visibility, sort, order, widths, and filters |
188
+ | `applyColumnState` | `(state: Partial<IGridColumnState>) => void` | Bulk-restore any combination of column state fields |
189
+ | `getColumnOrder` | `() => string[]` | Get the current column display order (array of column IDs) |
190
+ | `setColumnOrder` | `(order: string[]) => void` | Programmatically set the column display order |
191
+ | `setFilterModel` | `(filters: IFilters) => void` | Set the active filter model |
192
+ | `clearFilters` | `() => void` | Clear all active filters |
193
+ | `clearSort` | `() => void` | Reset the sort to no active sort |
194
+ | `resetGridState` | `(options?: { keepSelection?: boolean }) => void` | Reset all grid state (filters, sort, and optionally selection) |
195
+ | `getSelectedRows` | `() => RowId[]` | Get the IDs of currently selected rows |
196
+ | `setSelectedRows` | `(rowIds: RowId[]) => void` | Set which rows are selected |
197
+ | `selectAll` | `() => void` | Select all rows |
198
+ | `deselectAll` | `() => void` | Deselect all rows |
199
+ | `scrollToRow` | `(index: number, options?: { align?: 'start' \| 'center' \| 'end' }) => void` | Scroll to a row by index (virtual scrolling only) |
200
+
201
+ ### Save and Restore Column State
202
+
203
+ `getColumnState()` returns an `IGridColumnState` object that is fully JSON-serializable. Store it in `localStorage`, a database, or a URL parameter, and restore it later with `applyColumnState()`.
204
+
205
+ ```tsx
206
+ // Save to localStorage
207
+ const handleSave = () => {
208
+ const state = gridRef.current?.getColumnState();
209
+ if (state) {
210
+ localStorage.setItem('grid-state', JSON.stringify(state));
211
+ }
212
+ };
213
+
214
+ // Restore from localStorage
215
+ const handleRestore = () => {
216
+ const saved = localStorage.getItem('grid-state');
217
+ if (saved) {
218
+ gridRef.current?.applyColumnState(JSON.parse(saved));
219
+ }
220
+ };
221
+ ```
222
+
223
+ ### `IGridColumnState` Fields
224
+
225
+ ```tsx
226
+ interface IGridColumnState {
227
+ visibleColumns: string[];
228
+ sort?: { field: string; direction: 'asc' | 'desc' };
229
+ columnOrder?: string[];
230
+ columnWidths?: Record<string, number>;
231
+ filters?: IFilters;
232
+ pinnedColumns?: Record<string, 'left' | 'right'>;
233
+ }
234
+ ```
235
+
236
+ | Field | Description |
237
+ |---|---|
238
+ | `visibleColumns` | Array of visible column IDs |
239
+ | `sort` | Active sort column and direction |
240
+ | `columnOrder` | Column display order (array of column IDs) |
241
+ | `columnWidths` | Column width overrides in pixels |
242
+ | `filters` | Active filter values by field |
243
+ | `pinnedColumns` | Pinned column positions by column ID |
244
+
245
+ All fields in `applyColumnState` are optional. Pass only what you want to restore.
246
+
247
+ ```tsx
248
+ // Restore only sort and filters, leave visibility and widths unchanged
249
+ gridRef.current?.applyColumnState({
250
+ sort: { field: 'salary', direction: 'desc' },
251
+ filters: { department: { type: 'multiSelect', value: ['Engineering'] } },
252
+ });
253
+ ```
254
+
255
+ ### Programmatic Filtering
256
+
257
+ Use `setFilterModel` to apply filters from outside the grid.
258
+
259
+ ```tsx
260
+ gridRef.current?.setFilterModel({
261
+ department: { type: 'multiSelect', value: ['Engineering', 'Design'] },
262
+ name: { type: 'text', value: 'John' },
263
+ });
264
+ ```
265
+
266
+ Filter values use the `FilterValue` discriminated union type:
267
+ - `{ type: 'text', value: string }` for text filters
268
+ - `{ type: 'multiSelect', value: string[] }` for multi-select filters
269
+ - `{ type: 'people', value: UserLike }` for people filters
270
+ - `{ type: 'date', value: IDateFilterValue }` for date range filters
271
+
272
+ ### Row Selection Control
273
+
274
+ ```tsx
275
+ // Select specific rows
276
+ gridRef.current?.setSelectedRows([1, 5, 12]);
277
+
278
+ // Get current selection
279
+ const selected = gridRef.current?.getSelectedRows(); // [1, 5, 12]
280
+
281
+ // Select all / deselect all
282
+ gridRef.current?.selectAll();
283
+ gridRef.current?.deselectAll();
284
+ ```
285
+
286
+ ### Updating Data
287
+
288
+ For client-side grids, use `setRowData` to replace the data without re-mounting the component.
289
+
290
+ ```tsx
291
+ const refreshData = async () => {
292
+ gridRef.current?.setLoading(true);
293
+ const fresh = await fetch('/api/employees').then((r) => r.json());
294
+ gridRef.current?.setRowData(fresh);
295
+ gridRef.current?.setLoading(false);
296
+ };
297
+ ```
298
+
299
+ ## Props Reference
300
+
301
+ | Type | Field | Description |
302
+ |---|---|---|
303
+ | `OGrid` | `ref` | `React.Ref<IOGridApi<T>>` |
304
+ | `IOGridProps<T>` | `onColumnResized` | `(columnId: string, width: number) => void` -- notified on user column resize |
305
+
306
+ ## Related
307
+
308
+ - [Column Chooser](./column-chooser) -- the Column Chooser modifies the same visibility state accessible via `getColumnState`
309
+ - [Server-Side Data](./server-side-data) -- `setFilterModel` triggers a re-fetch in server-side mode
310
+ - [Status Bar](./status-bar) -- reflects the state managed by the API
311
+ - [CSV Export](./csv-export) -- combine with `getColumnState` to export only visible columns