@opencorestack/opengridx 1.0.2 → 1.0.4
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/CHANGELOG.md +14 -0
- package/dist/opengridx.es.js +616 -610
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +5 -5
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +743 -0
- package/lib/components/DataGrid/DataGrid.tsx +15 -3
- package/package.json +1 -1
package/docs/API_REFERENCE.md
CHANGED
|
@@ -39,6 +39,129 @@ The main component for displaying and interacting with data.
|
|
|
39
39
|
| `processRowUpdate` | `(new, old) => R \| Promise<R>` | — | Fired after a cell edit is committed. |
|
|
40
40
|
| `dataSource` | `GridDataSource` | — | Remote data provider interface. |
|
|
41
41
|
|
|
42
|
+
#### Sorting, Filtering & Pagination
|
|
43
|
+
|
|
44
|
+
| Prop | Type | Default | Description |
|
|
45
|
+
| :--- | :--- | :--- | :--- |
|
|
46
|
+
| `sortingMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles sorting. The grid calls `onSortModelChange` but does not re-sort rows locally. |
|
|
47
|
+
| `filterMode` | `'client' \| 'server'` | `'client'` | Set to `'server'` when your backend handles filtering. The grid calls `onFilterModelChange` but does not re-filter rows locally. |
|
|
48
|
+
| `onSortModelChange` | `(model: GridSortItem[]) => void` | — | Fired when the active sort model changes. |
|
|
49
|
+
| `onFilterModelChange` | `(model: GridFilterModel) => void` | — | Fired when the active filter model changes. |
|
|
50
|
+
|
|
51
|
+
#### Row Selection
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Default | Description |
|
|
54
|
+
| :--- | :--- | :--- | :--- |
|
|
55
|
+
| `rowSelectionModel` | `GridRowSelectionModel` | `[]` | Controlled selection state — array of selected row IDs. |
|
|
56
|
+
| `onRowSelectionModelChange` | `(model: GridRowSelectionModel) => void` | — | Fired when the selection changes. |
|
|
57
|
+
| `disableRowSelectionOnClick` | `boolean` | `false` | When `true`, clicking a row does not toggle its selection. |
|
|
58
|
+
| `disableMultipleRowSelection` | `boolean` | `false` | When `true`, at most one row can be selected at a time. |
|
|
59
|
+
|
|
60
|
+
#### Column Visibility
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| :--- | :--- | :--- | :--- |
|
|
64
|
+
| `columnVisibilityModel` | `GridColumnVisibilityModel` | — | Map of `field → boolean` controlling which columns are visible (`false` = hidden). |
|
|
65
|
+
| `onColumnVisibilityModelChange` | `(model: GridColumnVisibilityModel) => void` | — | Fired when the visibility of any column changes. |
|
|
66
|
+
|
|
67
|
+
#### Column & Row Reordering
|
|
68
|
+
|
|
69
|
+
| Prop | Type | Default | Description |
|
|
70
|
+
| :--- | :--- | :--- | :--- |
|
|
71
|
+
| `disableColumnReorder` | `boolean` | `false` | Disables drag-and-drop column reordering. |
|
|
72
|
+
| `columnOrder` | `GridColumnOrder` | — | Controlled ordered array of column field names. |
|
|
73
|
+
| `onColumnOrderChange` | `(params: GridColumnOrderChangeParams) => void` | — | Fired after a column is dragged to a new position. |
|
|
74
|
+
| `rowReordering` | `boolean` | `false` | Enables drag-and-drop row reordering. |
|
|
75
|
+
| `onRowOrderChange` | `(params: GridRowOrderChangeParams) => void` | — | Fired after a row is dragged to a new position. |
|
|
76
|
+
|
|
77
|
+
#### Pinning
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Default | Description |
|
|
80
|
+
| :--- | :--- | :--- | :--- |
|
|
81
|
+
| `pinnedColumns` | `GridColumnPinning` | — | Columns pinned to the left or right viewport edges. See [Column & Row Pinning](#-column--row-pinning). |
|
|
82
|
+
| `onPinnedColumnsChange` | `(model: GridColumnPinning) => void` | — | Fired when column pinning changes. |
|
|
83
|
+
| `pinnedRows` | `GridRowPinning` | — | Row IDs pinned to the top or bottom of the viewport. |
|
|
84
|
+
| `onPinnedRowsChange` | `(model: GridRowPinning) => void` | — | Fired when row pinning changes. |
|
|
85
|
+
| `pinCheckboxColumn` | `boolean` | `false` | Keeps the checkbox column visible during horizontal scrolling. |
|
|
86
|
+
| `pinExpandColumn` | `boolean` | `false` | Keeps the Master-Detail expansion column visible during horizontal scrolling. |
|
|
87
|
+
|
|
88
|
+
#### Inline Editing
|
|
89
|
+
|
|
90
|
+
| Prop | Type | Default | Description |
|
|
91
|
+
| :--- | :--- | :--- | :--- |
|
|
92
|
+
| `isCellEditable` | `(params: GridCellParams) => boolean` | — | Per-cell predicate. Return `false` to make a specific cell read-only even when the column has `editable: true`. |
|
|
93
|
+
| `onProcessRowUpdateError` | `(error: unknown) => void` | — | Fired if `processRowUpdate` throws or returns a rejected Promise. Use to display validation errors or restore the previous row value. |
|
|
94
|
+
|
|
95
|
+
#### Master-Detail
|
|
96
|
+
|
|
97
|
+
| Prop | Type | Default | Description |
|
|
98
|
+
| :--- | :--- | :--- | :--- |
|
|
99
|
+
| `getDetailPanelContent` | `(params: GridDetailPanelParams) => ReactNode` | — | Returns the JSX content rendered inside the expandable detail panel. |
|
|
100
|
+
| `getDetailPanelHeight` | `(params: GridDetailPanelParams) => number \| 'auto'` | `'auto'` | Controls the panel height in pixels, or `'auto'` to fit content. |
|
|
101
|
+
| `detailPanelExpandedRowIds` | `Set<GridRowId>` | — | Controlled set of currently-expanded detail panel row IDs. |
|
|
102
|
+
| `onDetailPanelExpandedRowIdsChange` | `(ids: Set<GridRowId>) => void` | — | Fired when detail panels expand or collapse. |
|
|
103
|
+
|
|
104
|
+
#### Tree Data
|
|
105
|
+
|
|
106
|
+
| Prop | Type | Default | Description |
|
|
107
|
+
| :--- | :--- | :--- | :--- |
|
|
108
|
+
| `treeData` | `boolean` | `false` | Enables hierarchical tree data display. |
|
|
109
|
+
| `getTreeDataPath` | `(row: R) => string[]` | — | Returns the hierarchy path for a row (e.g. `['Engineering', 'Frontend']`). |
|
|
110
|
+
| `groupingColDef` | `GridColDef` | — | Overrides the auto-generated tree expand/collapse column definition. |
|
|
111
|
+
| `defaultGroupingExpansionDepth` | `number` | `0` | Number of tree levels expanded on initial render (`-1` = all). |
|
|
112
|
+
|
|
113
|
+
#### Row Grouping & Aggregation
|
|
114
|
+
|
|
115
|
+
| Prop | Type | Default | Description |
|
|
116
|
+
| :--- | :--- | :--- | :--- |
|
|
117
|
+
| `rowGroupingModel` | `GridRowGroupingModel` | `[]` | Array of field names to group rows by (e.g. `['department', 'team']`). See [Row Grouping](#️-row-grouping). |
|
|
118
|
+
| `onRowGroupingModelChange` | `(model: GridRowGroupingModel) => void` | — | Fired when the grouping model changes. |
|
|
119
|
+
| `aggregationModel` | `GridAggregationModel` | — | Map of `field → aggFn` (e.g. `{ salary: 'sum', age: 'avg' }`). See [Aggregation Reference](#-aggregation-reference). |
|
|
120
|
+
| `onAggregationModelChange` | `(model: GridAggregationModel) => void` | — | Fired when the aggregation model changes. |
|
|
121
|
+
| `getAggregationPosition` | `(groupNode: GridTreeNode \| null) => 'inline' \| 'footer' \| null` | — | Controls where aggregation results appear. `'inline'` = inside the group row, `'footer'` = a dedicated row below the group, `null` = hidden. Pass `null` groupNode = grand-total (root) position. |
|
|
122
|
+
|
|
123
|
+
#### Pivot
|
|
124
|
+
|
|
125
|
+
| Prop | Type | Default | Description |
|
|
126
|
+
| :--- | :--- | :--- | :--- |
|
|
127
|
+
| `pivotMode` | `boolean` | `false` | Switches the grid to multidimensional Pivot Mode. |
|
|
128
|
+
| `pivotModel` | `GridPivotModel` | — | Controlled pivot configuration (row fields, column fields, value fields). |
|
|
129
|
+
| `onPivotModelChange` | `(model: GridPivotModel) => void` | — | Fired when the pivot model changes. |
|
|
130
|
+
|
|
131
|
+
#### Scroll & Viewport
|
|
132
|
+
|
|
133
|
+
| Prop | Type | Default | Description |
|
|
134
|
+
| :--- | :--- | :--- | :--- |
|
|
135
|
+
| `onRowsScrollEnd` | `(params: GridRowScrollEndParams) => void` | — | Fired when the user scrolls to the bottom of the grid viewport. Use this to trigger the next page in infinite-scroll mode. |
|
|
136
|
+
|
|
137
|
+
#### Accessibility & Appearance
|
|
138
|
+
|
|
139
|
+
| Prop | Type | Default | Description |
|
|
140
|
+
| :--- | :--- | :--- | :--- |
|
|
141
|
+
| `ariaLabel` | `string` | — | ARIA label for the grid container element. |
|
|
142
|
+
| `noRowsLabel` | `string` | `'No rows'` | Message shown in the empty-state overlay when there are no rows. |
|
|
143
|
+
| `className` | `string` | — | Additional CSS class applied to the outermost grid wrapper element. |
|
|
144
|
+
| `style` | `React.CSSProperties` | — | Inline styles applied to the grid wrapper element. |
|
|
145
|
+
|
|
146
|
+
#### Imperative Ref
|
|
147
|
+
|
|
148
|
+
| Prop | Type | Default | Description |
|
|
149
|
+
| :--- | :--- | :--- | :--- |
|
|
150
|
+
| `apiRef` | `React.MutableRefObject<GridApi>` | — | Reactive ref to the imperative API. Create with `useGridApiRef()`. |
|
|
151
|
+
|
|
152
|
+
#### List View
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Default | Description |
|
|
155
|
+
| :--- | :--- | :--- | :--- |
|
|
156
|
+
| `listView` | `boolean` | `false` | Renders the grid as a single-column list of cards. Designed for mobile and responsive layouts. |
|
|
157
|
+
| `listViewColumn` | `GridListViewColDef` | — | Column definition for list view mode. Must provide a `renderCell` function. The `field` value is used as a key. |
|
|
158
|
+
|
|
159
|
+
#### Column Group Headers
|
|
160
|
+
|
|
161
|
+
| Prop | Type | Default | Description |
|
|
162
|
+
| :--- | :--- | :--- | :--- |
|
|
163
|
+
| `columnGroupingModel` | `GridColumnGroupingModel` | — | Defines spanning header rows above the regular column headers. See [Column Group Headers](#️-column-group-headers). |
|
|
164
|
+
|
|
42
165
|
---
|
|
43
166
|
|
|
44
167
|
## 🕹️ Imperative API (`GridApi`)
|
|
@@ -387,3 +510,623 @@ interface GridPivotModel {
|
|
|
387
510
|
valueFields: { field: string; aggFn: string; headerName?: string }[];
|
|
388
511
|
}
|
|
389
512
|
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 📡 Event Callback Types
|
|
517
|
+
|
|
518
|
+
These interfaces describe the parameter objects passed to every event callback prop.
|
|
519
|
+
|
|
520
|
+
### `GridRowParams<R>`
|
|
521
|
+
Passed to `onRowClick`.
|
|
522
|
+
|
|
523
|
+
| Property | Type | Description |
|
|
524
|
+
| :--- | :--- | :--- |
|
|
525
|
+
| `row` | `R` | The full row data object. |
|
|
526
|
+
| `id` | `GridRowId` | Unique identifier of the row. |
|
|
527
|
+
| `rowIndex` | `number` | Zero-based index of the row in the visible dataset. |
|
|
528
|
+
|
|
529
|
+
### `GridCellParams<R>`
|
|
530
|
+
Passed to `onCellClick` and `isCellEditable`.
|
|
531
|
+
|
|
532
|
+
| Property | Type | Description |
|
|
533
|
+
| :--- | :--- | :--- |
|
|
534
|
+
| `row` | `R` | The full row data object. |
|
|
535
|
+
| `field` | `string` | The column field name. |
|
|
536
|
+
| `value` | `unknown` | The cell value (after `valueGetter`, before `valueFormatter`). |
|
|
537
|
+
| `colDef` | `GridColDef<R>` | The column definition. |
|
|
538
|
+
| `rowIndex` | `number` | Zero-based row index in the visible dataset. |
|
|
539
|
+
| `colIndex` | `number` | Zero-based column index. |
|
|
540
|
+
|
|
541
|
+
### `GridColumnOrderChangeParams`
|
|
542
|
+
Passed to `onColumnOrderChange`.
|
|
543
|
+
|
|
544
|
+
| Property | Type | Description |
|
|
545
|
+
| :--- | :--- | :--- |
|
|
546
|
+
| `column` | `GridColDef` | The column definition that was moved. |
|
|
547
|
+
| `oldIndex` | `number` | Previous column index. |
|
|
548
|
+
| `targetIndex` | `number` | New column index after the move. |
|
|
549
|
+
|
|
550
|
+
### `GridRowOrderChangeParams<R>`
|
|
551
|
+
Passed to `onRowOrderChange`.
|
|
552
|
+
|
|
553
|
+
| Property | Type | Description |
|
|
554
|
+
| :--- | :--- | :--- |
|
|
555
|
+
| `row` | `R` | The row data object that was moved. |
|
|
556
|
+
| `oldIndex` | `number` | Previous row index. |
|
|
557
|
+
| `targetIndex` | `number` | New row index after the move. |
|
|
558
|
+
|
|
559
|
+
### `GridRowScrollEndParams`
|
|
560
|
+
Passed to `onRowsScrollEnd`.
|
|
561
|
+
|
|
562
|
+
| Property | Type | Description |
|
|
563
|
+
| :--- | :--- | :--- |
|
|
564
|
+
| `visibleTop` | `number` | Pixel offset of the topmost visible row. |
|
|
565
|
+
| `visibleBottom` | `number` | Pixel offset of the bottommost visible row. |
|
|
566
|
+
| `viewportHeight` | `number` | Current height of the scroll viewport in pixels. |
|
|
567
|
+
|
|
568
|
+
### `GridDetailPanelParams<R>`
|
|
569
|
+
Passed to `getDetailPanelContent` and `getDetailPanelHeight`.
|
|
570
|
+
|
|
571
|
+
| Property | Type | Description |
|
|
572
|
+
| :--- | :--- | :--- |
|
|
573
|
+
| `row` | `R` | The full row data object. |
|
|
574
|
+
| `id` | `GridRowId` | Unique identifier of the row. |
|
|
575
|
+
| `rowIndex` | `number` | Zero-based index of the row in the visible dataset. |
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## 🌐 Server-Side Data Source (`GridDataSource`)
|
|
580
|
+
|
|
581
|
+
The `dataSource` prop is the primary integration point for connecting the grid to a remote API. When set, the grid delegates data fetching — including pagination, sorting, filtering, and optionally aggregation — to your `getRows` function instead of processing the data locally.
|
|
582
|
+
|
|
583
|
+
### `GridDataSource<R>`
|
|
584
|
+
|
|
585
|
+
| Property | Type | Required | Description |
|
|
586
|
+
| :--- | :--- | :--- | :--- |
|
|
587
|
+
| `getRows` | `(params: GridGetRowsParams) => Promise<GridGetRowsResponse<R>>` | Yes | Called whenever the grid needs data. The promise must resolve with rows and optionally a total row count. |
|
|
588
|
+
| `getAggregations` | `(params: Omit<GridGetRowsParams, 'startRow' \| 'endRow'>) => Promise<Record<string, unknown>>` | No | Called to fetch aggregate values for the full dataset (e.g. column totals). If omitted, aggregation results from `getRows.aggregationResults` are used instead. |
|
|
589
|
+
|
|
590
|
+
### `GridGetRowsParams`
|
|
591
|
+
The object passed to `getRows` by the grid on every data fetch.
|
|
592
|
+
|
|
593
|
+
| Property | Type | Description |
|
|
594
|
+
| :--- | :--- | :--- |
|
|
595
|
+
| `startRow` | `number` | Zero-based index of the first row to fetch (based on `page * pageSize`). |
|
|
596
|
+
| `endRow` | `number` | Zero-based index of the last row to fetch (exclusive). |
|
|
597
|
+
| `sortModel` | `GridSortItem[]` | Active sort configuration. Empty array when no sort is applied. |
|
|
598
|
+
| `filterModel` | `GridFilterModel` | Active filter state. Has empty `items` when no filters are applied. |
|
|
599
|
+
| `groupKeys` | `string[]` | Path of grouping key values for the current group level (used in lazy-loaded tree/grouping). Empty array for the root level. |
|
|
600
|
+
| `aggregationModel` | `GridAggregationModel \| undefined` | Active aggregation configuration, forwarded so the server can compute column summaries. |
|
|
601
|
+
|
|
602
|
+
### `GridGetRowsResponse<R>`
|
|
603
|
+
The object your `getRows` function must resolve with.
|
|
604
|
+
|
|
605
|
+
| Property | Type | Required | Description |
|
|
606
|
+
| :--- | :--- | :--- | :--- |
|
|
607
|
+
| `rows` | `R[]` | Yes | The rows for the requested page/range. |
|
|
608
|
+
| `rowCount` | `number` | No | Total number of rows in the full dataset. Required for server-side pagination; optional for infinite scroll. |
|
|
609
|
+
| `aggregationResults` | `Record<string, unknown>` | No | Column aggregation values for this response (e.g. `{ salary: 84500, age: 34.2 }`). |
|
|
610
|
+
|
|
611
|
+
### Complete Server-Side Usage Example
|
|
612
|
+
|
|
613
|
+
```tsx
|
|
614
|
+
import {
|
|
615
|
+
DataGrid,
|
|
616
|
+
GridDataSource,
|
|
617
|
+
GridGetRowsParams,
|
|
618
|
+
GridGetRowsResponse,
|
|
619
|
+
GridRowModel,
|
|
620
|
+
GridColDef,
|
|
621
|
+
} from '@opencorestack/opengridx';
|
|
622
|
+
|
|
623
|
+
interface Employee extends GridRowModel {
|
|
624
|
+
id: number;
|
|
625
|
+
name: string;
|
|
626
|
+
department: string;
|
|
627
|
+
salary: number;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const columns: GridColDef<Employee>[] = [
|
|
631
|
+
{ field: 'name', headerName: 'Name', flex: 1 },
|
|
632
|
+
{ field: 'department', headerName: 'Department', width: 160 },
|
|
633
|
+
{ field: 'salary', headerName: 'Salary', type: 'number', width: 130 },
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
const dataSource: GridDataSource<Employee> = {
|
|
637
|
+
getRows: async (params: GridGetRowsParams): Promise<GridGetRowsResponse<Employee>> => {
|
|
638
|
+
const response = await fetch('/api/employees', {
|
|
639
|
+
method: 'POST',
|
|
640
|
+
headers: { 'Content-Type': 'application/json' },
|
|
641
|
+
body: JSON.stringify(params),
|
|
642
|
+
});
|
|
643
|
+
const data = await response.json();
|
|
644
|
+
// data must have shape { rows: Employee[], total: number }
|
|
645
|
+
return { rows: data.rows, rowCount: data.total };
|
|
646
|
+
},
|
|
647
|
+
|
|
648
|
+
getAggregations: async (params) => {
|
|
649
|
+
const response = await fetch('/api/employees/aggregations', {
|
|
650
|
+
method: 'POST',
|
|
651
|
+
headers: { 'Content-Type': 'application/json' },
|
|
652
|
+
body: JSON.stringify(params),
|
|
653
|
+
});
|
|
654
|
+
return response.json(); // e.g. { salary: 4250000 }
|
|
655
|
+
},
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
export default function EmployeeGrid() {
|
|
659
|
+
return (
|
|
660
|
+
<DataGrid<Employee>
|
|
661
|
+
rows={[]}
|
|
662
|
+
columns={columns}
|
|
663
|
+
dataSource={dataSource}
|
|
664
|
+
paginationMode="server"
|
|
665
|
+
sortingMode="server"
|
|
666
|
+
filterMode="server"
|
|
667
|
+
pagination
|
|
668
|
+
pageSizeOptions={[25, 50, 100]}
|
|
669
|
+
height={600}
|
|
670
|
+
/>
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**How the grid decides when to call `getRows`:** The hook fires (with a 300 ms debounce) whenever `paginationMode`, `sortingMode`, or `filterMode` is set to `'server'` AND a `dataSource` is provided. Any change to `sortModel`, `filterModel`, or `paginationModel` re-triggers the fetch. Stale responses from superseded requests are silently discarded — only the latest request updates the grid.
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
## 📌 Column & Row Pinning
|
|
680
|
+
|
|
681
|
+
### `GridColumnPinning`
|
|
682
|
+
|
|
683
|
+
Pinned columns stay visible at the left or right edge of the grid during horizontal scrolling.
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
interface GridColumnPinning {
|
|
687
|
+
left?: string[]; // field names pinned to the left edge
|
|
688
|
+
right?: string[]; // field names pinned to the right edge
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### `GridRowPinning`
|
|
693
|
+
|
|
694
|
+
Pinned rows stay visible at the top or bottom of the viewport during vertical scrolling. The values are **row IDs**, not full row objects.
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
interface GridRowPinning {
|
|
698
|
+
top?: GridRowId[]; // IDs of rows pinned to the top
|
|
699
|
+
bottom?: GridRowId[]; // IDs of rows pinned to the bottom
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Column-level pinning control
|
|
704
|
+
|
|
705
|
+
Set `pinnable: false` on a `GridColDef` to prevent that column from being pinned via the UI menu.
|
|
706
|
+
|
|
707
|
+
### Usage Example
|
|
708
|
+
|
|
709
|
+
```tsx
|
|
710
|
+
import { DataGrid, GridColumnPinning, GridRowPinning } from '@opencorestack/opengridx';
|
|
711
|
+
|
|
712
|
+
<DataGrid
|
|
713
|
+
rows={rows}
|
|
714
|
+
columns={columns}
|
|
715
|
+
// Pin 'name' to the left and 'actions' to the right
|
|
716
|
+
pinnedColumns={{ left: ['name'], right: ['actions'] }}
|
|
717
|
+
// Pin rows with id=1 to the top, id=99 to the bottom
|
|
718
|
+
pinnedRows={{ top: [1], bottom: [99] }}
|
|
719
|
+
// Checkbox and expand columns stay visible while scrolling horizontally
|
|
720
|
+
checkboxSelection
|
|
721
|
+
pinCheckboxColumn
|
|
722
|
+
pinExpandColumn
|
|
723
|
+
/>
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
---
|
|
727
|
+
|
|
728
|
+
## 🔍 Filter Model Deep Reference
|
|
729
|
+
|
|
730
|
+
### `GridFilterModel`
|
|
731
|
+
|
|
732
|
+
The top-level filter state passed to `filterModel` and `onFilterModelChange`.
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
interface GridFilterModel {
|
|
736
|
+
items?: (GridFilterItem | GridFilterGroup)[];
|
|
737
|
+
logicOperator?: 'and' | 'or'; // default: 'and'
|
|
738
|
+
quickFilterValues?: string[]; // global search terms
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
| Property | Type | Default | Description |
|
|
743
|
+
| :--- | :--- | :--- | :--- |
|
|
744
|
+
| `items` | `(GridFilterItem \| GridFilterGroup)[]` | `[]` | Root-level filter conditions or nested groups. |
|
|
745
|
+
| `logicOperator` | `'and' \| 'or'` | `'and'` | How to combine the root `items`. |
|
|
746
|
+
| `quickFilterValues` | `string[]` | `[]` | Each string must match at least one field in the row (all strings must match). Applied before `items`. |
|
|
747
|
+
|
|
748
|
+
### `GridFilterItem`
|
|
749
|
+
|
|
750
|
+
A single column filter condition.
|
|
751
|
+
|
|
752
|
+
| Property | Type | Required | Description |
|
|
753
|
+
| :--- | :--- | :--- | :--- |
|
|
754
|
+
| `id` | `string \| number` | No | Optional stable identifier for this item (useful for controlled updates). |
|
|
755
|
+
| `field` | `string` | Yes | The column field to filter on. |
|
|
756
|
+
| `operator` | `GridFilterOperator` | Yes | The comparison operator to apply. |
|
|
757
|
+
| `value` | `unknown` | No | The value to compare against. Not required for `isEmpty` / `isNotEmpty`. |
|
|
758
|
+
|
|
759
|
+
### `GridFilterGroup`
|
|
760
|
+
|
|
761
|
+
A nested group of filter conditions combined with a logical operator. Groups can be nested arbitrarily.
|
|
762
|
+
|
|
763
|
+
| Property | Type | Required | Description |
|
|
764
|
+
| :--- | :--- | :--- | :--- |
|
|
765
|
+
| `id` | `string \| number` | No | Optional stable identifier. |
|
|
766
|
+
| `logicOperator` | `'and' \| 'or'` | Yes | How to combine the child `items` in this group. |
|
|
767
|
+
| `items` | `(GridFilterItem \| GridFilterGroup)[]` | Yes | Child conditions or nested groups. |
|
|
768
|
+
|
|
769
|
+
### `GridFilterOperator`
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
type GridFilterOperator =
|
|
773
|
+
| 'contains' | 'equals' | 'startsWith' | 'endsWith'
|
|
774
|
+
| 'isEmpty' | 'isNotEmpty' | 'isAnyOf'
|
|
775
|
+
| '>' | '>=' | '<' | '<=' | '!='
|
|
776
|
+
| 'is' | 'not';
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Default Operators per Column Type
|
|
780
|
+
|
|
781
|
+
The filter panel shows only the operators relevant to each column's `type`. These are also the operators used when the grid applies client-side filtering.
|
|
782
|
+
|
|
783
|
+
| Column `type` | Default operator | Available operators |
|
|
784
|
+
| :--- | :--- | :--- |
|
|
785
|
+
| `string` (default) | `contains` | `contains`, `equals`, `startsWith`, `endsWith`, `isEmpty`, `isNotEmpty` |
|
|
786
|
+
| `number` | `=` (numeric eq.) | `=`, `!=`, `>`, `>=`, `<`, `<=`, `isEmpty`, `isNotEmpty` |
|
|
787
|
+
| `date` | `is` | `is`, `not`, `after`, `onOrAfter`, `before`, `onOrBefore`, `isEmpty`, `isNotEmpty` |
|
|
788
|
+
| `boolean` | `is` | `is` |
|
|
789
|
+
| `singleSelect` | `isAnyOf` | `isAnyOf`, `is`, `not` |
|
|
790
|
+
|
|
791
|
+
### Nested Group Example
|
|
792
|
+
|
|
793
|
+
```tsx
|
|
794
|
+
import { DataGrid, GridFilterModel } from '@opencorestack/opengridx';
|
|
795
|
+
|
|
796
|
+
const filterModel: GridFilterModel = {
|
|
797
|
+
logicOperator: 'and',
|
|
798
|
+
items: [
|
|
799
|
+
// Simple item: department equals Engineering
|
|
800
|
+
{ id: 1, field: 'department', operator: 'equals', value: 'Engineering' },
|
|
801
|
+
// Nested group: salary > 80000 OR title contains 'Lead'
|
|
802
|
+
{
|
|
803
|
+
id: 2,
|
|
804
|
+
logicOperator: 'or',
|
|
805
|
+
items: [
|
|
806
|
+
{ id: 3, field: 'salary', operator: '>', value: 80000 },
|
|
807
|
+
{ id: 4, field: 'title', operator: 'contains', value: 'Lead' },
|
|
808
|
+
],
|
|
809
|
+
},
|
|
810
|
+
],
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
<DataGrid rows={rows} columns={columns} filterModel={filterModel} filterMode="client" />
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## 💾 `GridInitialState` and `GridState`
|
|
819
|
+
|
|
820
|
+
`GridInitialState` is a type alias for `GridState`. Pass it to `initialState` to restore persisted grid state on mount (e.g. from `localStorage`). Each key corresponds to a feature slice — all are optional.
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
type GridInitialState = GridState;
|
|
824
|
+
|
|
825
|
+
interface GridState {
|
|
826
|
+
sorting?: GridSortingState;
|
|
827
|
+
filter?: GridFilterState;
|
|
828
|
+
pagination?: GridPaginationState;
|
|
829
|
+
columns?: GridColumnsState;
|
|
830
|
+
density?: GridDensityState;
|
|
831
|
+
dataSource?: GridDataSourceState;
|
|
832
|
+
}
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
### State Slices
|
|
836
|
+
|
|
837
|
+
| Slice key | Interface | Shape |
|
|
838
|
+
| :--- | :--- | :--- |
|
|
839
|
+
| `sorting` | `GridSortingState` | `{ sortModel: GridSortItem[] }` |
|
|
840
|
+
| `filter` | `GridFilterState` | `{ filterModel: GridFilterModel }` |
|
|
841
|
+
| `pagination` | `GridPaginationState` | `{ paginationModel: GridPaginationModel; rowCount?: number }` |
|
|
842
|
+
| `columns` | `GridColumnsState` | `{ columnWidths: Record<string, number>; columnOrder: string[]; pinnedColumns?: GridColumnPinning; columnVisibilityModel?: Record<string, boolean> }` |
|
|
843
|
+
| `density` | `GridDensityState` | `{ density: 'compact' \| 'standard' \| 'comfortable' }` |
|
|
844
|
+
| `dataSource` | `GridDataSourceState` | `{ loading: boolean; error?: unknown }` (read-only — not meaningful to restore) |
|
|
845
|
+
|
|
846
|
+
### Restoring State from `localStorage`
|
|
847
|
+
|
|
848
|
+
The recommended way is `useGridStateStorage` (see Hooks section), but you can also drive it manually:
|
|
849
|
+
|
|
850
|
+
```tsx
|
|
851
|
+
import { DataGrid, GridState, GridInitialState } from '@opencorestack/opengridx';
|
|
852
|
+
import { useState, useCallback } from 'react';
|
|
853
|
+
|
|
854
|
+
const STORAGE_KEY = 'my-grid-state';
|
|
855
|
+
|
|
856
|
+
function loadState(): GridInitialState | undefined {
|
|
857
|
+
try {
|
|
858
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
859
|
+
return raw ? (JSON.parse(raw) as GridInitialState) : undefined;
|
|
860
|
+
} catch {
|
|
861
|
+
return undefined;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
export default function PersistentGrid() {
|
|
866
|
+
const [initialState] = useState<GridInitialState | undefined>(loadState);
|
|
867
|
+
|
|
868
|
+
const handleStateChange = useCallback((state: GridState) => {
|
|
869
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
870
|
+
}, []);
|
|
871
|
+
|
|
872
|
+
return (
|
|
873
|
+
<DataGrid
|
|
874
|
+
rows={rows}
|
|
875
|
+
columns={columns}
|
|
876
|
+
initialState={initialState}
|
|
877
|
+
onStateChange={handleStateChange}
|
|
878
|
+
/>
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
## ∑ Aggregation Reference
|
|
886
|
+
|
|
887
|
+
### `GridAggregationModel`
|
|
888
|
+
|
|
889
|
+
A plain object mapping column field names to aggregation function names.
|
|
890
|
+
|
|
891
|
+
```typescript
|
|
892
|
+
interface GridAggregationModel {
|
|
893
|
+
[field: string]: string; // e.g. { salary: 'sum', age: 'avg', name: 'count' }
|
|
894
|
+
}
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### `GridAggregationResult`
|
|
898
|
+
|
|
899
|
+
The computed output, also a plain object mapping field names to their computed values.
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
type GridAggregationResult = Record<string, unknown>;
|
|
903
|
+
// e.g. { salary: 420000, age: 34.2, name: 12 }
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
### Built-In Aggregation Functions
|
|
907
|
+
|
|
908
|
+
| Function | Input | Output | Description |
|
|
909
|
+
| :--- | :--- | :--- | :--- |
|
|
910
|
+
| `sum` | numeric values | `number` | Sum of all non-null numeric values in the group. |
|
|
911
|
+
| `avg` | numeric values | `number \| null` | Arithmetic mean. Returns `null` if there are no numeric values. |
|
|
912
|
+
| `min` | numeric values | `number \| null` | Smallest numeric value. Returns `null` if there are no numeric values. |
|
|
913
|
+
| `max` | numeric values | `number \| null` | Largest numeric value. Returns `null` if there are no numeric values. |
|
|
914
|
+
| `count` | any values | `number` | Count of non-null values. |
|
|
915
|
+
| `unique` | any values | `number` | Count of distinct non-null values (uses a `Set`). |
|
|
916
|
+
|
|
917
|
+
All built-in functions are exported as the `BuiltInAggFn` type: `'sum' | 'avg' | 'count' | 'min' | 'max' | 'unique'`.
|
|
918
|
+
|
|
919
|
+
Use `formatAggregationValue(value, fnName)` to turn a raw result into a display string. `avg` is formatted to 2 decimal places; all other numbers use `toLocaleString()`.
|
|
920
|
+
|
|
921
|
+
### `getAggregationPosition` Callback
|
|
922
|
+
|
|
923
|
+
```typescript
|
|
924
|
+
getAggregationPosition?: (groupNode: GridTreeNode | null) => 'inline' | 'footer' | null
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
Called once per group node (and once with `null` for the grand-total / root level) to decide where the aggregated row appears:
|
|
928
|
+
|
|
929
|
+
| Return value | Effect |
|
|
930
|
+
| :--- | :--- |
|
|
931
|
+
| `'inline'` | Aggregation values appear inside the group header row itself. |
|
|
932
|
+
| `'footer'` | A separate aggregation row is rendered below the group's last row. |
|
|
933
|
+
| `null` | Aggregation result is hidden for this group. |
|
|
934
|
+
|
|
935
|
+
### Aggregation + Row Grouping Example
|
|
936
|
+
|
|
937
|
+
```tsx
|
|
938
|
+
import {
|
|
939
|
+
DataGrid,
|
|
940
|
+
GridColDef,
|
|
941
|
+
GridRowModel,
|
|
942
|
+
GridTreeNode,
|
|
943
|
+
GridAggregationModel,
|
|
944
|
+
GridRowGroupingModel,
|
|
945
|
+
} from '@opencorestack/opengridx';
|
|
946
|
+
|
|
947
|
+
interface Employee extends GridRowModel {
|
|
948
|
+
id: number;
|
|
949
|
+
name: string;
|
|
950
|
+
department: string;
|
|
951
|
+
salary: number;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const columns: GridColDef<Employee>[] = [
|
|
955
|
+
{ field: 'name', headerName: 'Name', flex: 1 },
|
|
956
|
+
{ field: 'department', headerName: 'Department', width: 160, groupable: true },
|
|
957
|
+
{ field: 'salary', headerName: 'Salary', type: 'number', width: 130, aggregable: true },
|
|
958
|
+
];
|
|
959
|
+
|
|
960
|
+
const aggregationModel: GridAggregationModel = { salary: 'sum' };
|
|
961
|
+
const rowGroupingModel: GridRowGroupingModel = ['department'];
|
|
962
|
+
|
|
963
|
+
export default function GroupedGrid() {
|
|
964
|
+
return (
|
|
965
|
+
<DataGrid<Employee>
|
|
966
|
+
rows={rows}
|
|
967
|
+
columns={columns}
|
|
968
|
+
rowGroupingModel={rowGroupingModel}
|
|
969
|
+
aggregationModel={aggregationModel}
|
|
970
|
+
getAggregationPosition={(groupNode: GridTreeNode | null) => {
|
|
971
|
+
// Show grand total at the footer, group totals inline
|
|
972
|
+
return groupNode === null ? 'footer' : 'inline';
|
|
973
|
+
}}
|
|
974
|
+
/>
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
## 🗂️ Row Grouping
|
|
982
|
+
|
|
983
|
+
### `GridRowGroupingModel`
|
|
984
|
+
|
|
985
|
+
A simple array of field names that defines which columns the grid groups rows by, in order.
|
|
986
|
+
|
|
987
|
+
```typescript
|
|
988
|
+
type GridRowGroupingModel = string[];
|
|
989
|
+
// e.g. ['department'] → one level of grouping
|
|
990
|
+
// e.g. ['department', 'team'] → two levels (department → team → rows)
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
The referenced fields must have `groupable: true` (the default) on their `GridColDef`.
|
|
994
|
+
|
|
995
|
+
### Usage Example
|
|
996
|
+
|
|
997
|
+
```tsx
|
|
998
|
+
import { DataGrid, GridRowGroupingModel, GridAggregationModel } from '@opencorestack/opengridx';
|
|
999
|
+
import { useState } from 'react';
|
|
1000
|
+
|
|
1001
|
+
export default function GroupingExample() {
|
|
1002
|
+
const [rowGroupingModel, setRowGroupingModel] = useState<GridRowGroupingModel>(['department']);
|
|
1003
|
+
const [aggregationModel, setAggregationModel] = useState<GridAggregationModel>({
|
|
1004
|
+
salary: 'sum',
|
|
1005
|
+
age: 'avg',
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
return (
|
|
1009
|
+
<DataGrid
|
|
1010
|
+
rows={rows}
|
|
1011
|
+
columns={columns}
|
|
1012
|
+
rowGroupingModel={rowGroupingModel}
|
|
1013
|
+
onRowGroupingModelChange={setRowGroupingModel}
|
|
1014
|
+
aggregationModel={aggregationModel}
|
|
1015
|
+
onAggregationModelChange={setAggregationModel}
|
|
1016
|
+
/>
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
### Interaction with `aggregationModel`
|
|
1022
|
+
|
|
1023
|
+
When `rowGroupingModel` is set, the grid creates group nodes (see `GridTreeNode`). The `aggregationModel` controls which columns show summaries on those nodes. The `getAggregationPosition` callback gives per-group control over whether the summary appears inline (in the group header row) or as a footer row below the group. For server-side grouping, return aggregation values in `GridGetRowsResponse.aggregationResults`.
|
|
1024
|
+
|
|
1025
|
+
---
|
|
1026
|
+
|
|
1027
|
+
## 🗃️ Column Group Headers
|
|
1028
|
+
|
|
1029
|
+
Column groups render spanning header cells above the normal column header row, allowing multi-level column organisation.
|
|
1030
|
+
|
|
1031
|
+
### `GridColumnGroup`
|
|
1032
|
+
|
|
1033
|
+
```typescript
|
|
1034
|
+
interface GridColumnGroup {
|
|
1035
|
+
groupId: string; // unique identifier for this group
|
|
1036
|
+
headerName: string; // text shown in the spanning header cell
|
|
1037
|
+
headerClassName?: string; // optional CSS class for custom styling
|
|
1038
|
+
children: Array<string | GridColumnGroup>; // field names (leaf) or nested groups
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
type GridColumnGroupingModel = GridColumnGroup[];
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
The `children` array can contain:
|
|
1045
|
+
- **`string`** — a column `field` name, making this a leaf group that spans exactly those columns.
|
|
1046
|
+
- **`GridColumnGroup`** — a nested sub-group, enabling multi-level spanning headers.
|
|
1047
|
+
|
|
1048
|
+
### Usage Example
|
|
1049
|
+
|
|
1050
|
+
```tsx
|
|
1051
|
+
import { DataGrid, GridColumnGroup, GridColumnGroupingModel } from '@opencorestack/opengridx';
|
|
1052
|
+
|
|
1053
|
+
const columnGroupingModel: GridColumnGroupingModel = [
|
|
1054
|
+
{
|
|
1055
|
+
groupId: 'personal',
|
|
1056
|
+
headerName: 'Personal Info',
|
|
1057
|
+
children: ['firstName', 'lastName', 'age'],
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
groupId: 'compensation',
|
|
1061
|
+
headerName: 'Compensation',
|
|
1062
|
+
children: [
|
|
1063
|
+
{ groupId: 'base', headerName: 'Base', children: ['salary', 'bonus'] },
|
|
1064
|
+
{ groupId: 'equity', headerName: 'Equity', children: ['options', 'rsu'] },
|
|
1065
|
+
],
|
|
1066
|
+
},
|
|
1067
|
+
];
|
|
1068
|
+
|
|
1069
|
+
export default function GroupedHeadersGrid() {
|
|
1070
|
+
return (
|
|
1071
|
+
<DataGrid
|
|
1072
|
+
rows={rows}
|
|
1073
|
+
columns={columns}
|
|
1074
|
+
columnGroupingModel={columnGroupingModel}
|
|
1075
|
+
/>
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
The grid renders two header rows: the spanning group row on top, and the regular per-column header row below. Groups that are not referenced in `columnGroupingModel` are rendered without a spanning header.
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
1084
|
+
## 📋 List View
|
|
1085
|
+
|
|
1086
|
+
List view renders the grid as a single-column list of cards, replacing all normal column layout. It is designed for narrow viewports and mobile layouts.
|
|
1087
|
+
|
|
1088
|
+
### `GridListViewColDef<R>`
|
|
1089
|
+
|
|
1090
|
+
```typescript
|
|
1091
|
+
interface GridListViewColDef<R extends GridRowModel = GridRowModel> {
|
|
1092
|
+
field: string;
|
|
1093
|
+
renderCell: (params: GridRenderCellParams<R>) => React.ReactNode;
|
|
1094
|
+
}
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
The `renderCell` function receives the full `GridRenderCellParams` (including `row`), so you have access to every field of the row to build a rich card layout.
|
|
1098
|
+
|
|
1099
|
+
### Usage Example
|
|
1100
|
+
|
|
1101
|
+
```tsx
|
|
1102
|
+
import { DataGrid, GridListViewColDef, GridRenderCellParams } from '@opencorestack/opengridx';
|
|
1103
|
+
|
|
1104
|
+
interface Employee extends GridRowModel {
|
|
1105
|
+
id: number;
|
|
1106
|
+
name: string;
|
|
1107
|
+
department: string;
|
|
1108
|
+
salary: number;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
const listViewColumn: GridListViewColDef<Employee> = {
|
|
1112
|
+
field: 'card',
|
|
1113
|
+
renderCell: (params: GridRenderCellParams<Employee>) => (
|
|
1114
|
+
<div style={{ padding: '12px 16px' }}>
|
|
1115
|
+
<strong>{params.row.name}</strong>
|
|
1116
|
+
<span> — {params.row.department}</span>
|
|
1117
|
+
<div>${params.row.salary.toLocaleString()}</div>
|
|
1118
|
+
</div>
|
|
1119
|
+
),
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
export default function MobileGrid() {
|
|
1123
|
+
return (
|
|
1124
|
+
<DataGrid<Employee>
|
|
1125
|
+
rows={rows}
|
|
1126
|
+
columns={columns}
|
|
1127
|
+
listView
|
|
1128
|
+
listViewColumn={listViewColumn}
|
|
1129
|
+
/>
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
```
|