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