@opencorestack/opengridx 0.1.6 → 0.1.8

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.
Files changed (130) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +9 -0
  3. package/dist/index.d.ts +12 -1
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +2505 -2450
  6. package/dist/opengridx.es.js.map +1 -1
  7. package/dist/opengridx.umd.js +12 -12
  8. package/dist/opengridx.umd.js.map +1 -1
  9. package/docs/API_REFERENCE.md +177 -0
  10. package/docs/README.md +60 -0
  11. package/docs/components/cell.md +44 -0
  12. package/docs/components/column-group-header.md +57 -0
  13. package/docs/components/column-resize.md +63 -0
  14. package/docs/components/column-visibility.md +61 -0
  15. package/docs/components/datagrid.md +70 -0
  16. package/docs/components/filter-panel.md +26 -0
  17. package/docs/components/header.md +39 -0
  18. package/docs/components/pagination.md +34 -0
  19. package/docs/components/row.md +31 -0
  20. package/docs/components/toolbar.md +40 -0
  21. package/docs/components/tooltip.md +68 -0
  22. package/docs/customization/slots-api.md +227 -0
  23. package/docs/customization/theming.md +293 -0
  24. package/docs/features/aggregation-pivot.md +79 -0
  25. package/docs/features/cell-spanning.md +520 -0
  26. package/docs/features/clipboard.md +76 -0
  27. package/docs/features/custom-pagination.md +143 -0
  28. package/docs/features/data-source.md +72 -0
  29. package/docs/features/editing-reordering.md +99 -0
  30. package/docs/features/export-guide.md +434 -0
  31. package/docs/features/filtering.md +83 -0
  32. package/docs/features/infinite-scroll.md +87 -0
  33. package/docs/features/list-view.md +65 -0
  34. package/docs/features/loading-states.md +68 -0
  35. package/docs/features/master-detail.md +46 -0
  36. package/docs/features/pinning.md +67 -0
  37. package/docs/features/selection.md +70 -0
  38. package/docs/features/sorting-pagination.md +66 -0
  39. package/docs/features/state-persistence.md +67 -0
  40. package/docs/features/tree-data-grouping.md +67 -0
  41. package/docs/features/virtualization.md +188 -0
  42. package/docs/roadmap.md +71 -0
  43. package/lib/components/Cell/Cell.css +119 -0
  44. package/lib/components/Cell/Cell.tsx +264 -0
  45. package/lib/components/Cell/GridEditInputCell.css +62 -0
  46. package/lib/components/Cell/GridEditInputCell.tsx +152 -0
  47. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +67 -0
  48. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +220 -0
  49. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +31 -0
  50. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +85 -0
  51. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +137 -0
  52. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +190 -0
  53. package/lib/components/DataGrid/DataGrid.css +224 -0
  54. package/lib/components/DataGrid/DataGrid.tsx +2269 -0
  55. package/lib/components/DetailPanel/DetailPanel.css +36 -0
  56. package/lib/components/DetailPanel/DetailPanel.tsx +34 -0
  57. package/lib/components/FilterPanel/FilterPanel.css +196 -0
  58. package/lib/components/FilterPanel/FilterPanel.tsx +214 -0
  59. package/lib/components/FilterPanel/index.ts +1 -0
  60. package/lib/components/Header/ColumnMenu.css +68 -0
  61. package/lib/components/Header/ColumnMenu.tsx +249 -0
  62. package/lib/components/Header/Header.css +224 -0
  63. package/lib/components/Header/Header.tsx +516 -0
  64. package/lib/components/ListView/ListView.css +112 -0
  65. package/lib/components/ListView/ListViewRow.tsx +63 -0
  66. package/lib/components/Pagination/Pagination.css +91 -0
  67. package/lib/components/Pagination/Pagination.tsx +151 -0
  68. package/lib/components/QuickFilter/QuickFilter.css +23 -0
  69. package/lib/components/QuickFilter/QuickFilter.tsx +79 -0
  70. package/lib/components/Row/Row.css +126 -0
  71. package/lib/components/Row/Row.tsx +377 -0
  72. package/lib/components/Skeleton/Skeleton.css +55 -0
  73. package/lib/components/Skeleton/Skeleton.tsx +22 -0
  74. package/lib/components/SkeletonRow/SkeletonRow.css +32 -0
  75. package/lib/components/SkeletonRow/SkeletonRow.tsx +89 -0
  76. package/lib/components/SkeletonRow/index.ts +1 -0
  77. package/lib/components/Toolbar/GlobalSearch.css +110 -0
  78. package/lib/components/Toolbar/GlobalSearch.tsx +167 -0
  79. package/lib/components/Toolbar/GridToolbar.css +221 -0
  80. package/lib/components/Toolbar/GridToolbar.tsx +658 -0
  81. package/lib/components/Toolbar/PivotPanel.css +212 -0
  82. package/lib/components/Toolbar/PivotPanel.tsx +239 -0
  83. package/lib/components/Tooltip/Tooltip.css +59 -0
  84. package/lib/components/Tooltip/Tooltip.tsx +106 -0
  85. package/lib/components/ui/Button.css +89 -0
  86. package/lib/components/ui/Button.tsx +39 -0
  87. package/lib/components/ui/Checkbox.css +71 -0
  88. package/lib/components/ui/Checkbox.tsx +66 -0
  89. package/lib/components/ui/DragHandleIcon.css +19 -0
  90. package/lib/components/ui/DragHandleIcon.tsx +18 -0
  91. package/lib/components/ui/ExpandIcon.css +38 -0
  92. package/lib/components/ui/ExpandIcon.tsx +75 -0
  93. package/lib/components/ui/Input.css +59 -0
  94. package/lib/components/ui/Input.tsx +45 -0
  95. package/lib/components/ui/index.ts +15 -0
  96. package/lib/hooks/core/useDataGrid.ts +392 -0
  97. package/lib/hooks/core/useGridApiRef.ts +10 -0
  98. package/lib/hooks/features/useAggregation.ts +143 -0
  99. package/lib/hooks/features/useGridClipboard.ts +133 -0
  100. package/lib/hooks/features/useGridDataSource.ts +190 -0
  101. package/lib/hooks/features/useGridEditing.ts +100 -0
  102. package/lib/hooks/features/useGridSpanning.ts +349 -0
  103. package/lib/hooks/features/usePivot.ts +174 -0
  104. package/lib/hooks/useColumnReorder.ts +114 -0
  105. package/lib/hooks/useRowGrouping.ts +302 -0
  106. package/lib/hooks/useRowReorder.ts +83 -0
  107. package/lib/hooks/useTreeData.ts +323 -0
  108. package/lib/index.ts +106 -0
  109. package/lib/state/index.ts +17 -0
  110. package/lib/state/types.ts +48 -0
  111. package/lib/state/useGridStateStorage.ts +113 -0
  112. package/lib/styles/base/reset.css +21 -0
  113. package/lib/styles/base/variables.css +161 -0
  114. package/lib/styles/opengridx.css +27 -0
  115. package/lib/styles/opengridx.css.d.ts +4 -0
  116. package/lib/theme/DataGridThemeProvider.tsx +276 -0
  117. package/lib/theme/defaultStyles.ts +0 -0
  118. package/lib/theme/index.ts +21 -0
  119. package/lib/theme/presets.ts +148 -0
  120. package/lib/theme/types.ts +155 -0
  121. package/lib/types/index.ts +764 -0
  122. package/lib/utils/export/exportToExcelAdvanced.ts +516 -0
  123. package/lib/utils/export/index.ts +550 -0
  124. package/lib/utils/filtering/index.ts +240 -0
  125. package/lib/utils/pinning/index.ts +282 -0
  126. package/lib/utils/sorting/index.ts +55 -0
  127. package/lib/utils/virtualization/index.ts +106 -0
  128. package/lib/vite-env.d.ts +1 -0
  129. package/llms.txt +2 -1
  130. package/package.json +12 -4
