@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,290 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 10
|
|
3
|
+
title: Column Groups
|
|
4
|
+
description: Multi-row grouped column headers with arbitrary nesting
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Column Groups
|
|
9
|
+
|
|
10
|
+
Group related columns under shared parent headers. OGrid supports arbitrarily nested column groups that render as multi-row `<thead>` headers.
|
|
11
|
+
|
|
12
|
+
## Live Demo
|
|
13
|
+
|
|
14
|
+
<ColumnGroupsDemo />
|
|
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 Person {
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
age: number;
|
|
31
|
+
email: string;
|
|
32
|
+
department: string;
|
|
33
|
+
salary: number;
|
|
34
|
+
status: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const columns: (IColumnDef<Person> | IColumnGroupDef<Person>)[] = [
|
|
38
|
+
{
|
|
39
|
+
headerName: 'Personal Info',
|
|
40
|
+
children: [
|
|
41
|
+
{ columnId: 'name', name: 'Name' },
|
|
42
|
+
{ columnId: 'age', name: 'Age', type: 'numeric' },
|
|
43
|
+
{ columnId: 'email', name: 'Email' },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
headerName: 'Employment',
|
|
48
|
+
children: [
|
|
49
|
+
{ columnId: 'department', name: 'Department' },
|
|
50
|
+
{
|
|
51
|
+
columnId: 'salary',
|
|
52
|
+
name: 'Salary',
|
|
53
|
+
type: 'numeric',
|
|
54
|
+
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
|
|
55
|
+
},
|
|
56
|
+
{ columnId: 'status', name: 'Status' },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
function App() {
|
|
62
|
+
return (
|
|
63
|
+
<OGrid
|
|
64
|
+
columns={columns}
|
|
65
|
+
data={people}
|
|
66
|
+
getRowId={(p) => p.id}
|
|
67
|
+
defaultPageSize={10}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
:::tip Switching UI libraries
|
|
74
|
+
The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
|
|
75
|
+
|
|
76
|
+
- **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
|
|
77
|
+
- **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
|
|
78
|
+
- **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
|
|
79
|
+
:::
|
|
80
|
+
|
|
81
|
+
</TabItem>
|
|
82
|
+
<TabItem value="angular" label="Angular">
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
|
|
86
|
+
@Component({
|
|
87
|
+
standalone: true,
|
|
88
|
+
imports: [OGridComponent],
|
|
89
|
+
template: `<ogrid [props]="gridProps" />`
|
|
90
|
+
})
|
|
91
|
+
export class GridComponent {
|
|
92
|
+
gridProps = {
|
|
93
|
+
columns: [
|
|
94
|
+
{
|
|
95
|
+
headerName: 'Personal Info',
|
|
96
|
+
children: [
|
|
97
|
+
{ columnId: 'name', name: 'Name' },
|
|
98
|
+
{ columnId: 'age', name: 'Age', type: 'numeric' },
|
|
99
|
+
{ columnId: 'email', name: 'Email' },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
headerName: 'Employment',
|
|
104
|
+
children: [
|
|
105
|
+
{ columnId: 'department', name: 'Department' },
|
|
106
|
+
{
|
|
107
|
+
columnId: 'salary', name: 'Salary', type: 'numeric',
|
|
108
|
+
valueFormatter: (v: unknown) => `$${Number(v).toLocaleString()}`,
|
|
109
|
+
},
|
|
110
|
+
{ columnId: 'status', name: 'Status' },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
data: people,
|
|
115
|
+
getRowId: (item: Person) => item.id,
|
|
116
|
+
defaultPageSize: 10,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
:::tip Switching UI libraries
|
|
122
|
+
Same component API across Angular packages. To switch, just change the import:
|
|
123
|
+
|
|
124
|
+
- **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
|
|
125
|
+
- **Angular Material**: `from '@alaarab/ogrid-angular-material'`
|
|
126
|
+
- **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
|
|
127
|
+
|
|
128
|
+
All components are standalone — no NgModule required.
|
|
129
|
+
:::
|
|
130
|
+
|
|
131
|
+
</TabItem>
|
|
132
|
+
<TabItem value="vue" label="Vue">
|
|
133
|
+
|
|
134
|
+
```vue
|
|
135
|
+
<script setup lang="ts">
|
|
136
|
+
|
|
137
|
+
const columns = [
|
|
138
|
+
{
|
|
139
|
+
headerName: 'Personal Info',
|
|
140
|
+
children: [
|
|
141
|
+
{ columnId: 'name', name: 'Name' },
|
|
142
|
+
{ columnId: 'age', name: 'Age', type: 'numeric' },
|
|
143
|
+
{ columnId: 'email', name: 'Email' },
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
headerName: 'Employment',
|
|
148
|
+
children: [
|
|
149
|
+
{ columnId: 'department', name: 'Department' },
|
|
150
|
+
{
|
|
151
|
+
columnId: 'salary', name: 'Salary', type: 'numeric',
|
|
152
|
+
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
|
|
153
|
+
},
|
|
154
|
+
{ columnId: 'status', name: 'Status' },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const gridProps = {
|
|
160
|
+
columns,
|
|
161
|
+
data: people,
|
|
162
|
+
getRowId: (item) => item.id,
|
|
163
|
+
defaultPageSize: 10,
|
|
164
|
+
};
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<template>
|
|
168
|
+
<OGrid :gridProps="gridProps" />
|
|
169
|
+
</template>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
:::tip Switching UI libraries
|
|
173
|
+
Same component API across Vue packages. To switch, just change the import:
|
|
174
|
+
|
|
175
|
+
- **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
|
|
176
|
+
- **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
|
|
177
|
+
- **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
|
|
178
|
+
:::
|
|
179
|
+
|
|
180
|
+
</TabItem>
|
|
181
|
+
<TabItem value="js" label="Vanilla JS">
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
|
|
185
|
+
const grid = new OGrid(document.getElementById('grid'), {
|
|
186
|
+
columns: [
|
|
187
|
+
{
|
|
188
|
+
headerName: 'Personal Info',
|
|
189
|
+
children: [
|
|
190
|
+
{ columnId: 'name', name: 'Name' },
|
|
191
|
+
{ columnId: 'age', name: 'Age', type: 'numeric' },
|
|
192
|
+
{ columnId: 'email', name: 'Email' },
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
headerName: 'Employment',
|
|
197
|
+
children: [
|
|
198
|
+
{ columnId: 'department', name: 'Department' },
|
|
199
|
+
{
|
|
200
|
+
columnId: 'salary',
|
|
201
|
+
name: 'Salary',
|
|
202
|
+
type: 'numeric',
|
|
203
|
+
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
|
|
204
|
+
},
|
|
205
|
+
{ columnId: 'status', name: 'Status' },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
data: people,
|
|
210
|
+
getRowId: (p) => p.id,
|
|
211
|
+
pageSize: 10,
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
</TabItem>
|
|
216
|
+
</Tabs>
|
|
217
|
+
|
|
218
|
+
This renders a two-row header: the first row shows "Personal Info" spanning three columns and "Employment" spanning three columns, with the individual column names in the second row.
|
|
219
|
+
|
|
220
|
+
## How It Works
|
|
221
|
+
|
|
222
|
+
Column groups use the `IColumnGroupDef<T>` type. A group has a `headerName` (the text displayed in the group header cell) and a `children` array that can contain either `IColumnDef` items or nested `IColumnGroupDef` items.
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
// IColumnGroupDef<T>
|
|
226
|
+
{
|
|
227
|
+
headerName: string;
|
|
228
|
+
children: (IColumnGroupDef<T> | IColumnDef<T>)[];
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
When the `columns` prop contains groups, OGrid internally calls `buildHeaderRows()` to produce a multi-row header model. Each group header cell spans the correct number of leaf columns via `colSpan`.
|
|
233
|
+
|
|
234
|
+
### Nested Groups
|
|
235
|
+
|
|
236
|
+
Groups can be nested to any depth. Each level adds another header row.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
const columns: (IColumnDef<Row> | IColumnGroupDef<Row>)[] = [
|
|
240
|
+
{
|
|
241
|
+
headerName: 'Contact',
|
|
242
|
+
children: [
|
|
243
|
+
{
|
|
244
|
+
headerName: 'Name',
|
|
245
|
+
children: [
|
|
246
|
+
{ columnId: 'firstName', name: 'First' },
|
|
247
|
+
{ columnId: 'lastName', name: 'Last' },
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
{ columnId: 'email', name: 'Email' },
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
{ columnId: 'role', name: 'Role' },
|
|
254
|
+
];
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
This produces three header rows:
|
|
258
|
+
|
|
259
|
+
| Contact (span 3) | Role (span 1, rowSpan 3) |
|
|
260
|
+
|---|---|
|
|
261
|
+
| Name (span 2) | Email (rowSpan 2) | |
|
|
262
|
+
| First | Last | | |
|
|
263
|
+
|
|
264
|
+
### Mixing Groups and Standalone Columns
|
|
265
|
+
|
|
266
|
+
Standalone `IColumnDef` items at the top level are valid alongside groups. They span all header rows vertically.
|
|
267
|
+
|
|
268
|
+
## Group Header Behavior
|
|
269
|
+
|
|
270
|
+
Group headers are display-only:
|
|
271
|
+
|
|
272
|
+
- **Not sortable.** Clicking a group header does nothing.
|
|
273
|
+
- **Not filterable.** No filter icon or popover appears on group headers.
|
|
274
|
+
- **Not resizable.** Group headers cannot be dragged to resize.
|
|
275
|
+
- **Centered text.** Group header labels are centered over their children by default.
|
|
276
|
+
|
|
277
|
+
Sorting, filtering, and resizing remain available on the leaf column headers beneath the groups.
|
|
278
|
+
|
|
279
|
+
## Props Reference
|
|
280
|
+
|
|
281
|
+
| Type | Field | Description |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| `IColumnGroupDef<T>` | `headerName` | Display text for the group header |
|
|
284
|
+
| `IColumnGroupDef<T>` | `children` | Array of child columns or nested groups |
|
|
285
|
+
| `IOGridProps<T>` | `columns` | Accepts `(IColumnDef<T> \| IColumnGroupDef<T>)[]` |
|
|
286
|
+
|
|
287
|
+
## Related
|
|
288
|
+
|
|
289
|
+
- [Column Pinning](./column-pinning) -- pin grouped columns to the left edge
|
|
290
|
+
- [Column Chooser](./column-chooser) -- show/hide columns within groups
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 11
|
|
3
|
+
title: Column Pinning
|
|
4
|
+
description: Pin columns to the left edge so they stay visible during horizontal scroll
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Column Pinning
|
|
9
|
+
|
|
10
|
+
Pin columns to the left edge of the grid so they remain visible while the user scrolls horizontally through the remaining columns.
|
|
11
|
+
|
|
12
|
+
## Live Demo
|
|
13
|
+
|
|
14
|
+
<ColumnPinningDemo />
|
|
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 Person {
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
email: string;
|
|
31
|
+
department: string;
|
|
32
|
+
salary: number;
|
|
33
|
+
status: string;
|
|
34
|
+
age: number;
|
|
35
|
+
startDate: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const columns: IColumnDef<Person>[] = [
|
|
39
|
+
{ columnId: 'name', name: 'Name', pinned: 'left', defaultWidth: 160 },
|
|
40
|
+
{ columnId: 'email', name: 'Email', defaultWidth: 220 },
|
|
41
|
+
{ columnId: 'department', name: 'Department', defaultWidth: 160 },
|
|
42
|
+
{
|
|
43
|
+
columnId: 'salary',
|
|
44
|
+
name: 'Salary',
|
|
45
|
+
type: 'numeric',
|
|
46
|
+
defaultWidth: 120,
|
|
47
|
+
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
|
|
48
|
+
},
|
|
49
|
+
{ columnId: 'status', name: 'Status', defaultWidth: 120 },
|
|
50
|
+
{ columnId: 'age', name: 'Age', type: 'numeric', defaultWidth: 80 },
|
|
51
|
+
{ columnId: 'startDate', name: 'Start Date', defaultWidth: 130 },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<OGrid
|
|
57
|
+
columns={columns}
|
|
58
|
+
data={people}
|
|
59
|
+
getRowId={(p) => p.id}
|
|
60
|
+
defaultPageSize={10}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
:::tip Switching UI libraries
|
|
67
|
+
The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
|
|
68
|
+
|
|
69
|
+
- **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
|
|
70
|
+
- **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` — wrap in `<FluentProvider>`
|
|
71
|
+
- **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` — wrap in `<ThemeProvider>`
|
|
72
|
+
:::
|
|
73
|
+
|
|
74
|
+
</TabItem>
|
|
75
|
+
<TabItem value="angular" label="Angular">
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
|
|
79
|
+
@Component({
|
|
80
|
+
standalone: true,
|
|
81
|
+
imports: [OGridComponent],
|
|
82
|
+
template: `<ogrid [props]="gridProps" />`
|
|
83
|
+
})
|
|
84
|
+
export class GridComponent {
|
|
85
|
+
gridProps = {
|
|
86
|
+
columns: [
|
|
87
|
+
{ columnId: 'name', name: 'Name', pinned: 'left', defaultWidth: 160 },
|
|
88
|
+
{ columnId: 'email', name: 'Email', defaultWidth: 220 },
|
|
89
|
+
{ columnId: 'department', name: 'Department', defaultWidth: 160 },
|
|
90
|
+
{
|
|
91
|
+
columnId: 'salary', name: 'Salary', type: 'numeric', defaultWidth: 120,
|
|
92
|
+
valueFormatter: (v: unknown) => `$${Number(v).toLocaleString()}`,
|
|
93
|
+
},
|
|
94
|
+
{ columnId: 'status', name: 'Status', defaultWidth: 120 },
|
|
95
|
+
{ columnId: 'age', name: 'Age', type: 'numeric', defaultWidth: 80 },
|
|
96
|
+
{ columnId: 'startDate', name: 'Start Date', defaultWidth: 130 },
|
|
97
|
+
] as IColumnDef<Person>[],
|
|
98
|
+
data: people,
|
|
99
|
+
getRowId: (item: Person) => item.id,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
:::tip Switching UI libraries
|
|
105
|
+
Same component API across Angular packages. To switch, just change the import:
|
|
106
|
+
|
|
107
|
+
- **Radix (CDK)**: `from '@alaarab/ogrid-angular-radix'` *(default, lightweight)*
|
|
108
|
+
- **Angular Material**: `from '@alaarab/ogrid-angular-material'`
|
|
109
|
+
- **PrimeNG**: `from '@alaarab/ogrid-angular-primeng'`
|
|
110
|
+
|
|
111
|
+
All components are standalone — no NgModule required.
|
|
112
|
+
:::
|
|
113
|
+
|
|
114
|
+
</TabItem>
|
|
115
|
+
<TabItem value="vue" label="Vue">
|
|
116
|
+
|
|
117
|
+
```vue
|
|
118
|
+
<script setup lang="ts">
|
|
119
|
+
|
|
120
|
+
const columns: IColumnDef<Person>[] = [
|
|
121
|
+
{ columnId: 'name', name: 'Name', pinned: 'left', defaultWidth: 160 },
|
|
122
|
+
{ columnId: 'email', name: 'Email', defaultWidth: 220 },
|
|
123
|
+
{ columnId: 'department', name: 'Department', defaultWidth: 160 },
|
|
124
|
+
{
|
|
125
|
+
columnId: 'salary', name: 'Salary', type: 'numeric', defaultWidth: 120,
|
|
126
|
+
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
|
|
127
|
+
},
|
|
128
|
+
{ columnId: 'status', name: 'Status', defaultWidth: 120 },
|
|
129
|
+
{ columnId: 'age', name: 'Age', type: 'numeric', defaultWidth: 80 },
|
|
130
|
+
{ columnId: 'startDate', name: 'Start Date', defaultWidth: 130 },
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const gridProps = {
|
|
134
|
+
columns,
|
|
135
|
+
data: people,
|
|
136
|
+
getRowId: (item) => item.id,
|
|
137
|
+
};
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<template>
|
|
141
|
+
<OGrid :gridProps="gridProps" />
|
|
142
|
+
</template>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
:::tip Switching UI libraries
|
|
146
|
+
Same component API across Vue packages. To switch, just change the import:
|
|
147
|
+
|
|
148
|
+
- **Radix (Headless UI)**: `from '@alaarab/ogrid-vue-radix'` *(default, lightweight)*
|
|
149
|
+
- **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` — wrap in `<v-app>` for theming
|
|
150
|
+
- **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
|
|
151
|
+
:::
|
|
152
|
+
|
|
153
|
+
</TabItem>
|
|
154
|
+
<TabItem value="js" label="Vanilla JS">
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
|
|
158
|
+
const grid = new OGrid(document.getElementById('grid'), {
|
|
159
|
+
columns: [
|
|
160
|
+
{ columnId: 'name', name: 'Name', pinned: 'left', defaultWidth: 160 },
|
|
161
|
+
{ columnId: 'email', name: 'Email', defaultWidth: 220 },
|
|
162
|
+
{ columnId: 'department', name: 'Department', defaultWidth: 160 },
|
|
163
|
+
{
|
|
164
|
+
columnId: 'salary',
|
|
165
|
+
name: 'Salary',
|
|
166
|
+
type: 'numeric',
|
|
167
|
+
defaultWidth: 120,
|
|
168
|
+
valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
|
|
169
|
+
},
|
|
170
|
+
{ columnId: 'status', name: 'Status', defaultWidth: 120 },
|
|
171
|
+
{ columnId: 'age', name: 'Age', type: 'numeric', defaultWidth: 80 },
|
|
172
|
+
{ columnId: 'startDate', name: 'Start Date', defaultWidth: 130 },
|
|
173
|
+
],
|
|
174
|
+
data: people,
|
|
175
|
+
getRowId: (p) => p.id,
|
|
176
|
+
pageSize: 10,
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
</TabItem>
|
|
181
|
+
</Tabs>
|
|
182
|
+
|
|
183
|
+
The "Name" column stays fixed on the left while all other columns scroll normally.
|
|
184
|
+
|
|
185
|
+
## How It Works
|
|
186
|
+
|
|
187
|
+
Set `pinned: 'left'` on any `IColumnDef` to pin it. The grid renders pinned columns with CSS `position: sticky` and a calculated `left` offset so multiple pinned columns stack correctly.
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
{ columnId: 'name', name: 'Name', pinned: 'left' }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Pinned columns:
|
|
194
|
+
|
|
195
|
+
- Use **sticky positioning** so they stay visible during horizontal scroll.
|
|
196
|
+
- Stack left-to-right in the order they appear in the `columns` array.
|
|
197
|
+
- Work with all grid features: sorting, filtering, editing, cell selection, and context menu.
|
|
198
|
+
|
|
199
|
+
### With Row Selection
|
|
200
|
+
|
|
201
|
+
When `rowSelection` is `'single'` or `'multiple'`, OGrid inserts a checkbox column at the far left. Pinned columns appear after the checkbox column, and both stay visible during scroll.
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
<OGrid
|
|
205
|
+
columns={columns}
|
|
206
|
+
data={rows}
|
|
207
|
+
getRowId={(r) => r.id}
|
|
208
|
+
rowSelection="multiple"
|
|
209
|
+
/>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### With Column Groups
|
|
213
|
+
|
|
214
|
+
Pinning works inside column groups. Set `pinned: 'left'` on the leaf `IColumnDef` items within a group. The group header will span correctly over the pinned columns.
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
const columns = [
|
|
218
|
+
{
|
|
219
|
+
headerName: 'Identity',
|
|
220
|
+
children: [
|
|
221
|
+
{ columnId: 'id', name: 'ID', pinned: 'left' },
|
|
222
|
+
{ columnId: 'name', name: 'Name', pinned: 'left' },
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
{ columnId: 'email', name: 'Email' },
|
|
226
|
+
];
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Persisting Pin State
|
|
230
|
+
|
|
231
|
+
Pin state can be saved and restored via the Grid API, just like column widths and sort order:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
const gridRef = useRef<IOGridApi<Row>>(null);
|
|
235
|
+
|
|
236
|
+
// Save pin state (e.g. to localStorage)
|
|
237
|
+
const state = gridRef.current.getColumnState();
|
|
238
|
+
// state.pinnedColumns → { name: 'left' }
|
|
239
|
+
localStorage.setItem('gridState', JSON.stringify(state));
|
|
240
|
+
|
|
241
|
+
// Restore pin state on mount
|
|
242
|
+
const saved = JSON.parse(localStorage.getItem('gridState'));
|
|
243
|
+
gridRef.current.applyColumnState(saved);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### `onColumnPinned` Callback
|
|
247
|
+
|
|
248
|
+
Listen for pin changes to persist state automatically:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<OGrid
|
|
252
|
+
columns={columns}
|
|
253
|
+
data={rows}
|
|
254
|
+
getRowId={(r) => r.id}
|
|
255
|
+
ref={gridRef}
|
|
256
|
+
onColumnPinned={(columnId, pinned) => {
|
|
257
|
+
// pinned is 'left', 'right', or null (unpinned)
|
|
258
|
+
const state = gridRef.current.getColumnState();
|
|
259
|
+
localStorage.setItem('gridState', JSON.stringify(state));
|
|
260
|
+
}}
|
|
261
|
+
/>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Props Reference
|
|
265
|
+
|
|
266
|
+
| Type | Field | Values | Description |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| `IColumnMeta` | `pinned` | `'left' \| 'right'` | Pin column to left or right edge |
|
|
269
|
+
| `IOGridProps` | `onColumnPinned` | `(columnId, pinned) => void` | Called when a column is pinned or unpinned |
|
|
270
|
+
| `IGridColumnState` | `pinnedColumns` | `Record<string, 'left' \| 'right'>` | Pinned columns in saved state |
|
|
271
|
+
| `IColumnMeta` | `minWidth` | `number` | Minimum width in pixels (useful for pinned columns) |
|
|
272
|
+
| `IColumnMeta` | `defaultWidth` | `number` | Initial width in pixels |
|
|
273
|
+
|
|
274
|
+
:::tip
|
|
275
|
+
Pin identifying columns like "Name" or "ID" so users always know which row they are looking at, even when scrolling through many columns.
|
|
276
|
+
:::
|
|
277
|
+
|
|
278
|
+
## Related
|
|
279
|
+
|
|
280
|
+
- [Column Groups](./column-groups) -- group headers with pinned leaf columns
|
|
281
|
+
- [Column Chooser](./column-chooser) -- show/hide columns (including pinned ones)
|
|
282
|
+
- [Keyboard Navigation](./keyboard-navigation) -- arrow keys work across pinned and scrollable columns
|