@coreui/react-data-grid 0.1.0-rc.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.
Files changed (104) hide show
  1. package/LICENSE +3 -0
  2. package/README.md +33 -0
  3. package/dist/cjs/CDataGrid.d.ts +8 -0
  4. package/dist/cjs/CDataGrid.js +903 -0
  5. package/dist/cjs/CDataGrid.js.map +1 -0
  6. package/dist/cjs/CDataGridBody.d.ts +40 -0
  7. package/dist/cjs/CDataGridBody.js +144 -0
  8. package/dist/cjs/CDataGridBody.js.map +1 -0
  9. package/dist/cjs/CDataGridCellEditor.d.ts +8 -0
  10. package/dist/cjs/CDataGridCellEditor.js +67 -0
  11. package/dist/cjs/CDataGridCellEditor.js.map +1 -0
  12. package/dist/cjs/CDataGridColumnChooser.d.ts +23 -0
  13. package/dist/cjs/CDataGridColumnChooser.js +71 -0
  14. package/dist/cjs/CDataGridColumnChooser.js.map +1 -0
  15. package/dist/cjs/CDataGridFilterMenu.d.ts +22 -0
  16. package/dist/cjs/CDataGridFilterMenu.js +177 -0
  17. package/dist/cjs/CDataGridFilterMenu.js.map +1 -0
  18. package/dist/cjs/CDataGridHeader.d.ts +36 -0
  19. package/dist/cjs/CDataGridHeader.js +232 -0
  20. package/dist/cjs/CDataGridHeader.js.map +1 -0
  21. package/dist/cjs/CDataGridHeaderMenu.d.ts +29 -0
  22. package/dist/cjs/CDataGridHeaderMenu.js +127 -0
  23. package/dist/cjs/CDataGridHeaderMenu.js.map +1 -0
  24. package/dist/cjs/CDataGridPagination.d.ts +18 -0
  25. package/dist/cjs/CDataGridPagination.js +39 -0
  26. package/dist/cjs/CDataGridPagination.js.map +1 -0
  27. package/dist/cjs/CDataGridToolbar.d.ts +32 -0
  28. package/dist/cjs/CDataGridToolbar.js +49 -0
  29. package/dist/cjs/CDataGridToolbar.js.map +1 -0
  30. package/dist/cjs/active-cell.d.ts +22 -0
  31. package/dist/cjs/active-cell.js +89 -0
  32. package/dist/cjs/active-cell.js.map +1 -0
  33. package/dist/cjs/columns.d.ts +37 -0
  34. package/dist/cjs/columns.js +170 -0
  35. package/dist/cjs/columns.js.map +1 -0
  36. package/dist/cjs/icons.d.ts +22 -0
  37. package/dist/cjs/icons.js +40 -0
  38. package/dist/cjs/icons.js.map +1 -0
  39. package/dist/cjs/index.d.ts +12 -0
  40. package/dist/cjs/index.js +39 -0
  41. package/dist/cjs/index.js.map +1 -0
  42. package/dist/cjs/labels.d.ts +7 -0
  43. package/dist/cjs/pinning.d.ts +19 -0
  44. package/dist/cjs/pinning.js +32 -0
  45. package/dist/cjs/pinning.js.map +1 -0
  46. package/dist/cjs/toolbar.d.ts +22 -0
  47. package/dist/cjs/toolbar.js +43 -0
  48. package/dist/cjs/toolbar.js.map +1 -0
  49. package/dist/cjs/tooltip.d.ts +10 -0
  50. package/dist/cjs/tooltip.js +102 -0
  51. package/dist/cjs/tooltip.js.map +1 -0
  52. package/dist/cjs/types.d.ts +287 -0
  53. package/dist/esm/CDataGrid.d.ts +8 -0
  54. package/dist/esm/CDataGrid.mjs +901 -0
  55. package/dist/esm/CDataGrid.mjs.map +1 -0
  56. package/dist/esm/CDataGridBody.d.ts +40 -0
  57. package/dist/esm/CDataGridBody.mjs +142 -0
  58. package/dist/esm/CDataGridBody.mjs.map +1 -0
  59. package/dist/esm/CDataGridCellEditor.d.ts +8 -0
  60. package/dist/esm/CDataGridCellEditor.mjs +65 -0
  61. package/dist/esm/CDataGridCellEditor.mjs.map +1 -0
  62. package/dist/esm/CDataGridColumnChooser.d.ts +23 -0
  63. package/dist/esm/CDataGridColumnChooser.mjs +69 -0
  64. package/dist/esm/CDataGridColumnChooser.mjs.map +1 -0
  65. package/dist/esm/CDataGridFilterMenu.d.ts +22 -0
  66. package/dist/esm/CDataGridFilterMenu.mjs +175 -0
  67. package/dist/esm/CDataGridFilterMenu.mjs.map +1 -0
  68. package/dist/esm/CDataGridHeader.d.ts +36 -0
  69. package/dist/esm/CDataGridHeader.mjs +230 -0
  70. package/dist/esm/CDataGridHeader.mjs.map +1 -0
  71. package/dist/esm/CDataGridHeaderMenu.d.ts +29 -0
  72. package/dist/esm/CDataGridHeaderMenu.mjs +125 -0
  73. package/dist/esm/CDataGridHeaderMenu.mjs.map +1 -0
  74. package/dist/esm/CDataGridPagination.d.ts +18 -0
  75. package/dist/esm/CDataGridPagination.mjs +37 -0
  76. package/dist/esm/CDataGridPagination.mjs.map +1 -0
  77. package/dist/esm/CDataGridToolbar.d.ts +32 -0
  78. package/dist/esm/CDataGridToolbar.mjs +47 -0
  79. package/dist/esm/CDataGridToolbar.mjs.map +1 -0
  80. package/dist/esm/active-cell.d.ts +22 -0
  81. package/dist/esm/active-cell.mjs +86 -0
  82. package/dist/esm/active-cell.mjs.map +1 -0
  83. package/dist/esm/columns.d.ts +37 -0
  84. package/dist/esm/columns.mjs +163 -0
  85. package/dist/esm/columns.mjs.map +1 -0
  86. package/dist/esm/icons.d.ts +22 -0
  87. package/dist/esm/icons.mjs +24 -0
  88. package/dist/esm/icons.mjs.map +1 -0
  89. package/dist/esm/index.d.mts +1 -0
  90. package/dist/esm/index.d.ts +12 -0
  91. package/dist/esm/index.mjs +5 -0
  92. package/dist/esm/index.mjs.map +1 -0
  93. package/dist/esm/labels.d.ts +7 -0
  94. package/dist/esm/pinning.d.ts +19 -0
  95. package/dist/esm/pinning.mjs +29 -0
  96. package/dist/esm/pinning.mjs.map +1 -0
  97. package/dist/esm/toolbar.d.ts +22 -0
  98. package/dist/esm/toolbar.mjs +41 -0
  99. package/dist/esm/toolbar.mjs.map +1 -0
  100. package/dist/esm/tooltip.d.ts +10 -0
  101. package/dist/esm/tooltip.mjs +100 -0
  102. package/dist/esm/tooltip.mjs.map +1 -0
  103. package/dist/esm/types.d.ts +287 -0
  104. package/package.json +92 -0
