@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,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
|