@@ -0,0 +1,323 @@
1
+ import { useMemo, useState, useCallback, useEffect } from 'react';
2
+ import { GridRowModel, GridRowId, GridTreeNode, GridFilterModel, GridSortItem } from '../types';
3
+ import { isRowMatchingFilter } from '../utils/filtering';
4
+ import { compareValues } from '../utils/sorting';
5
+
6
+ interface UseTreeDataProps<R extends GridRowModel> {
7
+ rows: R[];
8
+ getRowId: (row: R) => GridRowId;
9
+ getTreeDataPath?: (row: R) => string[];
10
+ treeData?: boolean;
11
+ defaultGroupingExpansionDepth?: number;
12
+ filterModel?: GridFilterModel;
13
+ sortModel?: GridSortItem[];
14
+ onRowExpansionChange?: (node: GridTreeNode) => void;
15
+ }
16
+
17
+ export function useTreeData<R extends GridRowModel>(props: UseTreeDataProps<R>) {
18
+ const {
19
+ rows,
20
+ getRowId,
21
+ getTreeDataPath,
22
+ treeData = false,
23
+ defaultGroupingExpansionDepth = 0,
24
+ filterModel,
25
+ sortModel,
26
+ onRowExpansionChange
27
+ } = props;
28
+
29
+ const { treeNodes, rootIds, groupingRows } = useMemo(() => {
30
+ if (!treeData || !getTreeDataPath) {
31
+ return {
32
+ treeNodes: new Map<GridRowId, GridTreeNode>(),
33
+ rootIds: [],
34
+ groupingRows: []
35
+ };
36
+ }
37
+
38
+ const nodes = new Map<GridRowId, GridTreeNode>();
39
+ const roots: GridRowId[] = [];
40
+ const groupRows: GridRowModel[] = [];
41
+ const pathLookup = new Map<string, GridRowId>();
42
+
43
+ const getPathKey = (path: string[]) => path.join('/');
44
+
45
+ rows.forEach(row => {
46
+ const id = getRowId(row);
47
+ const path = getTreeDataPath(row);
48
+ const pathKey = getPathKey(path);
49
+
50
+ pathLookup.set(pathKey, id);
51
+
52
+ nodes.set(id, {
53
+ id,
54
+ parentId: null,
55
+ depth: path.length - 1,
56
+ groupingKey: path[path.length - 1],
57
+ isExpanded: false,
58
+ children: [],
59
+ serverChildrenCount: row.serverChildrenCount
60
+ });
61
+ });
62
+
63
+ rows.forEach(row => {
64
+ const id = getRowId(row);
65
+ const path = getTreeDataPath(row);
66
+
67
+ if (path.length === 1) {
68
+ roots.push(id);
69
+ return;
70
+ }
71
+
72
+ const ensureAncestors = (childPath: string[]) => {
73
+ if (childPath.length <= 1) return;
74
+
75
+ const parentPath = childPath.slice(0, -1);
76
+ const parentPathKey = getPathKey(parentPath);
77
+ let parentId = pathLookup.get(parentPathKey);
78
+
79
+ if (parentId === undefined) {
80
+ parentId = `auto-group-${parentPathKey}`;
81
+
82
+ if (!nodes.has(parentId)) {
83
+ const groupLabel = parentPath[parentPath.length - 1];
84
+ nodes.set(parentId, {
85
+ id: parentId,
86
+ parentId: null,
87
+ depth: parentPath.length - 1,
88
+ groupingKey: groupLabel,
89
+ isExpanded: false,
90
+ children: [],
91
+ label: groupLabel
92
+ });
93
+ pathLookup.set(parentPathKey, parentId);
94
+
95
+ const groupRow = {
96
+ id: parentId,
97
+ name: groupLabel,
98
+ _isGroupRow: true
99
+ } as unknown as GridRowModel;
100
+ groupRows.push(groupRow);
101
+
102
+ if (parentPath.length === 1) {
103
+ roots.push(parentId);
104
+ }
105
+
106
+ ensureAncestors(parentPath);
107
+ }
108
+ }
109
+
110
+ return parentId;
111
+ };
112
+
113
+ const parentId = ensureAncestors(path);
114
+
115
+ if (parentId !== undefined) {
116
+ const parentNode = nodes.get(parentId);
117
+ const childNode = nodes.get(id);
118
+
119
+ if (parentNode && childNode) {
120
+ childNode.parentId = parentId;
121
+ parentNode.children = parentNode.children || [];
122
+ if (!parentNode.children.includes(id)) {
123
+ parentNode.children.push(id);
124
+ }
125
+ }
126
+ }
127
+ });
128
+
129
+ groupRows.forEach(groupRow => {
130
+ const groupId = groupRow.id;
131
+ const node = nodes.get(groupId);
132
+ if (!node) return;
133
+
134
+ const nodePathKey = (groupId as string).replace('auto-group-', '');
135
+ const nodePath = nodePathKey.split('/');
136
+
137
+ if (nodePath.length > 1) {
138
+ const parentPath = nodePath.slice(0, -1);
139
+ const parentPathKey = getPathKey(parentPath);
140
+ const parentId = pathLookup.get(parentPathKey);
141
+
142
+ if (parentId && nodes.has(parentId)) {
143
+ node.parentId = parentId;
144
+ const parentNode = nodes.get(parentId);
145
+ if (parentNode) {
146
+ parentNode.children = parentNode.children || [];
147
+ if (!parentNode.children.includes(groupId)) {
148
+ parentNode.children.push(groupId);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ });
154
+
155
+ return { treeNodes: nodes, rootIds: roots, groupingRows: groupRows };
156
+
157
+ }, [rows, getTreeDataPath, treeData, getRowId]);
158
+
159
+ const initialExpandedIds = useMemo(() => {
160
+ if (!treeData) return new Set<GridRowId>();
161
+
162
+ const initialExpanded = new Set<GridRowId>();
163
+ treeNodes.forEach(node => {
164
+ if (node.children && node.children.length > 0) {
165
+ if (defaultGroupingExpansionDepth === -1 || node.depth < defaultGroupingExpansionDepth) {
166
+ initialExpanded.add(node.id);
167
+ }
168
+ }
169
+ });
170
+ return initialExpanded;
171
+ }, [treeNodes, treeData, defaultGroupingExpansionDepth]);
172
+
173
+ const [expandedGroupIds, setExpandedGroupIds] = useState<Set<GridRowId>>(new Set());
174
+
175
+ useEffect(() => {
176
+ if (initialExpandedIds.size > 0) {
177
+ setExpandedGroupIds(initialExpandedIds);
178
+ }
179
+ }, [initialExpandedIds]);
180
+
181
+ const toggleExpansion = useCallback((id: GridRowId) => {
182
+ setExpandedGroupIds(prev => {
183
+ const next = new Set(prev);
184
+ const isExpanding = !next.has(id);
185
+
186
+ if (next.has(id)) {
187
+ next.delete(id);
188
+ } else {
189
+ next.add(id);
190
+ }
191
+
192
+ if (isExpanding && onRowExpansionChange) {
193
+ const node = treeNodes.get(id);
194
+ if (node) {
195
+ onRowExpansionChange(node);
196
+ }
197
+ }
198
+
199
+ return next;
200
+ });
201
+ }, [treeNodes, onRowExpansionChange]);
202
+
203
+ const getVisibleRows = useCallback(() => {
204
+
205
+ if (!treeData) return null;
206
+
207
+ const rowLookup = new Map<GridRowId, GridRowModel>();
208
+ rows.forEach(r => rowLookup.set(getRowId(r), r));
209
+ groupingRows.forEach(r => rowLookup.set(r.id, r));
210
+
211
+ const matchesFilter = (row: GridRowModel) => {
212
+ return isRowMatchingFilter(row, filterModel!);
213
+ };
214
+
215
+ const filterCache = new Map<GridRowId, boolean>();
216
+
217
+ const isNodeVisible = (id: GridRowId): boolean => {
218
+ if (filterCache.has(id)) return filterCache.get(id)!;
219
+
220
+ const row = rowLookup.get(id);
221
+ if (!row) return false;
222
+
223
+ const selfMatch = matchesFilter(row);
224
+ if (selfMatch) {
225
+ filterCache.set(id, true);
226
+ return true;
227
+ }
228
+
229
+ const node = treeNodes.get(id);
230
+ if (node && node.children) {
231
+ const childMatch = node.children.some(childId => isNodeVisible(childId));
232
+ if (childMatch) {
233
+ filterCache.set(id, true);
234
+ return true;
235
+ }
236
+ }
237
+
238
+ filterCache.set(id, false);
239
+ return false;
240
+ };
241
+
242
+ const sortNodes = (aId: GridRowId, bId: GridRowId) => {
243
+ if (!sortModel || sortModel.length === 0) return 0;
244
+ const rowA = rowLookup.get(aId);
245
+ const rowB = rowLookup.get(bId);
246
+ if (!rowA || !rowB) return 0;
247
+
248
+ for (const sortItem of sortModel) {
249
+ const valA = rowA[sortItem.field];
250
+ const valB = rowB[sortItem.field];
251
+ const comp = compareValues(valA, valB, sortItem.sort);
252
+ if (comp !== 0) return comp;
253
+ }
254
+ return 0;
255
+ };
256
+
257
+ const result: GridRowModel[] = [];
258
+
259
+ const seenIds = new Set<GridRowId>();
260
+
261
+ const traverse = (ids: GridRowId[]) => {
262
+
263
+ const visibleIds = ids.filter(id => isNodeVisible(id));
264
+
265
+ visibleIds.sort(sortNodes);
266
+
267
+ visibleIds.forEach(id => {
268
+ if (seenIds.has(id)) return;
269
+
270
+ const row = rowLookup.get(id);
271
+ const node = treeNodes.get(id);
272
+
273
+ if (row && node) {
274
+ seenIds.add(id);
275
+
276
+ const enhancedRow = {
277
+ ...row,
278
+ _treeDepth: node.depth,
279
+ _isExpanded: expandedGroupIds.has(id),
280
+ _hasChildren: (node.children && node.children.length > 0) || (node.serverChildrenCount && node.serverChildrenCount > 0),
281
+ _descendantCount: node.children ? node.children.length : 0
282
+ };
283
+ result.push(enhancedRow);
284
+
285
+ if (expandedGroupIds.has(id) && node.children && node.children.length > 0) {
286
+ traverse(node.children);
287
+ }
288
+ }
289
+ });
290
+ };
291
+
292
+ traverse(rootIds);
293
+ return result;
294
+
295
+ }, [treeData, rows, groupingRows, rootIds, treeNodes, expandedGroupIds, filterModel, sortModel, getRowId]);
296
+
297
+ const isGroupExpanded = useCallback((id: GridRowId) => expandedGroupIds.has(id), [expandedGroupIds]);
298
+ const getNode = useCallback((id: GridRowId) => treeNodes.get(id), [treeNodes]);
299
+
300
+ const getNodePath = useCallback((id: GridRowId): string[] => {
301
+ const path: string[] = [];
302
+ let currentId: GridRowId | null = id;
303
+
304
+ while (currentId !== null) {
305
+ const node = treeNodes.get(currentId);
306
+ if (!node) break;
307
+
308
+ path.unshift(node.groupingKey);
309
+ currentId = node.parentId;
310
+ }
311
+ return path;
312
+ }, [treeNodes]);
313
+
314
+ return useMemo(() => ({
315
+ treeNodes,
316
+ groupingRows,
317
+ toggleExpansion,
318
+ isGroupExpanded,
319
+ getVisibleRows,
320
+ getNode,
321
+ getNodePath
322
+ }), [treeNodes, groupingRows, toggleExpansion, isGroupExpanded, getVisibleRows, getNode, getNodePath]);
323
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,106 @@
1
+
2
+ // Auto-import styles — this ensures consumers do NOT need a manual CSS import
3
+ import './styles/opengridx.css';
4
+
5
+ export { DataGrid } from './components/DataGrid/DataGrid';
6
+ export { Cell } from './components/Cell/Cell';
7
+ export { Row } from './components/Row/Row';
8
+ export { Header } from './components/Header/Header';
9
+ export { Skeleton } from './components/Skeleton/Skeleton';
10
+ export { FilterPanel } from './components/FilterPanel';
11
+ export { GridToolbar } from './components/Toolbar/GridToolbar';
12
+ export type { GridToolbarProps } from './components/Toolbar/GridToolbar';
13
+ export { GridTooltip } from './components/Tooltip/Tooltip';
14
+
15
+ export { useGridApiRef } from './hooks/core/useGridApiRef';
16
+ export { Button, Input, Checkbox } from './components/ui';
17
+
18
+ export {
19
+ exportToCsv,
20
+ exportToExcel,
21
+ exportToJson,
22
+ printGrid
23
+ } from './utils/export/index';
24
+
25
+ export { exportToExcelAdvanced } from './utils/export/exportToExcelAdvanced';
26
+ export type {
27
+ ExcelAdvancedExportOptions,
28
+ ExcelSheetDefinition,
29
+ ExcelColumnStyle
30
+ } from './utils/export/exportToExcelAdvanced';
31
+
32
+ export { useAggregation, formatAggregationValue } from './hooks/features/useAggregation';
33
+ export type { UseAggregationParams, UseAggregationReturn, BuiltInAggFn } from './hooks/features/useAggregation';
34
+ export { usePivot } from './hooks/features/usePivot';
35
+ export type { UsePivotReturn } from './hooks/features/usePivot';
36
+
37
+ export { DataGridThemeProvider } from './theme';
38
+ export type { DataGridThemeProviderProps, GridTheme, GridThemeColors, GridThemeTypography, GridThemeSpacing, GridThemeBorders, GridThemeShadows, GridThemeGrid, GridThemeTransitions } from './theme';
39
+ export { darkTheme, roseTheme, emeraldTheme, amberTheme, compactTheme } from './theme';
40
+
41
+ export { useGridStateStorage } from './state';
42
+ export type {
43
+ GridState,
44
+ GridInitialState,
45
+ GridSortingState,
46
+ GridFilterState,
47
+ GridPaginationState,
48
+ GridColumnsState,
49
+ GridDensityState,
50
+ UseGridStateStorageOptions,
51
+ UseGridStateStorageReturn,
52
+ } from './state';
53
+
54
+ export type {
55
+ CsvExportOptions,
56
+ ExcelExportOptions,
57
+ JsonExportOptions,
58
+ PrintOptions
59
+ } from './utils/export/index';
60
+
61
+ export type {
62
+ GridRowModel,
63
+ GridRowId,
64
+ GridColDef,
65
+ GridAlignment,
66
+ GridSortDirection,
67
+ GridSortModel,
68
+ GridSortItem,
69
+ GridFilterModel,
70
+ GridFilterItem,
71
+ GridFilterOperator,
72
+ GridFilterGroup,
73
+ GridPaginationModel,
74
+ GridRenderCellParams,
75
+ GridRenderHeaderParams,
76
+ GridValueGetterParams,
77
+ GridValueFormatterParams,
78
+ GridRowParams,
79
+ GridCellParams,
80
+ GridColumnPinning,
81
+ GridRowPinning,
82
+ GridPinnedPosition,
83
+ GridColumnVisibilityModel,
84
+ GridRowSelectionModel,
85
+ GridColumnOrderChangeParams,
86
+ GridRowOrderChangeParams,
87
+ GridRowGroupingModel,
88
+ GridAggregationModel,
89
+ GridAggregationResult,
90
+ DataGridProps,
91
+ GridDataSource,
92
+ GridGetRowsParams,
93
+ GridGetRowsResponse,
94
+
95
+ GridPivotModel,
96
+ GridPivotValueField,
97
+ GridPivotAggFn,
98
+
99
+ GridListViewColDef,
100
+ GridColumnGroup,
101
+ GridColumnGroupingModel,
102
+
103
+ GridApi,
104
+ GridTreeNode,
105
+ GridDetailPanelParams,
106
+ } from './types';
@@ -0,0 +1,17 @@
1
+ export type {
2
+ GridState,
3
+ GridInitialState,
4
+ GridSortingState,
5
+ GridFilterState,
6
+ GridPaginationState,
7
+ GridColumnsState,
8
+ GridDensityState,
9
+ } from './types';
10
+
11
+ export {
12
+ useGridStateStorage,
13
+ } from './useGridStateStorage';
14
+ export type {
15
+ UseGridStateStorageOptions,
16
+ UseGridStateStorageReturn,
17
+ } from './useGridStateStorage';
@@ -0,0 +1,48 @@
1
+
2
+ import type {
3
+ GridSortItem,
4
+ GridFilterModel,
5
+ GridPaginationModel,
6
+ GridColumnPinning,
7
+ GridColumnOrder,
8
+ } from '../types';
9
+
10
+ export interface GridSortingState {
11
+ sortModel: GridSortItem[];
12
+ }
13
+
14
+ export interface GridFilterState {
15
+ filterModel: GridFilterModel;
16
+ }
17
+
18
+ export interface GridPaginationState {
19
+ paginationModel: GridPaginationModel;
20
+ rowCount?: number;
21
+ }
22
+
23
+ export interface GridDataSourceState {
24
+ loading: boolean;
25
+ error?: any;
26
+ }
27
+
28
+ export interface GridColumnsState {
29
+ columnWidths: Record<string, number>;
30
+ columnOrder: GridColumnOrder;
31
+ pinnedColumns?: GridColumnPinning;
32
+ columnVisibilityModel?: Record<string, boolean>;
33
+ }
34
+
35
+ export interface GridDensityState {
36
+ density: 'compact' | 'standard' | 'comfortable';
37
+ }
38
+
39
+ export interface GridState {
40
+ sorting?: GridSortingState;
41
+ filter?: GridFilterState;
42
+ pagination?: GridPaginationState;
43
+ columns?: GridColumnsState;
44
+ density?: GridDensityState;
45
+ dataSource?: GridDataSourceState;
46
+ }
47
+
48
+ export type GridInitialState = GridState;
@@ -0,0 +1,113 @@
1
+
2
+ import { useRef, useCallback, useMemo, useEffect } from 'react';
3
+ import type { GridState } from './types';
4
+
5
+ export interface UseGridStateStorageOptions {
6
+ key: string;
7
+ debounceMs?: number;
8
+ include?: (keyof GridState)[];
9
+ storage?: {
10
+ getItem: (key: string) => string | null;
11
+ setItem: (key: string, value: string) => void;
12
+ removeItem: (key: string) => void;
13
+ };
14
+ }
15
+
16
+ export interface UseGridStateStorageReturn {
17
+ initialState: GridState | undefined;
18
+ onStateChange: (state: GridState) => void;
19
+ clearState: () => void;
20
+ }
21
+
22
+ function readFromStorage(
23
+ key: string,
24
+ storage: UseGridStateStorageOptions['storage']
25
+ ): GridState | undefined {
26
+ try {
27
+ const raw = storage!.getItem(key);
28
+ if (!raw) return undefined;
29
+ return JSON.parse(raw) as GridState;
30
+ } catch {
31
+
32
+ return undefined;
33
+ }
34
+ }
35
+
36
+ function writeToStorage(
37
+ key: string,
38
+ state: GridState,
39
+ include: (keyof GridState)[] | undefined,
40
+ storage: UseGridStateStorageOptions['storage']
41
+ ) {
42
+ try {
43
+ const toWrite = include
44
+ ? Object.fromEntries(
45
+ Object.entries(state).filter(([k]) => include.includes(k as keyof GridState))
46
+ )
47
+ : state;
48
+ storage!.setItem(key, JSON.stringify(toWrite));
49
+ } catch {
50
+
51
+ }
52
+ }
53
+
54
+ export function useGridStateStorage(
55
+ options: UseGridStateStorageOptions | string
56
+ ): UseGridStateStorageReturn {
57
+
58
+ const opts: UseGridStateStorageOptions = typeof options === 'string'
59
+ ? { key: options }
60
+ : options;
61
+
62
+ const {
63
+ key,
64
+ debounceMs = 300,
65
+ include,
66
+ storage = typeof window !== 'undefined' ? window.localStorage : undefined,
67
+ } = opts;
68
+
69
+ const initialState = useMemo(() => {
70
+ if (!storage) return undefined;
71
+ return readFromStorage(key, storage);
72
+
73
+ }, [key]);
74
+
75
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
76
+ const latestStateRef = useRef<GridState | null>(null);
77
+
78
+ const flush = useCallback(() => {
79
+ if (latestStateRef.current && storage) {
80
+ writeToStorage(key, latestStateRef.current, include, storage);
81
+ }
82
+ }, [key, include, storage]);
83
+
84
+ const onStateChange = useCallback(
85
+ (state: GridState) => {
86
+ latestStateRef.current = state;
87
+
88
+ if (timerRef.current) {
89
+ clearTimeout(timerRef.current);
90
+ }
91
+
92
+ timerRef.current = setTimeout(flush, debounceMs);
93
+ },
94
+ [flush, debounceMs]
95
+ );
96
+
97
+ useEffect(() => {
98
+ return () => {
99
+ if (timerRef.current) {
100
+ clearTimeout(timerRef.current);
101
+ flush();
102
+ }
103
+ };
104
+ }, [flush]);
105
+
106
+ const clearState = useCallback(() => {
107
+ if (storage) {
108
+ storage.removeItem(key);
109
+ }
110
+ }, [key, storage]);
111
+
112
+ return { initialState, onStateChange, clearState };
113
+ }
@@ -0,0 +1,21 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+ .ogx--dragging {
7
+ user-select: none;
8
+ -webkit-user-select: none;
9
+ -moz-user-select: none;
10
+ -ms-user-select: none;
11
+ }
12
+ .ogx *:focus {
13
+ outline: none;
14
+ }
15
+ .ogx button:not(.ogx-menu-item) {
16
+ background: none;
17
+ border: none;
18
+ cursor: pointer;
19
+ font-family: inherit;
20
+ font-size: inherit;
21
+ }