@opencorestack/opengridx 0.1.9 → 1.0.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/CHANGELOG.md +43 -0
- package/dist/index.d.ts +60 -41
- package/dist/opengridx.es.js +5045 -4549
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +11 -11
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/README.md +14 -0
- package/docs/components/aggregation-footer.md +48 -0
- package/docs/components/datagrid.md +3 -0
- package/docs/components/empty-state.md +50 -0
- package/docs/components/error-overlay.md +40 -0
- package/docs/roadmap.md +20 -1
- package/lib/components/Cell/Cell.tsx +9 -10
- package/lib/components/Cell/GridEditInputCell.tsx +4 -4
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +3 -2
- package/lib/components/DataGrid/DataGrid.regression.test.tsx +132 -0
- package/lib/components/DataGrid/DataGrid.tsx +336 -1018
- package/lib/components/DataGrid/GridAggregationFooter.tsx +68 -0
- package/lib/components/DataGrid/GridEmptyState.tsx +16 -0
- package/lib/components/DataGrid/GridErrorOverlay.tsx +33 -0
- package/lib/components/DataGrid/GridListView.tsx +116 -0
- package/lib/components/DataGrid/GridPinnedRows.tsx +98 -0
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +124 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +202 -0
- package/lib/components/FilterPanel/FilterPanel.tsx +18 -10
- package/lib/components/Header/ColumnMenu.tsx +2 -2
- package/lib/components/Header/Header.tsx +4 -4
- package/lib/components/ListView/ListViewRow.tsx +2 -2
- package/lib/components/Row/Row.tsx +10 -10
- package/lib/components/SkeletonRow/SkeletonRow.tsx +9 -2
- package/lib/components/Toolbar/GridToolbar.tsx +6 -0
- package/lib/hooks/core/useDataGrid.regression.test.ts +66 -0
- package/lib/hooks/core/useDataGrid.ts +14 -34
- package/lib/hooks/core/useGridColumns.tsx +228 -0
- package/lib/hooks/core/useGridControlledState.ts +192 -0
- package/lib/hooks/core/useGridRowPipeline.ts +109 -0
- package/lib/hooks/core/useGridScrollSync.ts +52 -0
- package/lib/hooks/core/useGridStateSnapshot.ts +63 -0
- package/lib/hooks/core/useGridVirtualization.ts +186 -0
- package/lib/hooks/core/useGridVisibleRows.ts +66 -0
- package/lib/hooks/core/useLayout.ts +0 -6
- package/lib/hooks/features/useAggregation.ts +7 -7
- package/lib/hooks/features/useGridDataSource.ts +1 -1
- package/lib/hooks/features/useGridEditing.ts +31 -32
- package/lib/hooks/features/usePivot.test.ts +202 -0
- package/lib/hooks/features/usePivot.ts +0 -0
- package/lib/hooks/useColumnReorder.ts +1 -1
- package/lib/hooks/useRowGrouping.ts +14 -14
- package/lib/hooks/useTreeData.ts +1 -1
- package/lib/state/types.ts +1 -1
- package/lib/state/useGridStateStorage.ts +1 -1
- package/lib/types/index.ts +48 -32
- package/lib/utils/export/exportToExcelAdvanced.ts +14 -14
- package/lib/utils/export/index.ts +22 -17
- package/lib/utils/filtering/index.ts +20 -20
- package/lib/utils/sorting/index.ts +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import type {
|
|
3
3
|
GridFilterModel,
|
|
4
4
|
GridFilterItem,
|
|
@@ -30,34 +30,42 @@ const FilterRow: React.FC<{
|
|
|
30
30
|
const currentOperator = item?.operator ?? operators[0];
|
|
31
31
|
|
|
32
32
|
// Local input state to prevent full filter-panel re-render on every keystroke
|
|
33
|
-
const [localValue, setLocalValue] = useState(item?.value
|
|
33
|
+
const [localValue, setLocalValue] = useState<string>(item?.value != null ? String(item.value) : '');
|
|
34
34
|
const onChangeRef = useRef(onChange);
|
|
35
|
-
|
|
35
|
+
const itemRef = useRef(item);
|
|
36
|
+
const colFieldRef = useRef(col.field);
|
|
37
|
+
const operatorRef = useRef(currentOperator);
|
|
38
|
+
useLayoutEffect(() => {
|
|
39
|
+
onChangeRef.current = onChange;
|
|
40
|
+
itemRef.current = item;
|
|
41
|
+
colFieldRef.current = col.field;
|
|
42
|
+
operatorRef.current = currentOperator;
|
|
43
|
+
});
|
|
36
44
|
|
|
37
45
|
// Sync when the external item changes (e.g. operator change, clear)
|
|
38
46
|
const prevItemValue = useRef(item?.value);
|
|
39
47
|
useEffect(() => {
|
|
40
48
|
if (item?.value !== prevItemValue.current) {
|
|
41
49
|
prevItemValue.current = item?.value;
|
|
42
|
-
setLocalValue(item?.value
|
|
50
|
+
setLocalValue(item?.value != null ? String(item.value) : '');
|
|
43
51
|
}
|
|
44
52
|
}, [item?.value]);
|
|
45
53
|
|
|
46
54
|
// Debounce propagating the value upward (300ms)
|
|
47
55
|
useEffect(() => {
|
|
48
56
|
const handler = setTimeout(() => {
|
|
49
|
-
const
|
|
57
|
+
const currentItem = itemRef.current;
|
|
58
|
+
const currentValue = currentItem?.value ?? '';
|
|
50
59
|
if (localValue !== currentValue) {
|
|
51
60
|
onChangeRef.current({
|
|
52
|
-
id:
|
|
53
|
-
field:
|
|
54
|
-
operator:
|
|
61
|
+
id: currentItem?.id ?? `${colFieldRef.current}-${Date.now()}`,
|
|
62
|
+
field: colFieldRef.current,
|
|
63
|
+
operator: operatorRef.current as GridFilterOperator,
|
|
55
64
|
value: localValue,
|
|
56
65
|
});
|
|
57
66
|
}
|
|
58
67
|
}, 300);
|
|
59
68
|
return () => clearTimeout(handler);
|
|
60
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
61
69
|
}, [localValue]);
|
|
62
70
|
|
|
63
71
|
const handleOperatorChange = (op: string) => {
|
|
@@ -158,7 +166,7 @@ const FilterRow: React.FC<{
|
|
|
158
166
|
// ── Root panel ────────────────────────────────────────────────────────────────
|
|
159
167
|
|
|
160
168
|
export const FilterPanel: React.FC<FilterPanelProps> = ({ filterModel, columns, onFilterModelChange }) => {
|
|
161
|
-
const items
|
|
169
|
+
const items = useMemo<GridFilterItem[]>(() => (filterModel.items ?? []) as GridFilterItem[], [filterModel.items]);
|
|
162
170
|
const logicOperator = filterModel.logicOperator ?? 'and';
|
|
163
171
|
|
|
164
172
|
const handleItemChange = useCallback((field: string, newItem: GridFilterItem | null) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { useRef, useEffect, useState } from 'react';
|
|
2
2
|
import ReactDOM from 'react-dom';
|
|
3
|
-
import type { GridColDef, GridSortDirection, GridColumnPinning } from '../../types';
|
|
3
|
+
import type { GridColDef, GridRowModel, GridSortDirection, GridColumnPinning } from '../../types';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
interface ColumnMenuProps {
|
|
7
|
-
colDef: GridColDef<
|
|
7
|
+
colDef: GridColDef<GridRowModel>;
|
|
8
8
|
sortModel?: Array<{ field: string; sort: 'asc' | 'desc' }>;
|
|
9
9
|
onSort?: (field: string, direction: GridSortDirection) => void;
|
|
10
10
|
onHide?: (field: string) => void;
|
|
@@ -236,7 +236,7 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
236
236
|
// All non-spacer fields in order (for group-span math) with their effective physical width
|
|
237
237
|
const allCols = allColumns ?? columns;
|
|
238
238
|
const allLeafFields = allCols
|
|
239
|
-
.filter(c => !
|
|
239
|
+
.filter(c => !c.isSpacer)
|
|
240
240
|
.map(c => ({
|
|
241
241
|
field: c.field,
|
|
242
242
|
width: columnWidths[c.field] ?? c.width ?? 100
|
|
@@ -354,7 +354,7 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
354
354
|
)}
|
|
355
355
|
|
|
356
356
|
{columns.map((colDef, index) => {
|
|
357
|
-
if (
|
|
357
|
+
if (colDef.isSpacer) {
|
|
358
358
|
return (
|
|
359
359
|
<div
|
|
360
360
|
key={colDef.field}
|
|
@@ -409,7 +409,7 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
const headerContent = colDef.renderHeader
|
|
412
|
-
? colDef.renderHeader({ field: colDef.field, colDef: colDef as
|
|
412
|
+
? colDef.renderHeader({ field: colDef.field, colDef: colDef as unknown as GridColDef, colIndex: index })
|
|
413
413
|
: colDef.headerName || colDef.field;
|
|
414
414
|
|
|
415
415
|
const handleHeaderClick = (e: React.MouseEvent) => {
|
|
@@ -499,7 +499,7 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
499
499
|
|
|
500
500
|
{menuOpenParams && (
|
|
501
501
|
<ColumnMenu
|
|
502
|
-
colDef={menuOpenParams.colDef}
|
|
502
|
+
colDef={menuOpenParams.colDef as unknown as GridColDef<GridRowModel>}
|
|
503
503
|
sortModel={sortModel}
|
|
504
504
|
onSort={onSort}
|
|
505
505
|
onHide={onHideColumn}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { GridRowModel, GridRowId, GridListViewColDef, GridRenderCellParams } from '../../types';
|
|
2
|
+
import type { GridColDef, GridRowModel, GridRowId, GridListViewColDef, GridRenderCellParams } from '../../types';
|
|
3
3
|
|
|
4
4
|
export interface ListViewRowProps<R extends GridRowModel = GridRowModel> {
|
|
5
5
|
row: R;
|
|
@@ -26,7 +26,7 @@ export function ListViewRow<R extends GridRowModel = GridRowModel>({
|
|
|
26
26
|
row,
|
|
27
27
|
value: undefined,
|
|
28
28
|
field: listViewColumn.field,
|
|
29
|
-
colDef: { field: listViewColumn.field } as
|
|
29
|
+
colDef: { field: listViewColumn.field } as unknown as GridColDef<R>,
|
|
30
30
|
rowIndex,
|
|
31
31
|
colIndex: 0,
|
|
32
32
|
};
|
|
@@ -40,10 +40,10 @@ export interface RowProps<R extends GridRowModel = GridRowModel> {
|
|
|
40
40
|
isDragging?: boolean;
|
|
41
41
|
isDragOver?: boolean;
|
|
42
42
|
|
|
43
|
-
editingCell?: { id: GridRowId; field: string; value:
|
|
44
|
-
onEditStart?: (params: { id: GridRowId, field: string, value:
|
|
43
|
+
editingCell?: { id: GridRowId; field: string; value: unknown; } | null;
|
|
44
|
+
onEditStart?: (params: { id: GridRowId, field: string, value: unknown }) => void;
|
|
45
45
|
onEditStop?: (params?: { cancel?: boolean }) => void;
|
|
46
|
-
onEditCellValueChange?: (params: { id: GridRowId, field: string, value:
|
|
46
|
+
onEditCellValueChange?: (params: { id: GridRowId, field: string, value: unknown }) => void;
|
|
47
47
|
|
|
48
48
|
focusedCellField?: string | null;
|
|
49
49
|
isFocusVisible?: boolean;
|
|
@@ -120,15 +120,15 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
120
120
|
);
|
|
121
121
|
}, [columns, columnWidths, pinnedColumns, checkboxSelection, pinCheckboxColumn, hasDetailPanel, pinExpandColumn, rowReordering]);
|
|
122
122
|
|
|
123
|
-
const isGroupRow =
|
|
123
|
+
const isGroupRow = row._hasChildren === true;
|
|
124
124
|
|
|
125
|
-
const handleCellEditStart = React.useCallback((field: string, value:
|
|
125
|
+
const handleCellEditStart = React.useCallback((field: string, value: unknown) => {
|
|
126
126
|
if (!isGroupRow) {
|
|
127
127
|
onEditStart?.({ id: row.id, field, value });
|
|
128
128
|
}
|
|
129
129
|
}, [isGroupRow, row.id, onEditStart]);
|
|
130
130
|
|
|
131
|
-
const handleCellValueChange = React.useCallback((field: string, newValue:
|
|
131
|
+
const handleCellValueChange = React.useCallback((field: string, newValue: unknown) => {
|
|
132
132
|
onEditCellValueChange?.({ id: row.id, field, value: newValue });
|
|
133
133
|
}, [onEditCellValueChange, row.id]);
|
|
134
134
|
|
|
@@ -137,7 +137,7 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
137
137
|
}, [onEditStop]);
|
|
138
138
|
|
|
139
139
|
// ── Skeleton row (shown during infinite-scroll fetch) ─────────────────────
|
|
140
|
-
if (
|
|
140
|
+
if (row._isSkeleton) {
|
|
141
141
|
// Render shimmer placeholders — one per visible column, matching row height
|
|
142
142
|
const cellCount = Math.max(columns.length, 5);
|
|
143
143
|
return (
|
|
@@ -250,7 +250,7 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
250
250
|
row,
|
|
251
251
|
field: '__expand_col__',
|
|
252
252
|
value: isDetailPanelExpanded,
|
|
253
|
-
colDef: { field: '__expand_col__', width: 48 } as
|
|
253
|
+
colDef: { field: '__expand_col__', width: 48 } as unknown as GridColDef<R>,
|
|
254
254
|
rowIndex,
|
|
255
255
|
colIndex: -1
|
|
256
256
|
});
|
|
@@ -284,7 +284,7 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
284
284
|
row,
|
|
285
285
|
field: '__checkbox_col__',
|
|
286
286
|
value: isSelected,
|
|
287
|
-
colDef: { field: '__checkbox_col__', width: 48 } as
|
|
287
|
+
colDef: { field: '__checkbox_col__', width: 48 } as unknown as GridColDef<R>,
|
|
288
288
|
rowIndex,
|
|
289
289
|
colIndex: -1
|
|
290
290
|
});
|
|
@@ -305,7 +305,7 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
305
305
|
|
|
306
306
|
{ }
|
|
307
307
|
{columns.map((colDef, colIndex) => {
|
|
308
|
-
if (
|
|
308
|
+
if (colDef.isSpacer) {
|
|
309
309
|
return (
|
|
310
310
|
<div
|
|
311
311
|
key={colDef.field}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { GridColDef } from '../../types';
|
|
2
3
|
|
|
4
|
+
interface SkeletonColumn extends GridColDef {
|
|
5
|
+
left?: number;
|
|
6
|
+
right?: number;
|
|
7
|
+
pinned?: 'left' | 'right';
|
|
8
|
+
zIndex?: number;
|
|
9
|
+
}
|
|
3
10
|
|
|
4
11
|
interface SkeletonRowProps {
|
|
5
|
-
columns:
|
|
12
|
+
columns: SkeletonColumn[];
|
|
6
13
|
rowHeight: number;
|
|
7
14
|
checkboxSelection?: boolean;
|
|
8
15
|
hasDetailPanel?: boolean;
|
|
@@ -54,7 +61,7 @@ export const SkeletonRow: React.FC<SkeletonRowProps> = ({
|
|
|
54
61
|
|
|
55
62
|
{ }
|
|
56
63
|
{columns.map((col, index) => {
|
|
57
|
-
const width = col.width
|
|
64
|
+
const width = typeof col.width === 'number' ? col.width : 150;
|
|
58
65
|
const skeletonWidth = Math.min(width * 0.7, width - 20);
|
|
59
66
|
|
|
60
67
|
return (
|
|
@@ -23,6 +23,7 @@ export interface GridToolbarProps {
|
|
|
23
23
|
columnVisibilityModel?: Record<string, boolean>;
|
|
24
24
|
onColumnVisibilityModelChange?: (model: Record<string, boolean>) => void;
|
|
25
25
|
onColumnReorder?: (fromField: string, toField: string) => void;
|
|
26
|
+
onColumnOrderReset?: () => void;
|
|
26
27
|
forceColumnsOpen?: boolean;
|
|
27
28
|
onColumnsPanelClose?: () => void;
|
|
28
29
|
children?: React.ReactNode;
|
|
@@ -201,6 +202,7 @@ function ColumnsPanelWrapper({
|
|
|
201
202
|
onShowAll,
|
|
202
203
|
onHideAll,
|
|
203
204
|
onColumnReorder,
|
|
205
|
+
onColumnOrderReset,
|
|
204
206
|
onClose
|
|
205
207
|
}: {
|
|
206
208
|
anchorRef: React.RefObject<HTMLButtonElement | null>;
|
|
@@ -210,6 +212,7 @@ function ColumnsPanelWrapper({
|
|
|
210
212
|
onShowAll: () => void;
|
|
211
213
|
onHideAll: () => void;
|
|
212
214
|
onColumnReorder?: (fromField: string, toField: string) => void;
|
|
215
|
+
onColumnOrderReset?: () => void;
|
|
213
216
|
onClose: () => void;
|
|
214
217
|
}) {
|
|
215
218
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
@@ -268,6 +271,7 @@ function ColumnsPanelWrapper({
|
|
|
268
271
|
onShowAll={onShowAll}
|
|
269
272
|
onHideAll={onHideAll}
|
|
270
273
|
onColumnReorder={onColumnReorder}
|
|
274
|
+
onColumnOrderReset={onColumnOrderReset}
|
|
271
275
|
/>
|
|
272
276
|
</div>
|
|
273
277
|
);
|
|
@@ -403,6 +407,7 @@ export function GridToolbar({
|
|
|
403
407
|
columnVisibilityModel = {},
|
|
404
408
|
onColumnVisibilityModelChange,
|
|
405
409
|
onColumnReorder,
|
|
410
|
+
onColumnOrderReset,
|
|
406
411
|
forceColumnsOpen,
|
|
407
412
|
onColumnsPanelClose,
|
|
408
413
|
children,
|
|
@@ -553,6 +558,7 @@ export function GridToolbar({
|
|
|
553
558
|
onShowAll={handleShowAllColumns}
|
|
554
559
|
onHideAll={handleHideAllColumns}
|
|
555
560
|
onColumnReorder={onColumnReorder}
|
|
561
|
+
onColumnOrderReset={onColumnOrderReset}
|
|
556
562
|
onClose={() => { setColsOpen(false); onColumnsPanelClose?.(); }}
|
|
557
563
|
/>
|
|
558
564
|
)}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for bugs fixed in the useDataGrid reducer.
|
|
3
|
+
*
|
|
4
|
+
* Each describe block names the bug and the commit that fixed it, so a
|
|
5
|
+
* future failure tells you exactly what regressed and where to look.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { renderHook, act } from '@testing-library/react';
|
|
10
|
+
import { useDataGrid } from './useDataGrid';
|
|
11
|
+
import type { GridColDef } from '../../types';
|
|
12
|
+
|
|
13
|
+
const COLS: GridColDef[] = [
|
|
14
|
+
{ field: 'id', headerName: 'ID', width: 80 },
|
|
15
|
+
{ field: 'name', headerName: 'Name', width: 150 },
|
|
16
|
+
{ field: 'age', headerName: 'Age', width: 100 },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const ROWS = [
|
|
20
|
+
{ id: 1, name: 'Alice', age: 30 },
|
|
21
|
+
{ id: 2, name: 'Bob', age: 25 },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
// Bug: SET_COLUMNS reducer was hardcoding columnVisibilityModel: {} on every
|
|
26
|
+
// column update, silently wiping any hidden columns.
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
describe('SET_COLUMNS preserves columnVisibilityModel', () => {
|
|
29
|
+
it('keeps hidden columns hidden after setColumns is called', () => {
|
|
30
|
+
const { result } = renderHook(() =>
|
|
31
|
+
useDataGrid({
|
|
32
|
+
rows: ROWS,
|
|
33
|
+
columns: COLS,
|
|
34
|
+
columnVisibilityModel: { age: false },
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Confirm the initial visibility model was applied.
|
|
39
|
+
expect(result.current.state.columns.columnVisibilityModel).toEqual({ age: false });
|
|
40
|
+
|
|
41
|
+
// Simulate a column update (e.g. a width change coming from a resize handler).
|
|
42
|
+
const updatedCols: GridColDef[] = COLS.map(c =>
|
|
43
|
+
c.field === 'name' ? { ...c, width: 200 } : c
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
act(() => {
|
|
47
|
+
result.current.setColumns(updatedCols);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// The visibility model must survive the column update — not reset to {}.
|
|
51
|
+
expect(result.current.state.columns.columnVisibilityModel).toEqual({ age: false });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('does not gain a visibility entry for columns that were already visible', () => {
|
|
55
|
+
const { result } = renderHook(() =>
|
|
56
|
+
useDataGrid({ rows: ROWS, columns: COLS })
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
act(() => {
|
|
60
|
+
result.current.setColumns(COLS);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// No spurious entries: an empty model means all columns are visible.
|
|
64
|
+
expect(result.current.state.columns.columnVisibilityModel).toEqual({});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
import { useReducer, useCallback,
|
|
2
|
+
import { useReducer, useCallback, useState, useRef, useEffect, useLayoutEffect } from 'react';
|
|
3
3
|
import type {
|
|
4
|
-
GridState,
|
|
4
|
+
GridInternalState as GridState,
|
|
5
5
|
GridRowModel,
|
|
6
6
|
GridColDef,
|
|
7
7
|
GridRowId,
|
|
@@ -19,27 +19,25 @@ type GridAction =
|
|
|
19
19
|
| { type: 'SET_FILTER_MODEL'; payload: GridFilterModel }
|
|
20
20
|
| { type: 'SET_PAGINATION_MODEL'; payload: GridPaginationModel }
|
|
21
21
|
| { type: 'SET_SELECTION'; payload: Set<GridRowId> }
|
|
22
|
-
| { type: 'SET_SCROLL'; payload: { scrollTop: number; scrollLeft: number } }
|
|
23
22
|
| { type: 'SET_DIMENSIONS'; payload: { viewportWidth: number; viewportHeight: number } }
|
|
24
23
|
| { type: 'SET_DATASOURCE_LOADING'; payload: boolean }
|
|
25
|
-
| { type: 'SET_DATASOURCE_ERROR'; payload:
|
|
24
|
+
| { type: 'SET_DATASOURCE_ERROR'; payload: unknown }
|
|
26
25
|
| { type: 'SET_ROW_COUNT'; payload: number };
|
|
27
26
|
|
|
28
|
-
function createInitialState(rows:
|
|
27
|
+
function createInitialState<R extends GridRowModel>(rows: R[], columns: GridColDef<R>[], columnVisibilityModel: GridColumnVisibilityModel = {}): GridState {
|
|
29
28
|
const idRowsLookup = new Map<GridRowId, GridRowModel>();
|
|
30
29
|
const allRows: GridRowId[] = [];
|
|
31
30
|
|
|
32
31
|
rows.forEach(row => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
allRows.push(id);
|
|
32
|
+
idRowsLookup.set(row.id, row);
|
|
33
|
+
allRows.push(row.id);
|
|
36
34
|
});
|
|
37
35
|
|
|
38
36
|
const columnLookup = new Map<string, GridColDef>();
|
|
39
37
|
const orderedFields: string[] = [];
|
|
40
38
|
|
|
41
39
|
columns.forEach(col => {
|
|
42
|
-
columnLookup.set(col.field, col);
|
|
40
|
+
columnLookup.set(col.field, col as unknown as GridColDef);
|
|
43
41
|
orderedFields.push(col.field);
|
|
44
42
|
});
|
|
45
43
|
|
|
@@ -49,7 +47,7 @@ function createInitialState(rows: GridRowModel[], columns: GridColDef[], columnV
|
|
|
49
47
|
allRows
|
|
50
48
|
},
|
|
51
49
|
columns: {
|
|
52
|
-
all: columns,
|
|
50
|
+
all: columns as unknown as GridColDef[],
|
|
53
51
|
lookup: columnLookup,
|
|
54
52
|
orderedFields,
|
|
55
53
|
columnVisibilityModel
|
|
@@ -78,8 +76,6 @@ function createInitialState(rows: GridRowModel[], columns: GridColDef[], columnV
|
|
|
78
76
|
firstColumnIndex: 0,
|
|
79
77
|
lastColumnIndex: 0
|
|
80
78
|
},
|
|
81
|
-
scrollTop: 0,
|
|
82
|
-
scrollLeft: 0
|
|
83
79
|
},
|
|
84
80
|
dimensions: {
|
|
85
81
|
rowHeight: 52,
|
|
@@ -137,7 +133,7 @@ function gridReducer(state: GridState, action: GridAction): GridState {
|
|
|
137
133
|
all: action.payload,
|
|
138
134
|
lookup: columnLookup,
|
|
139
135
|
orderedFields,
|
|
140
|
-
columnVisibilityModel:
|
|
136
|
+
columnVisibilityModel: state.columns.columnVisibilityModel
|
|
141
137
|
}
|
|
142
138
|
};
|
|
143
139
|
}
|
|
@@ -169,16 +165,6 @@ function gridReducer(state: GridState, action: GridAction): GridState {
|
|
|
169
165
|
selection: { selectedRows: action.payload }
|
|
170
166
|
};
|
|
171
167
|
|
|
172
|
-
case 'SET_SCROLL':
|
|
173
|
-
return {
|
|
174
|
-
...state,
|
|
175
|
-
virtualization: {
|
|
176
|
-
...state.virtualization,
|
|
177
|
-
scrollTop: action.payload.scrollTop,
|
|
178
|
-
scrollLeft: action.payload.scrollLeft
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
|
|
182
168
|
case 'SET_DIMENSIONS':
|
|
183
169
|
return {
|
|
184
170
|
...state,
|
|
@@ -234,18 +220,17 @@ export interface UseDataGridParams<R extends GridRowModel = GridRowModel> {
|
|
|
234
220
|
export function useDataGrid<R extends GridRowModel = GridRowModel>(params: UseDataGridParams<R>) {
|
|
235
221
|
const { rows, columns, rowHeight = 52, headerHeight = 56, columnVisibilityModel, initialState: propInitialState } = params;
|
|
236
222
|
|
|
237
|
-
const internalInitialState =
|
|
238
|
-
|
|
239
|
-
[]
|
|
223
|
+
const [internalInitialState] = useState(() =>
|
|
224
|
+
createInitialState(rows, columns, columnVisibilityModel)
|
|
240
225
|
);
|
|
241
226
|
|
|
242
227
|
const [state, dispatch] = useReducer(gridReducer, {
|
|
243
228
|
...internalInitialState,
|
|
244
229
|
sorting: propInitialState?.sorting || internalInitialState.sorting,
|
|
245
230
|
filter: propInitialState?.filter || internalInitialState.filter,
|
|
246
|
-
dataSource:
|
|
231
|
+
dataSource: propInitialState?.dataSource
|
|
247
232
|
? { ...internalInitialState.dataSource, ...propInitialState.dataSource }
|
|
248
|
-
: internalInitialState.dataSource
|
|
233
|
+
: internalInitialState.dataSource,
|
|
249
234
|
pagination: {
|
|
250
235
|
...internalInitialState.pagination,
|
|
251
236
|
...(propInitialState?.pagination || {}),
|
|
@@ -294,10 +279,6 @@ export function useDataGrid<R extends GridRowModel = GridRowModel>(params: UseDa
|
|
|
294
279
|
dispatch({ type: 'SET_SELECTION', payload: selectedRows });
|
|
295
280
|
}, []);
|
|
296
281
|
|
|
297
|
-
const setScroll = useCallback((scrollTop: number, scrollLeft: number) => {
|
|
298
|
-
dispatch({ type: 'SET_SCROLL', payload: { scrollTop, scrollLeft } });
|
|
299
|
-
}, []);
|
|
300
|
-
|
|
301
282
|
const setDimensions = useCallback((viewportWidth: number, viewportHeight: number) => {
|
|
302
283
|
dispatch({ type: 'SET_DIMENSIONS', payload: { viewportWidth, viewportHeight } });
|
|
303
284
|
}, []);
|
|
@@ -306,7 +287,7 @@ export function useDataGrid<R extends GridRowModel = GridRowModel>(params: UseDa
|
|
|
306
287
|
dispatch({ type: 'SET_DATASOURCE_LOADING', payload: loading });
|
|
307
288
|
}, []);
|
|
308
289
|
|
|
309
|
-
const setDataSourceError = useCallback((error:
|
|
290
|
+
const setDataSourceError = useCallback((error: unknown) => {
|
|
310
291
|
dispatch({ type: 'SET_DATASOURCE_ERROR', payload: error });
|
|
311
292
|
}, []);
|
|
312
293
|
|
|
@@ -383,7 +364,6 @@ export function useDataGrid<R extends GridRowModel = GridRowModel>(params: UseDa
|
|
|
383
364
|
setFilterModel,
|
|
384
365
|
setPaginationModel,
|
|
385
366
|
setSelection,
|
|
386
|
-
setScroll,
|
|
387
367
|
setDimensions,
|
|
388
368
|
setDataSourceLoading,
|
|
389
369
|
setDataSourceError,
|