@@ -0,0 +1,901 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { evaluateColumnFilter } from '@coreui/data-grid/filters';
3
+ import { useReactTable, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, getFacetedUniqueValues, getFacetedRowModel, getCoreRowModel } from '@tanstack/react-table';
4
+ import { forwardRef, useMemo, useRef, useState, useCallback, useEffect } from 'react';
5
+ import { clampActiveCell, resolveNavigation } from './active-cell.mjs';
6
+ import { CDataGridBody } from './CDataGridBody.mjs';
7
+ import { CDataGridCellEditor } from './CDataGridCellEditor.mjs';
8
+ import { CDataGridHeader } from './CDataGridHeader.mjs';
9
+ import { CDataGridPagination } from './CDataGridPagination.mjs';
10
+ import { CDataGridToolbar } from './CDataGridToolbar.mjs';
11
+ import { visibleColumns } from './columns.mjs';
12
+ import { DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON, DEFAULT_MOVE_LEFT_ICON, DEFAULT_MOVE_RIGHT_ICON, DEFAULT_PIN_LEFT_ICON, DEFAULT_PIN_RIGHT_ICON, DEFAULT_SORT_ASCENDING_ICON, DEFAULT_SORT_DESCENDING_ICON, DEFAULT_SORT_NEUTRAL_ICON, DEFAULT_TOOLBAR_COLUMNS_ICON, DEFAULT_TOOLBAR_EXPORT_ICON, DEFAULT_TOOLBAR_REDO_ICON, DEFAULT_TOOLBAR_UNDO_ICON, DEFAULT_UNPIN_ICON } from './icons.mjs';
13
+ import { DEFAULT_LABELS, interpolate } from '@coreui/data-grid/labels';
14
+ import { resolveToolbar } from './toolbar.mjs';
15
+ import { createTooltip } from './tooltip.mjs';
16
+
17
+ const DEFAULT_PAGE_SIZE_OPTIONS = [5, 10, 20, 50];
18
+ const STATE_STORAGE_PREFIX = 'coreui-data-grid:';
19
+ const HISTORY_LIMIT = 100;
20
+ const readPersistedState = (stateKey) => {
21
+ if (!stateKey) {
22
+ return null;
23
+ }
24
+ try {
25
+ const raw = window.localStorage.getItem(`${STATE_STORAGE_PREFIX}${stateKey}`);
26
+ return raw ? JSON.parse(raw) : null;
27
+ }
28
+ catch (_a) {
29
+ return null;
30
+ }
31
+ };
32
+ const FETCH_DEBOUNCE_MS = 100;
33
+ // Module-level so an omitted `items` prop keeps a stable identity - a fresh
34
+ // array per render would force TanStack to recompute row models every time.
35
+ const EMPTY_ITEMS = [];
36
+ const toTanStackFilterFn = (column) => {
37
+ const { filterFn } = column;
38
+ if (filterFn) {
39
+ return (row, columnId, filterValue) => filterFn(row.getValue(columnId), filterValue, row.original);
40
+ }
41
+ return (row, columnId, filterValue) => evaluateColumnFilter(row.getValue(columnId), filterValue, column.filterType);
42
+ };
43
+ const CDataGrid = forwardRef((props, ref) => {
44
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
45
+ const { cellNavigation = false, className, columnFilters = false, columnMenu = false, columnMenuIcon = DEFAULT_COLUMN_MENU_ICON, columnOrder = false, columnPinning = false, columnSizing = false, columnVisibility = false, columns, dataProvider, editing = false, empty = 'No items found', filterIcon = DEFAULT_FILTER_ICON, globalFilter = false, hideColumnIcon = DEFAULT_HIDE_COLUMN_ICON, history = false, itemKey, items = EMPTY_ITEMS, labels, moveLeftIcon = DEFAULT_MOVE_LEFT_ICON, moveRightIcon = DEFAULT_MOVE_RIGHT_ICON, onDataError, onDataLoad, onEditCancel, onEditCommit, onEditStart, onFilterChange, onOrderChange, onPaginationChange, onPinningChange, onSelectionChange, onSizingChange, onSortingChange, onVisibilityChange, overscan = 10, pagination = false, pinLeftIcon = DEFAULT_PIN_LEFT_ICON, pinRightIcon = DEFAULT_PIN_RIGHT_ICON, rowHeight = 44, rowSelection = false, slots, sortAscendingIcon = DEFAULT_SORT_ASCENDING_ICON, sortDescendingIcon = DEFAULT_SORT_DESCENDING_ICON, sortNeutralIcon = DEFAULT_SORT_NEUTRAL_ICON, sorterVisibility, sorting = true, stateKey, tableRef, toolbar = false, toolbarColumnsIcon = DEFAULT_TOOLBAR_COLUMNS_ICON, toolbarExportIcon = DEFAULT_TOOLBAR_EXPORT_ICON, toolbarRedoIcon = DEFAULT_TOOLBAR_REDO_ICON, toolbarUndoIcon = DEFAULT_TOOLBAR_UNDO_ICON, unpinIcon = DEFAULT_UNPIN_ICON, virtualization = true, ...rest } = props;
46
+ const mergedLabels = useMemo(() => ({ ...DEFAULT_LABELS, ...labels }), [labels]);
47
+ const menuIcons = useMemo(() => ({
48
+ columnMenu: columnMenuIcon,
49
+ filter: filterIcon,
50
+ hideColumn: hideColumnIcon,
51
+ moveLeft: moveLeftIcon,
52
+ moveRight: moveRightIcon,
53
+ pinLeft: pinLeftIcon,
54
+ pinRight: pinRightIcon,
55
+ sortAscending: sortAscendingIcon,
56
+ sortDescending: sortDescendingIcon,
57
+ sortNeutral: sortNeutralIcon,
58
+ unpin: unpinIcon
59
+ }), [
60
+ columnMenuIcon,
61
+ filterIcon,
62
+ hideColumnIcon,
63
+ moveLeftIcon,
64
+ moveRightIcon,
65
+ pinLeftIcon,
66
+ pinRightIcon,
67
+ sortAscendingIcon,
68
+ sortDescendingIcon,
69
+ sortNeutralIcon,
70
+ unpinIcon
71
+ ]);
72
+ const menuBuilder = typeof columnMenu === 'function' ? columnMenu : null;
73
+ // Editing needs the active-cell substrate (Enter/F2 target the active cell).
74
+ const cellNavigationEnabled = cellNavigation || editing;
75
+ const sortingOptions = typeof sorting === 'object' ? sorting : {};
76
+ const sizingEnabled = Boolean(columnSizing);
77
+ const sizingOptions = typeof columnSizing === 'object' ? columnSizing : {};
78
+ const pinningEnabled = Boolean(columnPinning);
79
+ const widthsEnabled = sizingEnabled || pinningEnabled;
80
+ const selectionEnabled = Boolean(rowSelection);
81
+ const selectionOptions = typeof rowSelection === 'object' ? rowSelection : {};
82
+ const serverSide = Boolean(dataProvider);
83
+ // Server-side mode implies pagination — virtualized infinite scroll over a
84
+ // dataProvider is out of scope until 0.3.0.
85
+ const paginationEnabled = Boolean(pagination) || serverSide;
86
+ const paginationOptions = typeof pagination === 'object' ? pagination : {};
87
+ const virtualizationEnabled = virtualization && !paginationEnabled;
88
+ const resolvedToolbar = resolveToolbar({
89
+ columnVisibility: Boolean(columnVisibility),
90
+ globalFilter,
91
+ history,
92
+ toolbar
93
+ });
94
+ const isColumnSortable = (column) => Boolean(sorting) && column.sortable !== false;
95
+ const isColumnFilterable = (column) => columnFilters && column.filterable !== false;
96
+ const isColumnResizable = (column) => sizingEnabled && column.resizable !== false;
97
+ // A persisted view (stateKey) seeds the initial state - read once.
98
+ const persistedStateRef = useRef(undefined);
99
+ if (persistedStateRef.current === undefined) {
100
+ persistedStateRef.current = readPersistedState(stateKey);
101
+ }
102
+ const persisted = persistedStateRef.current;
103
+ const [sortingState, setSortingState] = useState((_a = persisted === null || persisted === void 0 ? void 0 : persisted.sorting) !== null && _a !== void 0 ? _a : []);
104
+ const [columnFiltersState, setColumnFiltersState] = useState((_b = persisted === null || persisted === void 0 ? void 0 : persisted.columnFilters) !== null && _b !== void 0 ? _b : []);
105
+ const [globalFilterState, setGlobalFilterState] = useState((persisted === null || persisted === void 0 ? void 0 : persisted.globalFilter) === undefined || persisted.globalFilter === '' ? undefined : persisted.globalFilter);
106
+ const [rowSelectionState, setRowSelectionState] = useState((_c = persisted === null || persisted === void 0 ? void 0 : persisted.rowSelection) !== null && _c !== void 0 ? _c : {});
107
+ const [columnSizingState, setColumnSizingState] = useState((_d = persisted === null || persisted === void 0 ? void 0 : persisted.columnSizing) !== null && _d !== void 0 ? _d : {});
108
+ const [columnPinningState, setColumnPinningState] = useState((_e = persisted === null || persisted === void 0 ? void 0 : persisted.columnPinning) !== null && _e !== void 0 ? _e : (typeof columnPinning === 'object' ? columnPinning : {}));
109
+ const [columnOrderState, setColumnOrderState] = useState((_f = persisted === null || persisted === void 0 ? void 0 : persisted.columnOrder) !== null && _f !== void 0 ? _f : (Array.isArray(columnOrder) ? columnOrder : []));
110
+ const [columnVisibilityState, setColumnVisibilityState] = useState((_g = persisted === null || persisted === void 0 ? void 0 : persisted.columnVisibility) !== null && _g !== void 0 ? _g : (typeof columnVisibility === 'object' ? columnVisibility : {}));
111
+ // The chooser's Reset restores the initial columnVisibility object, captured
112
+ // once (initial-value-only, like the state seed above).
113
+ const initialColumnVisibility = useRef(typeof columnVisibility === 'object' ? columnVisibility : {});
114
+ const [paginationState, setPaginationState] = useState({
115
+ pageIndex: (_j = (_h = persisted === null || persisted === void 0 ? void 0 : persisted.pagination) === null || _h === void 0 ? void 0 : _h.pageIndex) !== null && _j !== void 0 ? _j : 0,
116
+ pageSize: (_m = (_l = (_k = persisted === null || persisted === void 0 ? void 0 : persisted.pagination) === null || _k === void 0 ? void 0 : _k.pageSize) !== null && _l !== void 0 ? _l : paginationOptions.pageSize) !== null && _m !== void 0 ? _m : 10
117
+ });
118
+ const [activeCell, setActiveCell] = useState(null);
119
+ // A pending focus request survives renders until the target cell exists in
120
+ // the DOM (the body's focus pass consumes it) - focus is derived from
121
+ // state, never from what the recycler left behind.
122
+ const focusRequestRef = useRef(null);
123
+ const containsFocusRef = useRef(false);
124
+ const scrollToIndexRef = useRef(null);
125
+ // The ref mirrors the state so keydown/pointerdown handlers stay
126
+ // synchronous (start-then-stop in one handler must not race renders).
127
+ const [editingCell, setEditingCellState] = useState(null);
128
+ const editingCellRef = useRef(null);
129
+ const setEditingCell = (next) => {
130
+ editingCellRef.current = next;
131
+ setEditingCellState(next);
132
+ };
133
+ const [editorInvalid, setEditorInvalid] = useState(null);
134
+ const [editorPopupStyle, setEditorPopupStyle] = useState(null);
135
+ const editorHandleRef = useRef(null);
136
+ // The draft survives the editor's unmount (a recycled editing row) so the
137
+ // scroll-away commit still carries the value.
138
+ const editorDraftRef = useRef(null);
139
+ const editorFocusPendingRef = useRef(false);
140
+ const registerEditorHandle = (handle) => {
141
+ var _a;
142
+ if (handle === null && ((_a = editorHandleRef.current) === null || _a === void 0 ? void 0 : _a.getValue) && editingCellRef.current) {
143
+ editorDraftRef.current = { value: editorHandleRef.current.getValue() };
144
+ }
145
+ editorHandleRef.current = handle;
146
+ if (handle !== null) {
147
+ editorDraftRef.current = null;
148
+ }
149
+ };
150
+ const [serverItems, setServerItems] = useState([]);
151
+ const [serverTotalRows, setServerTotalRows] = useState(0);
152
+ const [loading, setLoading] = useState(false);
153
+ const [loadFailed, setLoadFailed] = useState(false);
154
+ const [announcement, setAnnouncement] = useState('');
155
+ const columnDefs = useMemo(() => columns.map(column => {
156
+ var _a;
157
+ const size = (_a = column.width) !== null && _a !== void 0 ? _a : null;
158
+ return {
159
+ accessorKey: column.key,
160
+ enableColumnFilter: column.filterable !== false,
161
+ enableHiding: column.hideable !== false,
162
+ enableResizing: column.resizable !== false,
163
+ enableSorting: column.sortable !== false,
164
+ filterFn: toTanStackFilterFn(column),
165
+ id: column.key,
166
+ meta: { label: column.label, formatter: column.formatter },
167
+ ...(widthsEnabled && size !== null && { size })
168
+ };
169
+ }), [columns, widthsEnabled]);
170
+ const table = useReactTable({
171
+ columns: columnDefs,
172
+ columnResizeMode: (_o = sizingOptions.mode) !== null && _o !== void 0 ? _o : 'onChange',
173
+ data: serverSide ? serverItems : items,
174
+ enableColumnFilters: columnFilters,
175
+ enableColumnPinning: pinningEnabled,
176
+ enableColumnResizing: sizingEnabled,
177
+ enableGlobalFilter: resolvedToolbar.search,
178
+ enableHiding: Boolean(columnVisibility),
179
+ enableMultiSort: sortingOptions.multiple !== false,
180
+ enableRowSelection: selectionEnabled,
181
+ enableSorting: Boolean(sorting),
182
+ enableSortingRemoval: Boolean(sortingOptions.resetable),
183
+ getCoreRowModel: getCoreRowModel(),
184
+ // Faceting powers custom filter UIs (column.getFacetedUniqueValues()).
185
+ getFacetedRowModel: getFacetedRowModel(),
186
+ getFacetedUniqueValues: getFacetedUniqueValues(),
187
+ getFilteredRowModel: getFilteredRowModel(),
188
+ getSortedRowModel: getSortedRowModel(),
189
+ globalFilterFn: 'includesString',
190
+ ...(serverSide && {
191
+ manualFiltering: true,
192
+ manualPagination: true,
193
+ manualSorting: true,
194
+ rowCount: serverTotalRows
195
+ }),
196
+ ...(paginationEnabled && { getPaginationRowModel: getPaginationRowModel() }),
197
+ ...(itemKey && { getRowId: itemKey }),
198
+ onColumnFiltersChange: setColumnFiltersState,
199
+ onColumnOrderChange: setColumnOrderState,
200
+ onColumnPinningChange: setColumnPinningState,
201
+ onColumnSizingChange: setColumnSizingState,
202
+ onColumnVisibilityChange: setColumnVisibilityState,
203
+ onGlobalFilterChange: setGlobalFilterState,
204
+ onPaginationChange: setPaginationState,
205
+ onRowSelectionChange: setRowSelectionState,
206
+ onSortingChange: setSortingState,
207
+ state: {
208
+ columnFilters: columnFiltersState,
209
+ columnOrder: columnOrderState,
210
+ columnPinning: columnPinningState,
211
+ columnSizing: columnSizingState,
212
+ columnVisibility: columnVisibilityState,
213
+ globalFilter: globalFilterState,
214
+ pagination: paginationState,
215
+ rowSelection: rowSelectionState,
216
+ sorting: sortingState
217
+ }
218
+ });
219
+ const rootRef = useRef(null);
220
+ // Stable across renders - an inline callback ref would be detached/reattached
221
+ // (null, node) on every render, hammering consumer callback refs.
222
+ const setRootRef = useCallback((node) => {
223
+ rootRef.current = node;
224
+ if (typeof ref === 'function') {
225
+ ref(node);
226
+ }
227
+ else if (ref) {
228
+ ref.current = node;
229
+ }
230
+ }, [ref]);
231
+ // The viewport element travels to the body as STATE, not a ref - the body's
232
+ // virtualizer reads it in a layout effect that runs before an ancestor ref
233
+ // would be attached, so a plain ref would still be null there on mount.
234
+ const [viewportElement, setViewportElement] = useState(null);
235
+ useEffect(() => {
236
+ if (!tableRef) {
237
+ return;
238
+ }
239
+ if (typeof tableRef === 'function') {
240
+ tableRef(table);
241
+ return () => {
242
+ tableRef(null);
243
+ };
244
+ }
245
+ tableRef.current = table;
246
+ return () => {
247
+ tableRef.current = null;
248
+ };
249
+ }, [table, tableRef]);
250
+ useEffect(() => {
251
+ if (!rootRef.current) {
252
+ return;
253
+ }
254
+ const tooltip = createTooltip(rootRef.current);
255
+ return () => tooltip.dispose();
256
+ }, []);
257
+ const requestIdRef = useRef(0);
258
+ const lastFilterKeyRef = useRef(null);
259
+ useEffect(() => {
260
+ if (!dataProvider) {
261
+ return;
262
+ }
263
+ const filterKey = JSON.stringify([sortingState, columnFiltersState, globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '']);
264
+ const filtersChanged = lastFilterKeyRef.current !== null && lastFilterKeyRef.current !== filterKey;
265
+ lastFilterKeyRef.current = filterKey;
266
+ if (filtersChanged && paginationState.pageIndex !== 0) {
267
+ // Back to page one first; the pagination change re-runs this effect and
268
+ // fetches exactly once.
269
+ setPaginationState(previous => ({ ...previous, pageIndex: 0 }));
270
+ return;
271
+ }
272
+ const id = ++requestIdRef.current;
273
+ const timeout = setTimeout(async () => {
274
+ setLoading(true);
275
+ try {
276
+ const response = await dataProvider({
277
+ columnFilters: columnFiltersState,
278
+ globalFilter: globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '',
279
+ pagination: paginationState,
280
+ sorting: sortingState
281
+ });
282
+ if (id !== requestIdRef.current) {
283
+ return;
284
+ }
285
+ setServerItems(response.items);
286
+ setServerTotalRows(response.totalRows);
287
+ setLoadFailed(false);
288
+ setAnnouncement(interpolate(mergedLabels.resultsAnnouncement, { count: response.totalRows }));
289
+ onDataLoad === null || onDataLoad === void 0 ? void 0 : onDataLoad(response);
290
+ }
291
+ catch (error) {
292
+ if (id !== requestIdRef.current) {
293
+ return;
294
+ }
295
+ setServerItems([]);
296
+ setServerTotalRows(0);
297
+ setLoadFailed(true);
298
+ onDataError === null || onDataError === void 0 ? void 0 : onDataError(error);
299
+ }
300
+ finally {
301
+ if (id === requestIdRef.current) {
302
+ setLoading(false);
303
+ }
304
+ }
305
+ }, FETCH_DEBOUNCE_MS);
306
+ return () => clearTimeout(timeout);
307
+ // eslint-disable-next-line react-hooks/exhaustive-deps
308
+ }, [dataProvider, sortingState, columnFiltersState, globalFilterState, paginationState]);
309
+ const mounted = useRef(false);
310
+ const totalRows = serverSide ? serverTotalRows : table.getFilteredRowModel().rows.length;
311
+ useEffect(() => {
312
+ if (mounted.current) {
313
+ onSortingChange === null || onSortingChange === void 0 ? void 0 : onSortingChange(sortingState);
314
+ }
315
+ // eslint-disable-next-line react-hooks/exhaustive-deps
316
+ }, [sortingState]);
317
+ useEffect(() => {
318
+ if (mounted.current) {
319
+ onFilterChange === null || onFilterChange === void 0 ? void 0 : onFilterChange(columnFiltersState, globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '');
320
+ }
321
+ // eslint-disable-next-line react-hooks/exhaustive-deps
322
+ }, [columnFiltersState, globalFilterState]);
323
+ useEffect(() => {
324
+ if (mounted.current) {
325
+ onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(rowSelectionState, table.getSelectedRowModel().rows.map(row => row.original));
326
+ }
327
+ // eslint-disable-next-line react-hooks/exhaustive-deps
328
+ }, [rowSelectionState]);
329
+ useEffect(() => {
330
+ if (mounted.current) {
331
+ onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(paginationState);
332
+ }
333
+ // eslint-disable-next-line react-hooks/exhaustive-deps
334
+ }, [paginationState]);
335
+ useEffect(() => {
336
+ if (mounted.current) {
337
+ onSizingChange === null || onSizingChange === void 0 ? void 0 : onSizingChange(columnSizingState);
338
+ }
339
+ // eslint-disable-next-line react-hooks/exhaustive-deps
340
+ }, [columnSizingState]);
341
+ useEffect(() => {
342
+ if (mounted.current) {
343
+ onPinningChange === null || onPinningChange === void 0 ? void 0 : onPinningChange(columnPinningState);
344
+ }
345
+ // eslint-disable-next-line react-hooks/exhaustive-deps
346
+ }, [columnPinningState]);
347
+ useEffect(() => {
348
+ if (mounted.current) {
349
+ onOrderChange === null || onOrderChange === void 0 ? void 0 : onOrderChange(columnOrderState);
350
+ }
351
+ // eslint-disable-next-line react-hooks/exhaustive-deps
352
+ }, [columnOrderState]);
353
+ useEffect(() => {
354
+ if (mounted.current) {
355
+ onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(columnVisibilityState);
356
+ }
357
+ // eslint-disable-next-line react-hooks/exhaustive-deps
358
+ }, [columnVisibilityState]);
359
+ useEffect(() => {
360
+ if (mounted.current && !serverSide) {
361
+ setAnnouncement(interpolate(mergedLabels.resultsAnnouncement, { count: totalRows }));
362
+ }
363
+ // eslint-disable-next-line react-hooks/exhaustive-deps
364
+ }, [columnFiltersState, globalFilterState]);
365
+ // Pagination joins by VALUE: a data replacement makes TanStack auto-reset
366
+ // the page index, which replaces the state object without changing it -
367
+ // identity alone would kill an edit right after its onEditCommit handler
368
+ // applied the change.
369
+ const rowSetSignatureRef = useRef(null);
370
+ useEffect(() => {
371
+ // The active cell indexes into the current row set; any transition that
372
+ // reshuffles rows would make it point at an arbitrary one. An in-flight
373
+ // edit stops (commit) first. A pending focus request marks an intentional
374
+ // navigation (PageUp/PageDown page flip) - keep the cell it just set.
375
+ const signature = JSON.stringify([
376
+ sortingState, columnFiltersState,
377
+ globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '',
378
+ paginationState.pageIndex, paginationState.pageSize
379
+ ]);
380
+ if (rowSetSignatureRef.current === signature) {
381
+ return;
382
+ }
383
+ rowSetSignatureRef.current = signature;
384
+ if (mounted.current) {
385
+ stopEditingRef.current({ commit: true, force: true });
386
+ if (!focusRequestRef.current) {
387
+ setActiveCell(null);
388
+ }
389
+ }
390
+ }, [sortingState, columnFiltersState, globalFilterState, paginationState]);
391
+ useEffect(() => {
392
+ mounted.current = true;
393
+ // Reset on unmount, or StrictMode's mount-unmount-remount cycle would fire
394
+ // every change callback once with the initial state.
395
+ return () => {
396
+ mounted.current = false;
397
+ };
398
+ }, []);
399
+ const hasCustomFilterRow = columnFilters && columns.some(column => column.filter && column.filterable !== false);
400
+ const headerRowCount = hasCustomFilterRow ? 2 : 1;
401
+ const position = (_p = paginationOptions.position) !== null && _p !== void 0 ? _p : 'bottom';
402
+ const rowCount = table.getRowModel().rows.length;
403
+ const visibleColumnIds = visibleColumns(table, columns).map(column => column.key);
404
+ const activeCellContext = () => {
405
+ var _a;
406
+ return ({
407
+ columnIds: visibleColumnIds,
408
+ rowCount,
409
+ pageStep: paginationEnabled ?
410
+ paginationState.pageSize :
411
+ Math.max(1, Math.floor(((_a = viewportElement === null || viewportElement === void 0 ? void 0 : viewportElement.clientHeight) !== null && _a !== void 0 ? _a : 0) / rowHeight) || rowCount),
412
+ paginated: paginationEnabled
413
+ });
414
+ };
415
+ // Clamped for rendering only - the state stays as set and re-clamps as the
416
+ // column set or row count changes.
417
+ const effectiveActiveCell = cellNavigationEnabled ? clampActiveCell(activeCell, activeCellContext()) : null;
418
+ // With no active cell yet, the first data cell (or first header cell in an
419
+ // empty grid) is the grid's single tab stop.
420
+ const tabStop = cellNavigationEnabled ?
421
+ effectiveActiveCell !== null && effectiveActiveCell !== void 0 ? effectiveActiveCell : (visibleColumnIds.length > 0 ?
422
+ { rowIndex: rowCount > 0 ? 0 : -1, columnId: visibleColumnIds[0] } :
423
+ null) :
424
+ null;
425
+ // Maps an event target to the navigable cell that holds it: a data cell
426
+ // (rowIndex from data-coreui-index) or a header label-row cell (rowIndex
427
+ // -1). Selection cells, the filter row, and grid chrome map to null.
428
+ const navigableCellFromTarget = (target) => {
429
+ const cellElement = target.closest('td, th');
430
+ const tableElement = viewportElement === null || viewportElement === void 0 ? void 0 : viewportElement.querySelector('table');
431
+ if (!cellElement || !(tableElement === null || tableElement === void 0 ? void 0 : tableElement.contains(cellElement))) {
432
+ return null;
433
+ }
434
+ const columnId = cellElement.dataset.coreuiColumn;
435
+ if (!columnId) {
436
+ return null;
437
+ }
438
+ const rowElement = cellElement.closest('tr');
439
+ if (rowElement.closest('tbody')) {
440
+ const index = rowElement.dataset.coreuiIndex;
441
+ if (index === undefined) {
442
+ return null;
443
+ }
444
+ return { cellElement, cell: { rowIndex: Number(index), columnId } };
445
+ }
446
+ const headerElement = rowElement.closest('thead');
447
+ if (headerElement && rowElement === headerElement.querySelector('tr')) {
448
+ return { cellElement, cell: { rowIndex: -1, columnId } };
449
+ }
450
+ return null;
451
+ };
452
+ const requestActiveCell = (cell) => {
453
+ var _a;
454
+ focusRequestRef.current = cell;
455
+ setActiveCell(cell);
456
+ if (virtualizationEnabled && cell.rowIndex >= 0) {
457
+ // No-op when the row is already in view (align: auto); otherwise the
458
+ // focus request stays pending until the row renders.
459
+ (_a = scrollToIndexRef.current) === null || _a === void 0 ? void 0 : _a.call(scrollToIndexRef, cell.rowIndex);
460
+ }
461
+ };
462
+ const isColumnEditable = (column) =>
463
+ // Opt-IN, unlike the opt-out feature booleans - editing is invasive. A
464
+ // custom editor is itself the opt-in.
465
+ editing && (Boolean(column.editor) || column.editable === true || typeof column.editable === 'object');
466
+ const editingCellElement = (session) => {
467
+ var _a;
468
+ return (_a = viewportElement === null || viewportElement === void 0 ? void 0 : viewportElement.querySelector(`tbody tr[data-coreui-index="${session.rowIndex}"] td[data-coreui-column="${session.columnId}"]`)) !== null && _a !== void 0 ? _a : null;
469
+ };
470
+ // The edit scope: the editing cell, its popup layer, and anything the
471
+ // editor claims via contains() (portaled overlays).
472
+ const editScopeContains = (target) => {
473
+ var _a, _b, _c, _d, _e;
474
+ const session = editingCellRef.current;
475
+ if (!target || !session) {
476
+ return false;
477
+ }
478
+ if ((_b = (_a = editorHandleRef.current) === null || _a === void 0 ? void 0 : _a.contains) === null || _b === void 0 ? void 0 : _b.call(_a, target)) {
479
+ return true;
480
+ }
481
+ if ((_d = (_c = rootRef.current) === null || _c === void 0 ? void 0 : _c.querySelector('.data-grid-editor-popup')) === null || _d === void 0 ? void 0 : _d.contains(target)) {
482
+ return true;
483
+ }
484
+ return Boolean((_e = editingCellElement(session)) === null || _e === void 0 ? void 0 : _e.contains(target));
485
+ };
486
+ const finishEdit = (session) => {
487
+ editorHandleRef.current = null;
488
+ setEditingCell(null);
489
+ setEditorInvalid(null);
490
+ setEditorPopupStyle(null);
491
+ const cell = { rowIndex: session.rowIndex, columnId: session.columnId };
492
+ focusRequestRef.current = cell;
493
+ setActiveCell(cell);
494
+ };
495
+ const commitEdit = (value) => {
496
+ var _a;
497
+ const session = editingCellRef.current;
498
+ if (!session) {
499
+ return;
500
+ }
501
+ const validate = (_a = columns.find(column => column.key === session.columnId)) === null || _a === void 0 ? void 0 : _a.editValidate;
502
+ if (validate) {
503
+ const result = validate(value, session.item);
504
+ if (result !== true) {
505
+ setEditorInvalid(typeof result === 'string' ? result : '');
506
+ return;
507
+ }
508
+ }
509
+ finishEdit(session);
510
+ if (history) {
511
+ undoStackRef.current.push({
512
+ rowId: session.rowId, columnId: session.columnId, value, previousValue: session.previousValue
513
+ });
514
+ if (undoStackRef.current.length > HISTORY_LIMIT) {
515
+ undoStackRef.current.shift();
516
+ }
517
+ redoStackRef.current = [];
518
+ syncHistoryFlags();
519
+ }
520
+ // The grid never mutates items - the app applies the change and stays
521
+ // the single source of truth.
522
+ onEditCommit === null || onEditCommit === void 0 ? void 0 : onEditCommit({
523
+ item: session.item,
524
+ columnId: session.columnId,
525
+ value,
526
+ previousValue: session.previousValue
527
+ });
528
+ };
529
+ const cancelEdit = () => {
530
+ const session = editingCellRef.current;
531
+ if (!session) {
532
+ return;
533
+ }
534
+ finishEdit(session);
535
+ onEditCancel === null || onEditCancel === void 0 ? void 0 : onEditCancel(session.item, session.columnId);
536
+ };
537
+ const stopEditing = (options) => {
538
+ var _a;
539
+ const session = editingCellRef.current;
540
+ if (!session) {
541
+ return;
542
+ }
543
+ const getValue = (_a = editorHandleRef.current) === null || _a === void 0 ? void 0 : _a.getValue;
544
+ if (options.commit && (getValue || editorDraftRef.current)) {
545
+ commitEdit(getValue ? getValue() : editorDraftRef.current.value);
546
+ // An invalid value blocks the commit; when the edit cannot survive
547
+ // anyway (row recycled away, row set changed), drop the draft.
548
+ if (editingCellRef.current && options.force) {
549
+ cancelEdit();
550
+ }
551
+ return;
552
+ }
553
+ // A custom editor without getValue owns its commits - stopping from the
554
+ // outside can only cancel.
555
+ cancelEdit();
556
+ };
557
+ const stopEditingRef = useRef(stopEditing);
558
+ stopEditingRef.current = stopEditing;
559
+ const startEditing = (cell) => {
560
+ var _a;
561
+ const config = columns.find(column => column.key === cell.columnId);
562
+ if (!config || !isColumnEditable(config)) {
563
+ return;
564
+ }
565
+ stopEditing({ commit: true, force: true });
566
+ const row = table.getRowModel().rows[cell.rowIndex];
567
+ if (!row) {
568
+ return;
569
+ }
570
+ const session = {
571
+ rowId: row.id,
572
+ columnId: cell.columnId,
573
+ rowIndex: cell.rowIndex,
574
+ item: row.original,
575
+ previousValue: row.getValue(cell.columnId)
576
+ };
577
+ if (config.editorPopup) {
578
+ const cellElement = editingCellElement(session);
579
+ const rootRect = (_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
580
+ const cellRect = cellElement === null || cellElement === void 0 ? void 0 : cellElement.getBoundingClientRect();
581
+ setEditorPopupStyle(cellRect && rootRect ?
582
+ { top: cellRect.top - rootRect.top, left: cellRect.left - rootRect.left, minWidth: cellRect.width } :
583
+ { top: 0, left: 0, minWidth: 0 });
584
+ }
585
+ editorFocusPendingRef.current = true;
586
+ setEditorInvalid(null);
587
+ setEditingCell(session);
588
+ onEditStart === null || onEditStart === void 0 ? void 0 : onEditStart(row.original, cell.columnId);
589
+ };
590
+ const moveEditing = (direction) => {
591
+ const session = editingCellRef.current;
592
+ if (!session) {
593
+ return;
594
+ }
595
+ const visible = visibleColumns(table, columns);
596
+ const currentIndex = visible.findIndex(column => column.key === session.columnId);
597
+ let next = null;
598
+ for (let index = currentIndex + direction; index >= 0 && index < visible.length; index += direction) {
599
+ if (isColumnEditable(visible[index])) {
600
+ next = visible[index];
601
+ break;
602
+ }
603
+ }
604
+ stopEditing({ commit: true });
605
+ if (editingCellRef.current) {
606
+ // An invalid value blocked the commit - stay.
607
+ return;
608
+ }
609
+ if (next) {
610
+ const cell = { rowIndex: session.rowIndex, columnId: next.key };
611
+ requestActiveCell(cell);
612
+ startEditing(cell);
613
+ }
614
+ };
615
+ const snapshotState = () => ({
616
+ columnFilters: columnFiltersState,
617
+ columnOrder: columnOrderState,
618
+ columnPinning: columnPinningState,
619
+ columnSizing: columnSizingState,
620
+ columnVisibility: columnVisibilityState,
621
+ globalFilter: globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '',
622
+ pagination: paginationState,
623
+ rowSelection: rowSelectionState,
624
+ sorting: sortingState
625
+ });
626
+ // stateKey persistence is automatic - every state change (debounced)
627
+ // rewrites the snapshot, so the view survives a reload.
628
+ const persistTimeoutRef = useRef(undefined);
629
+ useEffect(() => {
630
+ if (!stateKey || !mounted.current) {
631
+ return;
632
+ }
633
+ clearTimeout(persistTimeoutRef.current);
634
+ persistTimeoutRef.current = setTimeout(() => {
635
+ try {
636
+ window.localStorage.setItem(`${STATE_STORAGE_PREFIX}${stateKey}`, JSON.stringify(snapshotState()));
637
+ }
638
+ catch (_a) {
639
+ // Quota/privacy-mode failures degrade to a non-persistent grid.
640
+ }
641
+ }, 250);
642
+ return () => clearTimeout(persistTimeoutRef.current);
643
+ // eslint-disable-next-line react-hooks/exhaustive-deps
644
+ }, [
645
+ sortingState,
646
+ columnFiltersState,
647
+ globalFilterState,
648
+ columnOrderState,
649
+ columnPinningState,
650
+ columnSizingState,
651
+ columnVisibilityState,
652
+ paginationState,
653
+ rowSelectionState,
654
+ stateKey
655
+ ]);
656
+ // Undo/redo of edit commits. Entries hold the ROW ID, not the item
657
+ // reference - an immutable app replaces the object on every commit, so
658
+ // undo/redo resolve the current item at emit time.
659
+ const undoStackRef = useRef([]);
660
+ const redoStackRef = useRef([]);
661
+ const [canUndo, setCanUndo] = useState(false);
662
+ const [canRedo, setCanRedo] = useState(false);
663
+ const syncHistoryFlags = () => {
664
+ setCanUndo(undoStackRef.current.length > 0);
665
+ setCanRedo(redoStackRef.current.length > 0);
666
+ };
667
+ const applyHistoryEntry = (from, to, inverted) => {
668
+ const entry = from.pop();
669
+ if (!entry) {
670
+ return;
671
+ }
672
+ // The row may be gone (data replaced without it) - drop the entry.
673
+ const row = table.getCoreRowModel().rowsById[entry.rowId];
674
+ if (!row) {
675
+ syncHistoryFlags();
676
+ return;
677
+ }
678
+ to.push(entry);
679
+ onEditCommit === null || onEditCommit === void 0 ? void 0 : onEditCommit({
680
+ item: row.original,
681
+ columnId: entry.columnId,
682
+ value: inverted ? entry.previousValue : entry.value,
683
+ previousValue: inverted ? entry.value : entry.previousValue
684
+ });
685
+ setAnnouncement(inverted ? mergedLabels.undoneAnnouncement : mergedLabels.redoneAnnouncement);
686
+ syncHistoryFlags();
687
+ };
688
+ const undo = () => applyHistoryEntry(undoStackRef.current, redoStackRef.current, true);
689
+ const redo = () => applyHistoryEntry(redoStackRef.current, undoStackRef.current, false);
690
+ // Commit on pointerdown outside the edit scope - blur alone cannot commit,
691
+ // picking a date in a calendar blurs the input mid-edit.
692
+ useEffect(() => {
693
+ if (!editingCell) {
694
+ return;
695
+ }
696
+ const handler = (event) => {
697
+ if (!editScopeContains(event.target)) {
698
+ stopEditingRef.current({ commit: true });
699
+ }
700
+ };
701
+ document.addEventListener('pointerdown', handler);
702
+ return () => document.removeEventListener('pointerdown', handler);
703
+ // eslint-disable-next-line react-hooks/exhaustive-deps
704
+ }, [editingCell]);
705
+ // The editor mounts during the body render - focus it right after (the
706
+ // child effect registering the handle runs before this one).
707
+ useEffect(() => {
708
+ var _a, _b;
709
+ if (editingCell && editorFocusPendingRef.current) {
710
+ editorFocusPendingRef.current = false;
711
+ (_b = (_a = editorHandleRef.current) === null || _a === void 0 ? void 0 : _a.focus) === null || _b === void 0 ? void 0 : _b.call(_a);
712
+ }
713
+ }, [editingCell]);
714
+ const handleEditingDoubleClick = (event) => {
715
+ if (!editing) {
716
+ return;
717
+ }
718
+ const found = navigableCellFromTarget(event.target);
719
+ if (found && found.cell.rowIndex >= 0) {
720
+ setActiveCell(found.cell);
721
+ startEditing(found.cell);
722
+ }
723
+ };
724
+ const handleNavigationFocus = (event) => {
725
+ containsFocusRef.current = true;
726
+ if (!cellNavigationEnabled) {
727
+ return;
728
+ }
729
+ const found = navigableCellFromTarget(event.target);
730
+ if (found) {
731
+ // The browser already moved focus (click, descend) - only sync state.
732
+ setActiveCell(found.cell);
733
+ }
734
+ };
735
+ const handleNavigationBlur = (event) => {
736
+ var _a;
737
+ const related = event.relatedTarget;
738
+ if (!related || !((_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.contains(related))) {
739
+ containsFocusRef.current = false;
740
+ }
741
+ };
742
+ const handleNavigationKeydown = (event) => {
743
+ var _a, _b, _c, _d;
744
+ if (!cellNavigationEnabled) {
745
+ return;
746
+ }
747
+ const target = event.target;
748
+ // Ctrl/Cmd+Z / Ctrl+Shift+Z / Ctrl+Y - edit history (MUI/AG parity).
749
+ if (history &&
750
+ (event.ctrlKey || event.metaKey) &&
751
+ !editingCellRef.current &&
752
+ !target.matches('input, textarea, select')) {
753
+ const key = event.key.toLowerCase();
754
+ if (key === 'z' && !event.shiftKey) {
755
+ event.preventDefault();
756
+ undo();
757
+ return;
758
+ }
759
+ if ((key === 'z' && event.shiftKey) || key === 'y') {
760
+ event.preventDefault();
761
+ redo();
762
+ return;
763
+ }
764
+ }
765
+ if (editingCellRef.current && editScopeContains(target)) {
766
+ // A defaultPrevented key was consumed by the editor (a rich editor
767
+ // closing its own overlay on Escape, an autocomplete accepting on
768
+ // Enter) - one key, one layer.
769
+ if (event.key === 'Enter') {
770
+ if (!event.defaultPrevented) {
771
+ event.preventDefault();
772
+ stopEditing({ commit: true });
773
+ }
774
+ return;
775
+ }
776
+ if (event.key === 'Escape') {
777
+ if (!event.defaultPrevented) {
778
+ cancelEdit();
779
+ }
780
+ return;
781
+ }
782
+ if (event.key === 'Tab') {
783
+ event.preventDefault();
784
+ moveEditing(event.shiftKey ? -1 : 1);
785
+ return;
786
+ }
787
+ // Everything else (arrows, typing) stays native inside the editor.
788
+ return;
789
+ }
790
+ // Focus fell back to the viewport after the active row was recycled out
791
+ // of the window: any navigation key scrolls the active cell back in view
792
+ // and refocuses it.
793
+ if (target === viewportElement) {
794
+ if (effectiveActiveCell && event.key !== 'Tab') {
795
+ event.preventDefault();
796
+ requestActiveCell(effectiveActiveCell);
797
+ }
798
+ return;
799
+ }
800
+ const found = navigableCellFromTarget(target);
801
+ if (!found) {
802
+ return;
803
+ }
804
+ if (target !== found.cellElement) {
805
+ // Focus is descended into the cell's content; Escape ascends back.
806
+ if (event.key === 'Escape') {
807
+ found.cellElement.focus({ preventScroll: true });
808
+ }
809
+ return;
810
+ }
811
+ if (event.key === 'Enter' || event.key === 'F2') {
812
+ event.preventDefault();
813
+ const config = columns.find(column => column.key === found.cell.columnId);
814
+ if (found.cell.rowIndex === -1) {
815
+ if (event.key === 'Enter' && config && isColumnSortable(config)) {
816
+ (_b = (_a = table.getColumn(found.cell.columnId)) === null || _a === void 0 ? void 0 : _a.getToggleSortingHandler()) === null || _b === void 0 ? void 0 : _b(event);
817
+ }
818
+ return;
819
+ }
820
+ if (config && isColumnEditable(config)) {
821
+ startEditing(found.cell);
822
+ return;
823
+ }
824
+ if (event.key === 'Enter') {
825
+ (_c = found.cellElement
826
+ .querySelector('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]')) === null || _c === void 0 ? void 0 : _c.focus({ preventScroll: true });
827
+ }
828
+ return;
829
+ }
830
+ if (event.key === ' ') {
831
+ if (selectionEnabled && found.cell.rowIndex >= 0) {
832
+ event.preventDefault();
833
+ (_d = table.getRowModel().rows[found.cell.rowIndex]) === null || _d === void 0 ? void 0 : _d.toggleSelected();
834
+ }
835
+ return;
836
+ }
837
+ const result = resolveNavigation(event.key, event.ctrlKey || event.metaKey, found.cell, activeCellContext());
838
+ if (!result) {
839
+ return;
840
+ }
841
+ event.preventDefault();
842
+ if (result.pageDelta === 1 && table.getCanNextPage()) {
843
+ table.nextPage();
844
+ }
845
+ else if (result.pageDelta === -1 && table.getCanPreviousPage()) {
846
+ table.previousPage();
847
+ }
848
+ requestActiveCell(result.cell);
849
+ };
850
+ const ToolbarSlot = slots === null || slots === void 0 ? void 0 : slots.toolbar;
851
+ const PaginationSlot = slots === null || slots === void 0 ? void 0 : slots.pagination;
852
+ const EmptySlot = slots === null || slots === void 0 ? void 0 : slots.empty;
853
+ const paginationElement = (paginationPosition) => {
854
+ var _a, _b;
855
+ return PaginationSlot ?
856
+ (jsx(PaginationSlot, { table: table, labels: mergedLabels })) :
857
+ (jsx(CDataGridPagination, { info: (_a = paginationOptions.info) !== null && _a !== void 0 ? _a : true, labels: mergedLabels, pageSizeOptions: (_b = paginationOptions.pageSizeOptions) !== null && _b !== void 0 ? _b : DEFAULT_PAGE_SIZE_OPTIONS, position: paginationPosition, table: table, totalRows: totalRows }));
858
+ };
859
+ const emptyContent = EmptySlot ?
860
+ jsx(EmptySlot, { table: table, labels: mergedLabels }) :
861
+ (loadFailed ? mergedLabels.loadError : empty);
862
+ const editingConfig = editingCell ?
863
+ (_q = columns.find(column => column.key === editingCell.columnId)) !== null && _q !== void 0 ? _q : null :
864
+ null;
865
+ const EditorComponent = (_r = editingConfig === null || editingConfig === void 0 ? void 0 : editingConfig.editor) !== null && _r !== void 0 ? _r : CDataGridCellEditor;
866
+ const editorSession = editingCell && editingConfig ?
867
+ {
868
+ rowId: editingCell.rowId,
869
+ columnId: editingCell.columnId,
870
+ rowIndex: editingCell.rowIndex,
871
+ popup: Boolean(editingConfig.editorPopup),
872
+ render: () => (jsx(EditorComponent, { cancel: cancelEdit, column: editingConfig, commit: commitEdit, handleRef: registerEditorHandle, invalid: editorInvalid, item: editingCell.item, labels: mergedLabels, value: editingCell.previousValue })),
873
+ // Blur commits (inline editors) unless focus moved within the edit
874
+ // scope - the outside-pointerdown listener covers pointer-only flows.
875
+ onBlur(event) {
876
+ const related = event.relatedTarget;
877
+ if (related && !editScopeContains(related)) {
878
+ stopEditingRef.current({ commit: true });
879
+ }
880
+ },
881
+ onHidden: () => stopEditingRef.current({ commit: true, force: true })
882
+ } :
883
+ null;
884
+ return (jsxs("div", { className: `data-grid${sizingEnabled ? ' data-grid-resizable' : ''}${pinningEnabled ? ' data-grid-pinned' : ''}${className ? ` ${className}` : ''}`, "data-coreui-sorter-visibility": sorterVisibility || undefined, ref: setRootRef, ...rest, children: [jsx("div", { className: "visually-hidden", "aria-live": "polite", children: announcement }), ToolbarSlot ?
885
+ jsx(ToolbarSlot, { table: table, labels: mergedLabels }) :
886
+ (resolvedToolbar.columns || resolvedToolbar.export || resolvedToolbar.search || resolvedToolbar.history) && (jsx(CDataGridToolbar, { columns: columns, columnsIcon: toolbarColumnsIcon, container: rootRef, export: resolvedToolbar.export, exportIcon: toolbarExportIcon, exportOptions: resolvedToolbar.exportOptions, initialVisibility: initialColumnVisibility.current, labels: mergedLabels, canRedo: canRedo, canUndo: canUndo, onRedo: redo, onUndo: undo, redoIcon: toolbarRedoIcon, search: resolvedToolbar.search, searchValue: globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '', showColumns: resolvedToolbar.columns, showHistory: resolvedToolbar.history, undoIcon: toolbarUndoIcon, table: table })), paginationEnabled && (position === 'top' || position === 'both') && paginationElement('top'), jsxs("div", { className: "data-grid-viewport", ref: setViewportElement, ...(serverSide && { 'aria-busy': loading }), ...(cellNavigationEnabled && {
887
+ // Focus parks here when the focused row is recycled out of the
888
+ // window; the keydown fallback then scrolls the active cell back.
889
+ tabIndex: -1,
890
+ onBlur: handleNavigationBlur,
891
+ onFocus: handleNavigationFocus,
892
+ onKeyDown: handleNavigationKeydown
893
+ }), ...(editing && { onDoubleClick: handleEditingDoubleClick }), children: [jsxs("table", { className: "table", "aria-rowcount": headerRowCount + totalRows, ...(cellNavigationEnabled && {
894
+ role: 'grid',
895
+ 'aria-colcount': visibleColumnIds.length + (selectionEnabled ? 1 : 0)
896
+ }), children: [jsx(CDataGridHeader, { cellNavigation: cellNavigationEnabled, columnFilters: columnFilters, columns: columns, draggable: Boolean(columnOrder), icons: menuIcons, isFilterable: isColumnFilterable, isResizable: isColumnResizable, isSortable: isColumnSortable, labels: mergedLabels, menu: Boolean(columnMenu), menuBuilder: menuBuilder, menuContainer: rootRef, pinning: pinningEnabled, resizable: sizingEnabled, selectAll: selectionEnabled && selectionOptions.selectAll !== false, selection: selectionEnabled, tabStop: tabStop, table: table, visibility: Boolean(columnVisibility) }), jsx(CDataGridBody, { cellNavigation: cellNavigationEnabled, columns: columns, containsFocusRef: containsFocusRef, editorSession: editorSession, empty: emptyContent, focusRequestRef: focusRequestRef, headerRowCount: headerRowCount, labels: mergedLabels, overscan: overscan, pinning: pinningEnabled, rowHeight: rowHeight, rowIndexOffset: paginationEnabled ? paginationState.pageIndex * paginationState.pageSize : 0, scrollElement: viewportElement, scrollToIndexRef: scrollToIndexRef, selection: selectionEnabled, tabStop: tabStop, table: table, virtualization: virtualizationEnabled })] }), serverSide && (jsx("div", { className: "data-grid-loading", hidden: !loading, children: jsx("div", { className: "spinner-border", role: "status", children: jsx("span", { className: "visually-hidden", children: mergedLabels.loading }) }) }))] }), (editorSession === null || editorSession === void 0 ? void 0 : editorSession.popup) && (jsx("div", { className: "data-grid-editor-popup", style: editorPopupStyle !== null && editorPopupStyle !== void 0 ? editorPopupStyle : undefined, onBlur: editorSession.onBlur, onKeyDown: handleNavigationKeydown, children: editorSession.render() })), paginationEnabled && (position === 'bottom' || position === 'both') && paginationElement('bottom')] }));
897
+ });
898
+ CDataGrid.displayName = 'CDataGrid';
899
+
900
+ export { CDataGrid };
901
+ //# sourceMappingURL=CDataGrid.mjs.map