@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,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 2
|
|
3
|
+
title: Custom Cell Editors
|
|
4
|
+
description: Build custom inline and popup cell editors for OGrid columns.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Custom Cell Editors
|
|
8
|
+
|
|
9
|
+
OGrid includes built-in editors for common types (`text`, `select`, `checkbox`, `richSelect`, `date`), but you can create fully custom editors for any use case. A custom editor is a React component that receives the cell value, update callbacks, and optional params.
|
|
10
|
+
|
|
11
|
+
## The ICellEditorProps Interface
|
|
12
|
+
|
|
13
|
+
Every custom editor receives these props:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
interface ICellEditorProps<T> {
|
|
17
|
+
value: unknown; // Current cell value
|
|
18
|
+
onValueChange: (value: unknown) => void; // Update the value as user types
|
|
19
|
+
onCommit: () => void; // Save and close
|
|
20
|
+
onCancel: () => void; // Discard and close
|
|
21
|
+
item: T; // The full row data
|
|
22
|
+
column: IColumnDef<T>; // The column definition
|
|
23
|
+
cellEditorParams?: CellEditorParams; // Extra config from the column def
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Popup Editor Example
|
|
28
|
+
|
|
29
|
+
A popup editor renders in a floating overlay above the cell. This is ideal for editors that need more space than an inline cell -- like a textarea, a date picker, or a color picker.
|
|
30
|
+
|
|
31
|
+
### Step 1: Create the Editor Component
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
|
|
35
|
+
interface NoteItem {
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
notes: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function NotesEditor({ value, onValueChange, onCommit, onCancel }: ICellEditorProps<NoteItem>) {
|
|
42
|
+
return (
|
|
43
|
+
<div style={{ padding: 12, width: 300 }}>
|
|
44
|
+
<textarea
|
|
45
|
+
autoFocus
|
|
46
|
+
rows={5}
|
|
47
|
+
value={String(value ?? '')}
|
|
48
|
+
onChange={(e) => onValueChange(e.target.value)}
|
|
49
|
+
onKeyDown={(e) => {
|
|
50
|
+
if (e.key === 'Escape') onCancel();
|
|
51
|
+
}}
|
|
52
|
+
style={{ width: '100%', resize: 'vertical', fontFamily: 'inherit', fontSize: 14 }}
|
|
53
|
+
/>
|
|
54
|
+
<div style={{ display: 'flex', gap: 8, marginTop: 8, justifyContent: 'flex-end' }}>
|
|
55
|
+
<button onClick={onCancel}>Cancel</button>
|
|
56
|
+
<button onClick={onCommit}>Save</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Step 2: Wire It Up in the Column Definition
|
|
64
|
+
|
|
65
|
+
Set `cellEditor` to your component and `cellEditorPopup: true` to render it in a floating overlay:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
|
|
69
|
+
const columns: IColumnDef<NoteItem>[] = [
|
|
70
|
+
{ columnId: 'title', name: 'Title', editable: true },
|
|
71
|
+
{
|
|
72
|
+
columnId: 'notes',
|
|
73
|
+
name: 'Notes',
|
|
74
|
+
editable: true,
|
|
75
|
+
cellEditor: NotesEditor,
|
|
76
|
+
cellEditorPopup: true,
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 3: Enable Editing on the Grid
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<OGrid
|
|
85
|
+
columns={columns}
|
|
86
|
+
data={items}
|
|
87
|
+
getRowId={(item) => item.id}
|
|
88
|
+
editable
|
|
89
|
+
onCellValueChanged={(event) => {
|
|
90
|
+
console.log(`${event.columnId} changed from`, event.oldValue, 'to', event.newValue);
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
When a user double-clicks the Notes cell (or presses Enter on it), the `NotesEditor` appears in a popup. Clicking Save calls `onCommit`, which writes the value back to the grid and fires `onCellValueChanged`.
|
|
96
|
+
|
|
97
|
+
## Inline Editor Example
|
|
98
|
+
|
|
99
|
+
Without `cellEditorPopup`, the editor replaces the cell content inline. This is the default behavior and works well for small inputs:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
function ColorEditor({ value, onValueChange, onCommit, onCancel }: ICellEditorProps<any>) {
|
|
103
|
+
return (
|
|
104
|
+
<input
|
|
105
|
+
type="color"
|
|
106
|
+
autoFocus
|
|
107
|
+
value={String(value ?? '#000000')}
|
|
108
|
+
onChange={(e) => onValueChange(e.target.value)}
|
|
109
|
+
onBlur={onCommit}
|
|
110
|
+
onKeyDown={(e) => {
|
|
111
|
+
if (e.key === 'Escape') onCancel();
|
|
112
|
+
if (e.key === 'Enter') onCommit();
|
|
113
|
+
}}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const columns = [
|
|
119
|
+
{
|
|
120
|
+
columnId: 'color',
|
|
121
|
+
name: 'Color',
|
|
122
|
+
editable: true,
|
|
123
|
+
cellEditor: ColorEditor,
|
|
124
|
+
// No cellEditorPopup -- renders inline
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Using cellEditorParams
|
|
130
|
+
|
|
131
|
+
Pass extra configuration to your editor through `cellEditorParams`. This keeps your editor component reusable across columns:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
function RatingEditor({ value, onValueChange, onCommit, cellEditorParams }: ICellEditorProps<any>) {
|
|
135
|
+
const maxStars = (cellEditorParams?.maxStars as number) ?? 5;
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div style={{ display: 'flex', gap: 4 }}>
|
|
139
|
+
{Array.from({ length: maxStars }, (_, i) => (
|
|
140
|
+
<button
|
|
141
|
+
key={i}
|
|
142
|
+
onClick={() => {
|
|
143
|
+
onValueChange(i + 1);
|
|
144
|
+
// Auto-commit after selection
|
|
145
|
+
setTimeout(onCommit, 0);
|
|
146
|
+
}}
|
|
147
|
+
style={{
|
|
148
|
+
background: 'none',
|
|
149
|
+
border: 'none',
|
|
150
|
+
cursor: 'pointer',
|
|
151
|
+
fontSize: 18,
|
|
152
|
+
opacity: (value as number) >= i + 1 ? 1 : 0.3,
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
*
|
|
156
|
+
</button>
|
|
157
|
+
))}
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const columns = [
|
|
163
|
+
{
|
|
164
|
+
columnId: 'rating',
|
|
165
|
+
name: 'Rating',
|
|
166
|
+
editable: true,
|
|
167
|
+
cellEditor: RatingEditor,
|
|
168
|
+
cellEditorParams: { maxStars: 10 },
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
:::tip
|
|
174
|
+
The `cellEditorParams` object is passed through as-is. You can put any serializable configuration there -- select options, validation rules, format strings, API endpoints, or anything your editor needs.
|
|
175
|
+
:::
|
|
176
|
+
|
|
177
|
+
## Built-in Editors
|
|
178
|
+
|
|
179
|
+
Before building a custom editor, check if a built-in one fits your needs:
|
|
180
|
+
|
|
181
|
+
| `cellEditor` | Description | `cellEditorParams` |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `'text'` | Standard text input (default) | -- |
|
|
184
|
+
| `'select'` | Dropdown select | `{ values: ['Option A', 'Option B'] }` |
|
|
185
|
+
| `'checkbox'` | Boolean toggle | -- |
|
|
186
|
+
| `'richSelect'` | Searchable dropdown with keyboard navigation | `{ values: [...], formatValue: (v) => string }` |
|
|
187
|
+
| `'date'` | Native date picker (`<input type="date">`) | -- |
|
|
188
|
+
|
|
189
|
+
## Conditional Editability
|
|
190
|
+
|
|
191
|
+
Use a function for `editable` to control which rows allow editing:
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
{
|
|
195
|
+
columnId: 'status',
|
|
196
|
+
name: 'Status',
|
|
197
|
+
editable: (item) => item.status !== 'locked',
|
|
198
|
+
cellEditor: 'select',
|
|
199
|
+
cellEditorParams: { values: ['Draft', 'Review', 'Published'] },
|
|
200
|
+
}
|
|
201
|
+
```
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 5
|
|
3
|
+
title: Framework Showcase
|
|
4
|
+
description: See OGrid in action with React, Angular, Vue, and Vanilla JS — 8 UI implementations, one API
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Framework Showcase
|
|
9
|
+
|
|
10
|
+
OGrid renders with your design system's native components. Same API, same features — different look and feel. Every grid below is **fully interactive**: sort, filter, edit, select cells, copy/paste, and undo/redo.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## React
|
|
15
|
+
|
|
16
|
+
Three design-system implementations, all sharing the same hooks and headless logic from `@alaarab/ogrid-react`.
|
|
17
|
+
|
|
18
|
+
### Radix UI <small>(Default)</small>
|
|
19
|
+
|
|
20
|
+
The lightest option — Radix primitives are bundled with zero peer dependencies beyond React.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @alaarab/ogrid-react-radix
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
<ShowcaseRadixDemo />
|
|
27
|
+
|
|
28
|
+
### Fluent UI
|
|
29
|
+
|
|
30
|
+
Microsoft's Fluent UI v9 components. Best for **Microsoft 365 and SharePoint** projects.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @alaarab/ogrid-react-fluent @fluentui/react-components
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
<ShowcaseFluentDemo />
|
|
37
|
+
|
|
38
|
+
### Material UI
|
|
39
|
+
|
|
40
|
+
Google's Material Design via MUI v7. Best for apps already on **Material Design**.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install @alaarab/ogrid-react-material @mui/material @emotion/react @emotion/styled
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
<ShowcaseMaterialDemo />
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Angular
|
|
51
|
+
|
|
52
|
+
Signals-based reactivity with standalone components. Zone-less by default, no NgModule boilerplate. All three packages share the same services from `@alaarab/ogrid-angular`.
|
|
53
|
+
|
|
54
|
+
### Radix (Angular CDK) <small>(Default)</small>
|
|
55
|
+
|
|
56
|
+
The lightest option — Angular CDK for overlays with zero heavy UI framework dependencies.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install @alaarab/ogrid-angular-radix @angular/cdk
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
|
|
64
|
+
@Component({
|
|
65
|
+
standalone: true,
|
|
66
|
+
imports: [OGridComponent],
|
|
67
|
+
template: `<ogrid [props]="gridProps" />`
|
|
68
|
+
})
|
|
69
|
+
export class GridComponent {
|
|
70
|
+
gridProps = { columns, data, getRowId: (row) => row.id, defaultPageSize: 10 };
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
<div style={{ marginTop: '0.75rem' }}>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
### Angular Material
|
|
78
|
+
|
|
79
|
+
Angular Material v21 components. Best for projects already using **Material Design**.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install @alaarab/ogrid-angular-material @angular/material @angular/cdk
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Same API — just change the import
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### PrimeNG
|
|
90
|
+
|
|
91
|
+
PrimeNG v21 components for teams already using the PrimeFaces ecosystem.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install @alaarab/ogrid-angular-primeng primeng
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// Same API — just change the import
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Vue
|
|
104
|
+
|
|
105
|
+
Composition API composables using `ref()` and `computed()`. All three packages share the same composables from `@alaarab/ogrid-vue`.
|
|
106
|
+
|
|
107
|
+
### Radix (Headless UI) <small>(Default)</small>
|
|
108
|
+
|
|
109
|
+
The lightest option — Headless UI Vue components are bundled with zero peer dependencies beyond Vue.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install @alaarab/ogrid-vue-radix
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```vue
|
|
116
|
+
<script setup lang="ts">
|
|
117
|
+
|
|
118
|
+
const gridProps = { columns, data, getRowId: (row) => row.id, defaultPageSize: 10 };
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<template>
|
|
122
|
+
<OGrid :gridProps="gridProps" />
|
|
123
|
+
</template>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
<div style={{ marginTop: '0.75rem' }}>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
### Vuetify
|
|
130
|
+
|
|
131
|
+
Vuetify 3 components. Best for projects already using **Vuetify and Material Design**.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm install @alaarab/ogrid-vue-vuetify vuetify
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```vue
|
|
138
|
+
<script setup lang="ts">
|
|
139
|
+
// Same API — just change the import
|
|
140
|
+
</script>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### PrimeVue
|
|
144
|
+
|
|
145
|
+
PrimeVue 4 components for teams already using the PrimeFaces ecosystem.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm install @alaarab/ogrid-vue-primevue primevue
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```vue
|
|
152
|
+
<script setup lang="ts">
|
|
153
|
+
// Same API — just change the import
|
|
154
|
+
</script>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Vanilla JS / TypeScript
|
|
160
|
+
|
|
161
|
+
No framework, no virtual DOM. A class-based imperative API that renders directly to the DOM with a built-in CSS theme supporting light and dark mode.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm install @alaarab/ogrid-js
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
<VanillaJSDemo />
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## At a Glance
|
|
172
|
+
|
|
173
|
+
| Framework | Design Systems | Reactivity |
|
|
174
|
+
|-----------|---------------|------------|
|
|
175
|
+
| **React** 17–19 | Radix *(default, bundled)* · Fluent UI v9 · MUI v7 | Hooks |
|
|
176
|
+
| **Angular** 21 | Radix (CDK) *(default)* · Angular Material · PrimeNG | Signals |
|
|
177
|
+
| **Vue** 3.3+ | Radix (Headless UI) *(default, bundled)* · Vuetify 3 · PrimeVue 4 | Composition API |
|
|
178
|
+
| **Vanilla JS/TS** | Built-in CSS theme *(zero deps)* | EventEmitter |
|
|
179
|
+
|
|
180
|
+
All 10 packages share the same **headless core** (`@alaarab/ogrid-core`) and pass the same **2,135 tests**. Switching design systems is a matter of changing imports.
|
|
181
|
+
|
|
182
|
+
## Choosing a Package
|
|
183
|
+
|
|
184
|
+
- **Starting fresh with React?** → **Radix** — lightest weight, zero peer deps, full feature parity.
|
|
185
|
+
- **Microsoft ecosystem?** → **Fluent** — native look in Teams, SharePoint, Outlook.
|
|
186
|
+
- **Already using MUI?** → **Material** — matches your existing theme and components.
|
|
187
|
+
- **Starting fresh with Angular?** → **Radix (CDK)** — lightest weight with Angular CDK for overlays.
|
|
188
|
+
- **Already using Angular Material?** → **Angular Material** — matches your existing theme.
|
|
189
|
+
- **Already using PrimeNG?** → **PrimeNG** — integrates with PrimeFaces ecosystem.
|
|
190
|
+
- **Starting fresh with Vue?** → **Radix (Headless UI)** — lightest weight, zero peer deps, Composition API.
|
|
191
|
+
- **Already using Vuetify?** → **Vuetify** — matches your existing theme and components.
|
|
192
|
+
- **Already using PrimeVue?** → **PrimeVue** — integrates with PrimeFaces ecosystem.
|
|
193
|
+
- **No framework?** → **Vanilla JS** — works anywhere, class-based API, CSS-only theming.
|
|
194
|
+
|
|
195
|
+
## Related
|
|
196
|
+
|
|
197
|
+
- [Installation](../getting-started/installation) — install instructions for all packages
|
|
198
|
+
- [Quick Start](../getting-started/quick-start) — build your first grid
|
|
199
|
+
- [Theming Guide](./theming) — customize colors and styling
|
|
200
|
+
- [Vanilla JS Guide](../getting-started/vanilla-js) — full JS API documentation
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: MCP Live Testing Bridge
|
|
3
|
+
description: Connect AI assistants (Claude, Cursor) to a running OGrid instance for real-time testing and inspection.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# MCP Live Testing Bridge
|
|
8
|
+
|
|
9
|
+
The `@alaarab/ogrid-mcp` package includes a **live testing bridge** that lets AI assistants connected via MCP read your grid's current state, update cell values, apply filters, and navigate pages — all in real time while your dev app is running.
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Claude (MCP client)
|
|
15
|
+
│ MCP tools
|
|
16
|
+
▼
|
|
17
|
+
ogrid-mcp (stdio process + HTTP bridge on :7890)
|
|
18
|
+
│ HTTP polling every 500ms
|
|
19
|
+
▼
|
|
20
|
+
Your dev app (localhost:3001)
|
|
21
|
+
└── connectGridToBridge() — pushes state + handles commands
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The bridge is **opt-in** and **dev-only** — it only runs when you explicitly start the MCP server with the `--bridge` flag or `OGRID_BRIDGE_PORT` env var.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
### 1. Start the MCP server with bridge enabled
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Option A: use --bridge flag (port 7890)
|
|
34
|
+
npx @alaarab/ogrid-mcp --bridge
|
|
35
|
+
|
|
36
|
+
# Option B: use env var (custom port)
|
|
37
|
+
OGRID_BRIDGE_PORT=7890 npx @alaarab/ogrid-mcp
|
|
38
|
+
|
|
39
|
+
# Claude Desktop / claude mcp add
|
|
40
|
+
claude mcp add ogrid -- npx -y @alaarab/ogrid-mcp --bridge
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Connect your dev app
|
|
44
|
+
|
|
45
|
+
<Tabs groupId="framework">
|
|
46
|
+
<TabItem value="react" label="React">
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
|
|
50
|
+
export function MyGrid() {
|
|
51
|
+
const [data, setData] = useState(myRows);
|
|
52
|
+
const apiRef = useRef<IOGridApi | null>(null);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
// Only connect in development
|
|
56
|
+
if (process.env.NODE_ENV !== 'development') return;
|
|
57
|
+
|
|
58
|
+
const bridge = connectGridToBridge({
|
|
59
|
+
gridId: 'my-grid',
|
|
60
|
+
getData: () => data,
|
|
61
|
+
getColumns: () => columns,
|
|
62
|
+
api: apiRef.current ?? undefined,
|
|
63
|
+
// Handle update_cell commands from Claude
|
|
64
|
+
onCellUpdate: (rowIndex, columnId, value) => {
|
|
65
|
+
setData(prev =>
|
|
66
|
+
prev.map((row, i) => i === rowIndex ? { ...row, [columnId]: value } : row)
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return () => bridge.disconnect();
|
|
72
|
+
}, [data]);
|
|
73
|
+
|
|
74
|
+
return <OGrid ref={apiRef} data={data} columns={columns} />;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
</TabItem>
|
|
79
|
+
<TabItem value="angular" label="Angular">
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
|
|
83
|
+
@Component({ /* ... */ })
|
|
84
|
+
export class MyGridComponent implements OnInit, OnDestroy {
|
|
85
|
+
data = myRows;
|
|
86
|
+
columns = myColumns;
|
|
87
|
+
service = new OGridService();
|
|
88
|
+
private bridge?: BridgeConnection;
|
|
89
|
+
|
|
90
|
+
ngOnInit() {
|
|
91
|
+
if (!isDevMode()) return;
|
|
92
|
+
this.bridge = connectGridToBridge({
|
|
93
|
+
gridId: 'my-grid',
|
|
94
|
+
getData: () => this.data,
|
|
95
|
+
getColumns: () => this.columns,
|
|
96
|
+
onCellUpdate: (rowIndex, columnId, value) => {
|
|
97
|
+
this.data = this.data.map((row, i) =>
|
|
98
|
+
i === rowIndex ? { ...row, [columnId]: value } : row
|
|
99
|
+
);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
ngOnDestroy() {
|
|
105
|
+
this.bridge?.disconnect();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
</TabItem>
|
|
111
|
+
<TabItem value="vue" label="Vue">
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<script setup lang="ts">
|
|
115
|
+
|
|
116
|
+
const data = ref(myRows);
|
|
117
|
+
let bridge: ReturnType<typeof connectGridToBridge> | null = null;
|
|
118
|
+
|
|
119
|
+
onMounted(() => {
|
|
120
|
+
if (import.meta.env.DEV) {
|
|
121
|
+
bridge = connectGridToBridge({
|
|
122
|
+
gridId: 'my-grid',
|
|
123
|
+
getData: () => data.value,
|
|
124
|
+
getColumns: () => columns,
|
|
125
|
+
onCellUpdate: (rowIndex, columnId, value) => {
|
|
126
|
+
data.value = data.value.map((row, i) =>
|
|
127
|
+
i === rowIndex ? { ...row, [columnId]: value } : row
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
onUnmounted(() => bridge?.disconnect());
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
</TabItem>
|
|
139
|
+
<TabItem value="js" label="Vanilla JS">
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
|
|
143
|
+
const grid = new OGrid(container, { data: myRows, columns });
|
|
144
|
+
|
|
145
|
+
// Only in development
|
|
146
|
+
if (location.hostname === 'localhost') {
|
|
147
|
+
const bridge = connectGridToBridge({
|
|
148
|
+
gridId: 'my-grid',
|
|
149
|
+
getData: () => grid.getApi().getData(),
|
|
150
|
+
getColumns: () => columns,
|
|
151
|
+
onCellUpdate: (rowIndex, columnId, value) => {
|
|
152
|
+
// Update your data source and refresh
|
|
153
|
+
myRows[rowIndex][columnId] = value;
|
|
154
|
+
grid.getApi().refresh();
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
</TabItem>
|
|
161
|
+
</Tabs>
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## MCP tools
|
|
166
|
+
|
|
167
|
+
Once connected, these tools are available to your AI assistant:
|
|
168
|
+
|
|
169
|
+
### `list_grids`
|
|
170
|
+
|
|
171
|
+
Lists all OGrid instances currently connected to the bridge.
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
> list_grids
|
|
175
|
+
|
|
176
|
+
2 connected grid(s):
|
|
177
|
+
|
|
178
|
+
**my-grid**
|
|
179
|
+
Rows: 50 displayed / 1247 total
|
|
180
|
+
Page: 1 / 25 (50 per page)
|
|
181
|
+
Columns: id, name, email, department, salary
|
|
182
|
+
Active filters: 0
|
|
183
|
+
Last seen: 0s ago
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### `get_grid_state`
|
|
187
|
+
|
|
188
|
+
Returns the current state of a grid: columns, pagination, sort, filters, selection, and optionally the row data.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
> get_grid_state gridId="my-grid" includeData=true maxRows=5
|
|
192
|
+
|
|
193
|
+
# Grid: my-grid
|
|
194
|
+
Last seen: 0s ago
|
|
195
|
+
|
|
196
|
+
## Pagination
|
|
197
|
+
Page 1 of 25 | 50 rows displayed | 1247 total
|
|
198
|
+
|
|
199
|
+
## Columns (5)
|
|
200
|
+
id (numeric), name (text), email (text), department (text), salary (numeric)
|
|
201
|
+
|
|
202
|
+
## Sort
|
|
203
|
+
salary desc
|
|
204
|
+
|
|
205
|
+
## Data (first 5 of 50 rows)
|
|
206
|
+
[
|
|
207
|
+
{ "id": 1, "name": "Alice", "salary": 120000 },
|
|
208
|
+
...
|
|
209
|
+
]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### `send_grid_command`
|
|
213
|
+
|
|
214
|
+
Sends a command to a connected grid and waits for the result.
|
|
215
|
+
|
|
216
|
+
**Update a cell:**
|
|
217
|
+
```
|
|
218
|
+
> send_grid_command gridId="my-grid" type="update_cell"
|
|
219
|
+
payload={ "rowIndex": 0, "columnId": "salary", "value": 130000 }
|
|
220
|
+
|
|
221
|
+
✅ Command executed successfully
|
|
222
|
+
Type: update_cell
|
|
223
|
+
Payload: {"rowIndex":0,"columnId":"salary","value":130000}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Apply a filter:**
|
|
227
|
+
```
|
|
228
|
+
> send_grid_command gridId="my-grid" type="set_filter"
|
|
229
|
+
payload={ "columnId": "department", "value": "Engineering" }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Clear all filters:**
|
|
233
|
+
```
|
|
234
|
+
> send_grid_command gridId="my-grid" type="clear_filters" payload={}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Change sort:**
|
|
238
|
+
```
|
|
239
|
+
> send_grid_command gridId="my-grid" type="set_sort"
|
|
240
|
+
payload={ "sortModel": [{ "columnId": "name", "direction": "asc" }] }
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Navigate to page:**
|
|
244
|
+
```
|
|
245
|
+
> send_grid_command gridId="my-grid" type="go_to_page" payload={ "page": 3 }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## connectGridToBridge() options
|
|
251
|
+
|
|
252
|
+
| Option | Type | Description |
|
|
253
|
+
|--------|------|-------------|
|
|
254
|
+
| `gridId` | `string` | Unique ID shown in `list_grids` |
|
|
255
|
+
| `getData` | `() => unknown[]` | Returns current displayed rows |
|
|
256
|
+
| `getColumns` | `() => BridgeColumnInfo[]` | Returns current columns |
|
|
257
|
+
| `getPagination` | `() => { page, pageSize, totalCount, pageCount }` | (optional) Pagination state |
|
|
258
|
+
| `api` | `BridgeGridApi` | (optional) Grid API for filter/sort/page commands |
|
|
259
|
+
| `onCellUpdate` | `(rowIndex, columnId, value) => void` | (optional) Handle `update_cell` commands |
|
|
260
|
+
| `bridgeUrl` | `string` | Bridge URL (default: `http://localhost:7890`) |
|
|
261
|
+
| `pollIntervalMs` | `number` | Poll frequency in ms (default: `500`) |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Example AI testing session
|
|
266
|
+
|
|
267
|
+
Here's what a typical Claude testing session looks like with the bridge active:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
Me: The salary column seems wrong. Can you check?
|
|
271
|
+
|
|
272
|
+
Claude: Let me look at the current grid state.
|
|
273
|
+
[calls get_grid_state gridId="employees" includeData=true maxRows=10]
|
|
274
|
+
|
|
275
|
+
The grid shows salary values like "120" instead of "120000".
|
|
276
|
+
It looks like the values are being divided by 1000 somewhere.
|
|
277
|
+
|
|
278
|
+
Let me update a cell to verify:
|
|
279
|
+
[calls send_grid_command type="update_cell" payload={rowIndex:0, columnId:"salary", value:120000}]
|
|
280
|
+
✅ Done. Now let me re-read to confirm:
|
|
281
|
+
[calls get_grid_state]
|
|
282
|
+
|
|
283
|
+
The update worked. Row 0 now shows 120000. The issue is in your
|
|
284
|
+
valueGetter — it's dividing by 1000. Here's the fix: ...
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Security note
|
|
290
|
+
|
|
291
|
+
The bridge server only listens on `127.0.0.1` (localhost) and is designed for local development only. **Never run with `--bridge` in production.** The bridge has no authentication — anyone on localhost can read your grid data and send commands.
|