@coreui/react-data-grid 0.1.0-rc.0 → 0.2.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/README.md +3 -2
- package/dist/cjs/CDataGrid.d.ts +11 -1
- package/dist/cjs/CDataGrid.js +228 -43
- package/dist/cjs/CDataGrid.js.map +1 -1
- package/dist/cjs/CDataGridBody.d.ts +9 -4
- package/dist/cjs/CDataGridBody.js +54 -11
- package/dist/cjs/CDataGridBody.js.map +1 -1
- package/dist/cjs/CDataGridColumnChooser.d.ts +2 -3
- package/dist/cjs/CDataGridColumnChooser.js.map +1 -1
- package/dist/cjs/CDataGridFilterMenu.d.ts +2 -3
- package/dist/cjs/CDataGridFilterMenu.js.map +1 -1
- package/dist/cjs/CDataGridHeader.d.ts +4 -4
- package/dist/cjs/CDataGridHeader.js +25 -66
- package/dist/cjs/CDataGridHeader.js.map +1 -1
- package/dist/cjs/CDataGridHeaderMenu.d.ts +2 -3
- package/dist/cjs/CDataGridHeaderMenu.js.map +1 -1
- package/dist/cjs/CDataGridPagination.d.ts +2 -3
- package/dist/cjs/CDataGridPagination.js +1 -1
- package/dist/cjs/CDataGridPagination.js.map +1 -1
- package/dist/cjs/CDataGridToolbar.d.ts +8 -6
- package/dist/cjs/CDataGridToolbar.js +7 -2
- package/dist/cjs/CDataGridToolbar.js.map +1 -1
- package/dist/cjs/columns.d.ts +38 -12
- package/dist/cjs/columns.js +51 -31
- package/dist/cjs/columns.js.map +1 -1
- package/dist/cjs/features.d.ts +52 -0
- package/dist/cjs/features.js +66 -0
- package/dist/cjs/features.js.map +1 -0
- package/dist/cjs/icons.d.ts +6 -4
- package/dist/cjs/icons.js +12 -8
- package/dist/cjs/icons.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pinning.d.ts +4 -5
- package/dist/cjs/pinning.js +10 -8
- package/dist/cjs/pinning.js.map +1 -1
- package/dist/cjs/toolbar.d.ts +3 -2
- package/dist/cjs/toolbar.js +5 -2
- package/dist/cjs/toolbar.js.map +1 -1
- package/dist/cjs/types.d.ts +81 -29
- package/dist/esm/CDataGrid.d.ts +11 -1
- package/dist/esm/CDataGrid.mjs +231 -46
- package/dist/esm/CDataGrid.mjs.map +1 -1
- package/dist/esm/CDataGridBody.d.ts +9 -4
- package/dist/esm/CDataGridBody.mjs +55 -12
- package/dist/esm/CDataGridBody.mjs.map +1 -1
- package/dist/esm/CDataGridColumnChooser.d.ts +2 -3
- package/dist/esm/CDataGridColumnChooser.mjs.map +1 -1
- package/dist/esm/CDataGridFilterMenu.d.ts +2 -3
- package/dist/esm/CDataGridFilterMenu.mjs.map +1 -1
- package/dist/esm/CDataGridHeader.d.ts +4 -4
- package/dist/esm/CDataGridHeader.mjs +27 -68
- package/dist/esm/CDataGridHeader.mjs.map +1 -1
- package/dist/esm/CDataGridHeaderMenu.d.ts +2 -3
- package/dist/esm/CDataGridHeaderMenu.mjs.map +1 -1
- package/dist/esm/CDataGridPagination.d.ts +2 -3
- package/dist/esm/CDataGridPagination.mjs +1 -1
- package/dist/esm/CDataGridPagination.mjs.map +1 -1
- package/dist/esm/CDataGridToolbar.d.ts +8 -6
- package/dist/esm/CDataGridToolbar.mjs +7 -2
- package/dist/esm/CDataGridToolbar.mjs.map +1 -1
- package/dist/esm/columns.d.ts +38 -12
- package/dist/esm/columns.mjs +51 -32
- package/dist/esm/columns.mjs.map +1 -1
- package/dist/esm/features.d.ts +52 -0
- package/dist/esm/features.mjs +63 -0
- package/dist/esm/features.mjs.map +1 -0
- package/dist/esm/icons.d.ts +6 -4
- package/dist/esm/icons.mjs +7 -5
- package/dist/esm/icons.mjs.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/pinning.d.ts +4 -5
- package/dist/esm/pinning.mjs +10 -8
- package/dist/esm/pinning.mjs.map +1 -1
- package/dist/esm/toolbar.d.ts +3 -2
- package/dist/esm/toolbar.mjs +5 -2
- package/dist/esm/toolbar.mjs.map +1 -1
- package/dist/esm/types.d.ts +81 -29
- package/package.json +6 -6
package/dist/esm/CDataGrid.mjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { evaluateColumnFilter } from '@coreui/data-grid/filters';
|
|
3
|
-
import {
|
|
3
|
+
import { copyCellSelection } from '@coreui/data-grid/clipboard';
|
|
4
|
+
import { printGrid } from '@coreui/data-grid/print';
|
|
5
|
+
import { createColumnDragController, createRowDragController } from '@coreui/data-grid/drag';
|
|
6
|
+
import { assertFeatureCoverage } from '@coreui/data-grid/validate';
|
|
7
|
+
import { useTable } from '@tanstack/react-table';
|
|
4
8
|
import { forwardRef, useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
5
9
|
import { clampActiveCell, resolveNavigation } from './active-cell.mjs';
|
|
6
10
|
import { CDataGridBody } from './CDataGridBody.mjs';
|
|
@@ -8,8 +12,9 @@ import { CDataGridCellEditor } from './CDataGridCellEditor.mjs';
|
|
|
8
12
|
import { CDataGridHeader } from './CDataGridHeader.mjs';
|
|
9
13
|
import { CDataGridPagination } from './CDataGridPagination.mjs';
|
|
10
14
|
import { CDataGridToolbar } from './CDataGridToolbar.mjs';
|
|
11
|
-
import { visibleColumns } from './columns.mjs';
|
|
12
|
-
import {
|
|
15
|
+
import { reorderColumn, canReorderColumn, visibleColumns } from './columns.mjs';
|
|
16
|
+
import { dataGridFeatures } from './features.mjs';
|
|
17
|
+
import { DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON, DEFAULT_MOVE_START_ICON, DEFAULT_MOVE_END_ICON, DEFAULT_PIN_START_ICON, DEFAULT_PIN_END_ICON, DEFAULT_ROW_HANDLE_ICON, DEFAULT_SORT_ASCENDING_ICON, DEFAULT_SORT_DESCENDING_ICON, DEFAULT_SORT_NEUTRAL_ICON, DEFAULT_TOOLBAR_COLUMNS_ICON, DEFAULT_TOOLBAR_EXPORT_ICON, DEFAULT_TOOLBAR_PRINT_ICON, DEFAULT_TOOLBAR_REDO_ICON, DEFAULT_TOOLBAR_UNDO_ICON, DEFAULT_UNPIN_ICON } from './icons.mjs';
|
|
13
18
|
import { DEFAULT_LABELS, interpolate } from '@coreui/data-grid/labels';
|
|
14
19
|
import { resolveToolbar } from './toolbar.mjs';
|
|
15
20
|
import { createTooltip } from './tooltip.mjs';
|
|
@@ -17,6 +22,8 @@ import { createTooltip } from './tooltip.mjs';
|
|
|
17
22
|
const DEFAULT_PAGE_SIZE_OPTIONS = [5, 10, 20, 50];
|
|
18
23
|
const STATE_STORAGE_PREFIX = 'coreui-data-grid:';
|
|
19
24
|
const HISTORY_LIMIT = 100;
|
|
25
|
+
const INFINITE_SCROLL_PAGE_SIZE = 50;
|
|
26
|
+
const UNPAGINATED_PAGE_SIZE = Number.MAX_SAFE_INTEGER;
|
|
20
27
|
const readPersistedState = (stateKey) => {
|
|
21
28
|
if (!stateKey) {
|
|
22
29
|
return null;
|
|
@@ -29,6 +36,12 @@ const readPersistedState = (stateKey) => {
|
|
|
29
36
|
return null;
|
|
30
37
|
}
|
|
31
38
|
};
|
|
39
|
+
const CELL_SELECTION_DIRECTIONS = {
|
|
40
|
+
ArrowDown: 'down',
|
|
41
|
+
ArrowLeft: 'left',
|
|
42
|
+
ArrowRight: 'right',
|
|
43
|
+
ArrowUp: 'up'
|
|
44
|
+
};
|
|
32
45
|
const FETCH_DEBOUNCE_MS = 100;
|
|
33
46
|
// Module-level so an omitted `items` prop keeps a stable identity - a fresh
|
|
34
47
|
// array per render would force TanStack to recompute row models every time.
|
|
@@ -41,17 +54,17 @@ const toTanStackFilterFn = (column) => {
|
|
|
41
54
|
return (row, columnId, filterValue) => evaluateColumnFilter(row.getValue(columnId), filterValue, column.filterType);
|
|
42
55
|
};
|
|
43
56
|
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,
|
|
57
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
58
|
+
const { autoRowHeight = false, cellNavigation = false, cellSelection = 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', features = dataGridFeatures, filterIcon = DEFAULT_FILTER_ICON, globalFilter = false, hideColumnIcon = DEFAULT_HIDE_COLUMN_ICON, history = false, infiniteScroll = false, itemKey, items = EMPTY_ITEMS, labels, moveStartIcon = DEFAULT_MOVE_START_ICON, moveEndIcon = DEFAULT_MOVE_END_ICON, onDataError, onDataLoad, onEditCancel, onEditCommit, onEditStart, onFilterChange, onOrderChange, onPaginationChange, onPinningChange, onSelectionChange, onRowOrderChange, onCellCopy, onSizingChange, onSortingChange, onVisibilityChange, overscan = 10, pagination = false, pinStartIcon = DEFAULT_PIN_START_ICON, pinEndIcon = DEFAULT_PIN_END_ICON, rowHandleIcon = DEFAULT_ROW_HANDLE_ICON, rowHeight = 44, rowOrder = false, 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, printTitle, toolbarExportIcon = DEFAULT_TOOLBAR_EXPORT_ICON, toolbarPrintIcon = DEFAULT_TOOLBAR_PRINT_ICON, toolbarRedoIcon = DEFAULT_TOOLBAR_REDO_ICON, toolbarUndoIcon = DEFAULT_TOOLBAR_UNDO_ICON, unpinIcon = DEFAULT_UNPIN_ICON, virtualization = true, ...rest } = props;
|
|
46
59
|
const mergedLabels = useMemo(() => ({ ...DEFAULT_LABELS, ...labels }), [labels]);
|
|
47
60
|
const menuIcons = useMemo(() => ({
|
|
48
61
|
columnMenu: columnMenuIcon,
|
|
49
62
|
filter: filterIcon,
|
|
50
63
|
hideColumn: hideColumnIcon,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
moveStart: moveStartIcon,
|
|
65
|
+
moveEnd: moveEndIcon,
|
|
66
|
+
pinStart: pinStartIcon,
|
|
67
|
+
pinEnd: pinEndIcon,
|
|
55
68
|
sortAscending: sortAscendingIcon,
|
|
56
69
|
sortDescending: sortDescendingIcon,
|
|
57
70
|
sortNeutral: sortNeutralIcon,
|
|
@@ -60,18 +73,19 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
60
73
|
columnMenuIcon,
|
|
61
74
|
filterIcon,
|
|
62
75
|
hideColumnIcon,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
moveStartIcon,
|
|
77
|
+
moveEndIcon,
|
|
78
|
+
pinStartIcon,
|
|
79
|
+
pinEndIcon,
|
|
67
80
|
sortAscendingIcon,
|
|
68
81
|
sortDescendingIcon,
|
|
69
82
|
sortNeutralIcon,
|
|
70
83
|
unpinIcon
|
|
71
84
|
]);
|
|
72
85
|
const menuBuilder = typeof columnMenu === 'function' ? columnMenu : null;
|
|
73
|
-
//
|
|
74
|
-
|
|
86
|
+
// Both need the active-cell substrate: editing targets it with Enter/F2,
|
|
87
|
+
// cell selection anchors ranges on it.
|
|
88
|
+
const cellNavigationEnabled = cellNavigation || editing || cellSelection;
|
|
75
89
|
const sortingOptions = typeof sorting === 'object' ? sorting : {};
|
|
76
90
|
const sizingEnabled = Boolean(columnSizing);
|
|
77
91
|
const sizingOptions = typeof columnSizing === 'object' ? columnSizing : {};
|
|
@@ -80,9 +94,23 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
80
94
|
const selectionEnabled = Boolean(rowSelection);
|
|
81
95
|
const selectionOptions = typeof rowSelection === 'object' ? rowSelection : {};
|
|
82
96
|
const serverSide = Boolean(dataProvider);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
if (infiniteScroll) {
|
|
98
|
+
if (!serverSide) {
|
|
99
|
+
throw new TypeError('DATA-GRID: Option "infiniteScroll" requires "dataProvider".');
|
|
100
|
+
}
|
|
101
|
+
if (pagination) {
|
|
102
|
+
throw new TypeError('DATA-GRID: Option "infiniteScroll" cannot be combined with "pagination".');
|
|
103
|
+
}
|
|
104
|
+
if (!virtualization) {
|
|
105
|
+
throw new TypeError('DATA-GRID: Option "infiniteScroll" requires "virtualization".');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const infiniteOptions = typeof infiniteScroll === 'object' ? infiniteScroll : {};
|
|
109
|
+
// Server-side mode implies pagination unless it scrolls infinitely, where
|
|
110
|
+
// the pages accumulate behind the virtualizer instead of replacing. The
|
|
111
|
+
// pagination STATE stays either way — infinite scroll pages the requests.
|
|
112
|
+
const paginationEnabled = Boolean(pagination) || (serverSide && !infiniteScroll);
|
|
113
|
+
const paginated = paginationEnabled || Boolean(infiniteScroll);
|
|
86
114
|
const paginationOptions = typeof pagination === 'object' ? pagination : {};
|
|
87
115
|
const virtualizationEnabled = virtualization && !paginationEnabled;
|
|
88
116
|
const resolvedToolbar = resolveToolbar({
|
|
@@ -105,7 +133,11 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
105
133
|
const [globalFilterState, setGlobalFilterState] = useState((persisted === null || persisted === void 0 ? void 0 : persisted.globalFilter) === undefined || persisted.globalFilter === '' ? undefined : persisted.globalFilter);
|
|
106
134
|
const [rowSelectionState, setRowSelectionState] = useState((_c = persisted === null || persisted === void 0 ? void 0 : persisted.rowSelection) !== null && _c !== void 0 ? _c : {});
|
|
107
135
|
const [columnSizingState, setColumnSizingState] = useState((_d = persisted === null || persisted === void 0 ? void 0 : persisted.columnSizing) !== null && _d !== void 0 ? _d : {});
|
|
108
|
-
const [columnPinningState, setColumnPinningState] = useState(
|
|
136
|
+
const [columnPinningState, setColumnPinningState] = useState({
|
|
137
|
+
start: [],
|
|
138
|
+
end: [],
|
|
139
|
+
...((_e = persisted === null || persisted === void 0 ? void 0 : persisted.columnPinning) !== null && _e !== void 0 ? _e : (typeof columnPinning === 'object' ? columnPinning : {}))
|
|
140
|
+
});
|
|
109
141
|
const [columnOrderState, setColumnOrderState] = useState((_f = persisted === null || persisted === void 0 ? void 0 : persisted.columnOrder) !== null && _f !== void 0 ? _f : (Array.isArray(columnOrder) ? columnOrder : []));
|
|
110
142
|
const [columnVisibilityState, setColumnVisibilityState] = useState((_g = persisted === null || persisted === void 0 ? void 0 : persisted.columnVisibility) !== null && _g !== void 0 ? _g : (typeof columnVisibility === 'object' ? columnVisibility : {}));
|
|
111
143
|
// The chooser's Reset restores the initial columnVisibility object, captured
|
|
@@ -113,7 +145,13 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
113
145
|
const initialColumnVisibility = useRef(typeof columnVisibility === 'object' ? columnVisibility : {});
|
|
114
146
|
const [paginationState, setPaginationState] = useState({
|
|
115
147
|
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
|
-
|
|
148
|
+
// Pagination is a registered feature on every grid (v9 features are
|
|
149
|
+
// static), so an unpaginated grid is one page holding every row.
|
|
150
|
+
pageSize: infiniteScroll ?
|
|
151
|
+
(_k = infiniteOptions.pageSize) !== null && _k !== void 0 ? _k : INFINITE_SCROLL_PAGE_SIZE :
|
|
152
|
+
(paginationEnabled ?
|
|
153
|
+
(_o = (_m = (_l = persisted === null || persisted === void 0 ? void 0 : persisted.pagination) === null || _l === void 0 ? void 0 : _l.pageSize) !== null && _m !== void 0 ? _m : paginationOptions.pageSize) !== null && _o !== void 0 ? _o : 10 :
|
|
154
|
+
UNPAGINATED_PAGE_SIZE)
|
|
117
155
|
});
|
|
118
156
|
const [activeCell, setActiveCell] = useState(null);
|
|
119
157
|
// A pending focus request survives renders until the target cell exists in
|
|
@@ -163,14 +201,28 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
163
201
|
enableSorting: column.sortable !== false,
|
|
164
202
|
filterFn: toTanStackFilterFn(column),
|
|
165
203
|
id: column.key,
|
|
166
|
-
meta: { label: column.label, formatter: column.formatter },
|
|
204
|
+
meta: { label: column.label, formatter: column.formatter, width: column.width },
|
|
167
205
|
...(widthsEnabled && size !== null && { size })
|
|
168
206
|
};
|
|
169
207
|
}), [columns, widthsEnabled]);
|
|
170
|
-
|
|
208
|
+
assertFeatureCoverage('data-grid', features, {
|
|
209
|
+
cellSelection,
|
|
210
|
+
columnFilters,
|
|
211
|
+
columnOrder: Boolean(columnOrder),
|
|
212
|
+
columnPinning: pinningEnabled,
|
|
213
|
+
columnSizing: sizingEnabled,
|
|
214
|
+
columnVisibility: Boolean(columnVisibility),
|
|
215
|
+
globalFilter: resolvedToolbar.search,
|
|
216
|
+
pagination: paginated,
|
|
217
|
+
rowSelection: selectionEnabled,
|
|
218
|
+
selectFilters: columnFilters && columns.some(column => column.filterType === 'select'),
|
|
219
|
+
sorting: Boolean(sorting)
|
|
220
|
+
});
|
|
221
|
+
const table = useTable({
|
|
171
222
|
columns: columnDefs,
|
|
172
|
-
columnResizeMode: (
|
|
223
|
+
columnResizeMode: (_p = sizingOptions.mode) !== null && _p !== void 0 ? _p : 'onChange',
|
|
173
224
|
data: serverSide ? serverItems : items,
|
|
225
|
+
enableCellSelection: cellSelection,
|
|
174
226
|
enableColumnFilters: columnFilters,
|
|
175
227
|
enableColumnPinning: pinningEnabled,
|
|
176
228
|
enableColumnResizing: sizingEnabled,
|
|
@@ -180,12 +232,10 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
180
232
|
enableRowSelection: selectionEnabled,
|
|
181
233
|
enableSorting: Boolean(sorting),
|
|
182
234
|
enableSortingRemoval: Boolean(sortingOptions.resetable),
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
getFilteredRowModel: getFilteredRowModel(),
|
|
188
|
-
getSortedRowModel: getSortedRowModel(),
|
|
235
|
+
// The runtime set may be smaller than the DataGridFeatures the table is
|
|
236
|
+
// typed against - internals stay typed on the full set and every call
|
|
237
|
+
// into an optional feature is capability-guarded.
|
|
238
|
+
features,
|
|
189
239
|
globalFilterFn: 'includesString',
|
|
190
240
|
...(serverSide && {
|
|
191
241
|
manualFiltering: true,
|
|
@@ -193,7 +243,6 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
193
243
|
manualSorting: true,
|
|
194
244
|
rowCount: serverTotalRows
|
|
195
245
|
}),
|
|
196
|
-
...(paginationEnabled && { getPaginationRowModel: getPaginationRowModel() }),
|
|
197
246
|
...(itemKey && { getRowId: itemKey }),
|
|
198
247
|
onColumnFiltersChange: setColumnFiltersState,
|
|
199
248
|
onColumnOrderChange: setColumnOrderState,
|
|
@@ -254,8 +303,68 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
254
303
|
const tooltip = createTooltip(rootRef.current);
|
|
255
304
|
return () => tooltip.dispose();
|
|
256
305
|
}, []);
|
|
306
|
+
// Pointer-based column drag: the shared controller owns the ghost, the drop
|
|
307
|
+
// indicator and the edge auto-scroll, and writes to the DOM directly, so a
|
|
308
|
+
// drag costs no React renders. The table is read through a ref so the
|
|
309
|
+
// controller survives every unrelated re-render.
|
|
310
|
+
const tableRefForDrag = useRef(table);
|
|
311
|
+
tableRefForDrag.current = table;
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
const root = rootRef.current;
|
|
314
|
+
if (!columnOrder || !root) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const controller = createColumnDragController({
|
|
318
|
+
root,
|
|
319
|
+
getScrollElement: () => viewportElement,
|
|
320
|
+
canDrop: (sourceKey, targetKey) => canReorderColumn(tableRefForDrag.current, sourceKey, targetKey),
|
|
321
|
+
onReorder: (sourceKey, targetKey, after) => reorderColumn(tableRefForDrag.current, sourceKey, targetKey, after)
|
|
322
|
+
});
|
|
323
|
+
return () => controller.dispose();
|
|
324
|
+
}, [columnOrder, viewportElement]);
|
|
325
|
+
// The grid never mutates `items` — it hands the reordered array to the app
|
|
326
|
+
// through onRowOrderChange, the same contract inline editing uses.
|
|
327
|
+
const rowOrderRef = useRef({ items, rowIndexOffset: 0, onRowOrderChange });
|
|
328
|
+
rowOrderRef.current = {
|
|
329
|
+
items,
|
|
330
|
+
rowIndexOffset: paginationEnabled ? paginationState.pageIndex * paginationState.pageSize : 0,
|
|
331
|
+
onRowOrderChange
|
|
332
|
+
};
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
const root = rootRef.current;
|
|
335
|
+
if (!rowOrder || !root) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const controller = createRowDragController({
|
|
339
|
+
root,
|
|
340
|
+
getScrollElement: () => viewportElement,
|
|
341
|
+
onReorder(sourceIndex, targetIndex, after) {
|
|
342
|
+
var _a;
|
|
343
|
+
const { current } = rowOrderRef;
|
|
344
|
+
const next = [...current.items];
|
|
345
|
+
const from = current.rowIndexOffset + sourceIndex;
|
|
346
|
+
const [item] = next.splice(from, 1);
|
|
347
|
+
if (item === undefined) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// Resolved AFTER the removal, so the index still points at the target
|
|
351
|
+
// whichever direction the row travelled.
|
|
352
|
+
const target = current.rowIndexOffset + targetIndex;
|
|
353
|
+
const to = target > from ? target - 1 : target;
|
|
354
|
+
const newIndex = after ? to + 1 : to;
|
|
355
|
+
next.splice(newIndex, 0, item);
|
|
356
|
+
(_a = current.onRowOrderChange) === null || _a === void 0 ? void 0 : _a.call(current, {
|
|
357
|
+
items: next, item, oldIndex: from, newIndex
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
return () => controller.dispose();
|
|
362
|
+
}, [rowOrder, viewportElement]);
|
|
257
363
|
const requestIdRef = useRef(0);
|
|
258
364
|
const lastFilterKeyRef = useRef(null);
|
|
365
|
+
// One page bump per fetch: the body re-renders many times before the next
|
|
366
|
+
// page lands, and each of those passes still sees the end of the buffer.
|
|
367
|
+
const loadMorePendingRef = useRef(false);
|
|
259
368
|
useEffect(() => {
|
|
260
369
|
if (!dataProvider) {
|
|
261
370
|
return;
|
|
@@ -282,7 +391,14 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
282
391
|
if (id !== requestIdRef.current) {
|
|
283
392
|
return;
|
|
284
393
|
}
|
|
285
|
-
|
|
394
|
+
// Infinite scroll accumulates: only a first page replaces what is on
|
|
395
|
+
// screen, every later one appends to it. A replacement also shrinks
|
|
396
|
+
// the buffer back under a viewport scrolled far past it.
|
|
397
|
+
loadMorePendingRef.current = false;
|
|
398
|
+
if (infiniteScroll && paginationState.pageIndex === 0 && viewportElement) {
|
|
399
|
+
viewportElement.scrollTop = 0;
|
|
400
|
+
}
|
|
401
|
+
setServerItems(previous => (infiniteScroll && paginationState.pageIndex > 0 ? [...previous, ...response.items] : response.items));
|
|
286
402
|
setServerTotalRows(response.totalRows);
|
|
287
403
|
setLoadFailed(false);
|
|
288
404
|
setAnnouncement(interpolate(mergedLabels.resultsAnnouncement, { count: response.totalRows }));
|
|
@@ -292,7 +408,8 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
292
408
|
if (id !== requestIdRef.current) {
|
|
293
409
|
return;
|
|
294
410
|
}
|
|
295
|
-
|
|
411
|
+
loadMorePendingRef.current = false;
|
|
412
|
+
setServerItems(EMPTY_ITEMS);
|
|
296
413
|
setServerTotalRows(0);
|
|
297
414
|
setLoadFailed(true);
|
|
298
415
|
onDataError === null || onDataError === void 0 ? void 0 : onDataError(error);
|
|
@@ -307,7 +424,9 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
307
424
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
308
425
|
}, [dataProvider, sortingState, columnFiltersState, globalFilterState, paginationState]);
|
|
309
426
|
const mounted = useRef(false);
|
|
310
|
-
const totalRows = serverSide ?
|
|
427
|
+
const totalRows = serverSide ?
|
|
428
|
+
serverTotalRows :
|
|
429
|
+
(typeof table.getFilteredRowModel === 'function' ? table.getFilteredRowModel() : table.getCoreRowModel()).rows.length;
|
|
311
430
|
useEffect(() => {
|
|
312
431
|
if (mounted.current) {
|
|
313
432
|
onSortingChange === null || onSortingChange === void 0 ? void 0 : onSortingChange(sortingState);
|
|
@@ -321,13 +440,15 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
321
440
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
322
441
|
}, [columnFiltersState, globalFilterState]);
|
|
323
442
|
useEffect(() => {
|
|
324
|
-
if (mounted.current) {
|
|
443
|
+
if (mounted.current && typeof table.getSelectedRowModel === 'function') {
|
|
325
444
|
onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(rowSelectionState, table.getSelectedRowModel().rows.map(row => row.original));
|
|
326
445
|
}
|
|
327
446
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
328
447
|
}, [rowSelectionState]);
|
|
448
|
+
// No onPaginationChange under infinite scroll — pagination is internal
|
|
449
|
+
// there (no widget, not user-adjustable); onDataLoad is the page signal.
|
|
329
450
|
useEffect(() => {
|
|
330
|
-
if (mounted.current) {
|
|
451
|
+
if (mounted.current && !infiniteScroll) {
|
|
331
452
|
onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(paginationState);
|
|
332
453
|
}
|
|
333
454
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -372,11 +493,17 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
372
493
|
// reshuffles rows would make it point at an arbitrary one. An in-flight
|
|
373
494
|
// edit stops (commit) first. A pending focus request marks an intentional
|
|
374
495
|
// navigation (PageUp/PageDown page flip) - keep the cell it just set.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
496
|
+
// Infinite scroll is the exception: its page bumps only append, so every
|
|
497
|
+
// row already on screen keeps its index and its focus.
|
|
498
|
+
const signature = JSON.stringify(infiniteScroll ?
|
|
499
|
+
[sortingState, columnFiltersState, globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : ''] :
|
|
500
|
+
[
|
|
501
|
+
sortingState,
|
|
502
|
+
columnFiltersState,
|
|
503
|
+
globalFilterState !== null && globalFilterState !== void 0 ? globalFilterState : '',
|
|
504
|
+
paginationState.pageIndex,
|
|
505
|
+
paginationState.pageSize
|
|
506
|
+
]);
|
|
380
507
|
if (rowSetSignatureRef.current === signature) {
|
|
381
508
|
return;
|
|
382
509
|
}
|
|
@@ -387,6 +514,7 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
387
514
|
setActiveCell(null);
|
|
388
515
|
}
|
|
389
516
|
}
|
|
517
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
390
518
|
}, [sortingState, columnFiltersState, globalFilterState, paginationState]);
|
|
391
519
|
useEffect(() => {
|
|
392
520
|
mounted.current = true;
|
|
@@ -398,9 +526,31 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
398
526
|
}, []);
|
|
399
527
|
const hasCustomFilterRow = columnFilters && columns.some(column => column.filter && column.filterable !== false);
|
|
400
528
|
const headerRowCount = hasCustomFilterRow ? 2 : 1;
|
|
401
|
-
const position = (
|
|
529
|
+
const position = (_q = paginationOptions.position) !== null && _q !== void 0 ? _q : 'bottom';
|
|
402
530
|
const rowCount = table.getRowModel().rows.length;
|
|
531
|
+
const appending = Boolean(infiniteScroll) && serverItems.length > 0;
|
|
532
|
+
const loadMore = () => {
|
|
533
|
+
if (loadMorePendingRef.current || rowCount === 0 || rowCount >= totalRows) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
loadMorePendingRef.current = true;
|
|
537
|
+
setPaginationState(previous => ({ ...previous, pageIndex: previous.pageIndex + 1 }));
|
|
538
|
+
};
|
|
403
539
|
const visibleColumnIds = visibleColumns(table, columns).map(column => column.key);
|
|
540
|
+
// The columns the selection's column indexes address, with the formatter
|
|
541
|
+
// each cell displays through — a copied date pastes as the date on screen.
|
|
542
|
+
const clipboardColumns = () => visibleColumns(table, columns).map(column => ({ id: column.key, formatter: column.formatter }));
|
|
543
|
+
// Prints every sorted and filtered row, not the virtual window or the
|
|
544
|
+
// current page: the grid renders a plain complete table, prints it, and
|
|
545
|
+
// throws it away.
|
|
546
|
+
const printGridTable = () => printGrid(table, visibleColumns(table, columns).map(column => {
|
|
547
|
+
var _a;
|
|
548
|
+
return ({
|
|
549
|
+
id: column.key,
|
|
550
|
+
label: (_a = column.label) !== null && _a !== void 0 ? _a : column.key,
|
|
551
|
+
formatter: column.formatter
|
|
552
|
+
});
|
|
553
|
+
}), { title: printTitle });
|
|
404
554
|
const activeCellContext = () => {
|
|
405
555
|
var _a;
|
|
406
556
|
return ({
|
|
@@ -453,6 +603,14 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
453
603
|
var _a;
|
|
454
604
|
focusRequestRef.current = cell;
|
|
455
605
|
setActiveCell(cell);
|
|
606
|
+
// The active cell is the grid's single focus owner; cell selection follows
|
|
607
|
+
// it rather than keeping a second anchor of its own.
|
|
608
|
+
if (cellSelection && cell.rowIndex >= 0) {
|
|
609
|
+
const row = table.getRowModel().rows[cell.rowIndex];
|
|
610
|
+
if (row) {
|
|
611
|
+
table.setFocusedCell(row.id, cell.columnId);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
456
614
|
if (virtualizationEnabled && cell.rowIndex >= 0) {
|
|
457
615
|
// No-op when the row is already in view (align: auto); otherwise the
|
|
458
616
|
// focus request stays pending until the row renders.
|
|
@@ -834,11 +992,37 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
834
992
|
}
|
|
835
993
|
return;
|
|
836
994
|
}
|
|
995
|
+
if (cellSelection && (event.ctrlKey || event.metaKey) && found.cell.rowIndex >= 0) {
|
|
996
|
+
const key = event.key.toLowerCase();
|
|
997
|
+
if (key === 'a') {
|
|
998
|
+
event.preventDefault();
|
|
999
|
+
table.selectAllCells();
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (key === 'c') {
|
|
1003
|
+
event.preventDefault();
|
|
1004
|
+
copyCellSelection(table, clipboardColumns()).then(text => {
|
|
1005
|
+
if (text !== '') {
|
|
1006
|
+
onCellCopy === null || onCellCopy === void 0 ? void 0 : onCellCopy({ text });
|
|
1007
|
+
}
|
|
1008
|
+
}, () => { });
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
837
1012
|
const result = resolveNavigation(event.key, event.ctrlKey || event.metaKey, found.cell, activeCellContext());
|
|
838
1013
|
if (!result) {
|
|
839
1014
|
return;
|
|
840
1015
|
}
|
|
841
1016
|
event.preventDefault();
|
|
1017
|
+
// Shift+arrow grows the range from its anchor and leaves focus put; a
|
|
1018
|
+
// plain arrow moves focus and collapses the range to the new cell.
|
|
1019
|
+
if (cellSelection && found.cell.rowIndex >= 0 && result.cell.rowIndex >= 0) {
|
|
1020
|
+
const direction = CELL_SELECTION_DIRECTIONS[event.key];
|
|
1021
|
+
if (event.shiftKey && direction) {
|
|
1022
|
+
table.extendCellSelection(direction);
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
842
1026
|
if (result.pageDelta === 1 && table.getCanNextPage()) {
|
|
843
1027
|
table.nextPage();
|
|
844
1028
|
}
|
|
@@ -860,9 +1044,9 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
860
1044
|
jsx(EmptySlot, { table: table, labels: mergedLabels }) :
|
|
861
1045
|
(loadFailed ? mergedLabels.loadError : empty);
|
|
862
1046
|
const editingConfig = editingCell ?
|
|
863
|
-
(
|
|
1047
|
+
(_r = columns.find(column => column.key === editingCell.columnId)) !== null && _r !== void 0 ? _r : null :
|
|
864
1048
|
null;
|
|
865
|
-
const EditorComponent = (
|
|
1049
|
+
const EditorComponent = (_s = editingConfig === null || editingConfig === void 0 ? void 0 : editingConfig.editor) !== null && _s !== void 0 ? _s : CDataGridCellEditor;
|
|
866
1050
|
const editorSession = editingCell && editingConfig ?
|
|
867
1051
|
{
|
|
868
1052
|
rowId: editingCell.rowId,
|
|
@@ -881,9 +1065,10 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
881
1065
|
onHidden: () => stopEditingRef.current({ commit: true, force: true })
|
|
882
1066
|
} :
|
|
883
1067
|
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 ?
|
|
1068
|
+
return (jsxs("div", { className: `data-grid${autoRowHeight && virtualizationEnabled ? ' data-grid-auto-row-height' : ''}${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
1069
|
jsx(ToolbarSlot, { table: table, labels: mergedLabels }) :
|
|
886
|
-
(resolvedToolbar.columns || resolvedToolbar.export || resolvedToolbar.
|
|
1070
|
+
(resolvedToolbar.columns || resolvedToolbar.export || resolvedToolbar.print ||
|
|
1071
|
+
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, onPrint: printGridTable, onRedo: redo, onUndo: undo, print: resolvedToolbar.print, printIcon: toolbarPrintIcon, 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
1072
|
// Focus parks here when the focused row is recycled out of the
|
|
888
1073
|
// window; the keydown fallback then scrolls the active cell back.
|
|
889
1074
|
tabIndex: -1,
|
|
@@ -893,7 +1078,7 @@ const CDataGrid = forwardRef((props, ref) => {
|
|
|
893
1078
|
}), ...(editing && { onDoubleClick: handleEditingDoubleClick }), children: [jsxs("table", { className: "table", "aria-rowcount": headerRowCount + totalRows, ...(cellNavigationEnabled && {
|
|
894
1079
|
role: 'grid',
|
|
895
1080
|
'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')] }));
|
|
1081
|
+
}), 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, rowOrder: rowOrder, 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, loadMoreThreshold: infiniteScroll ? (_t = infiniteOptions.threshold) !== null && _t !== void 0 ? _t : overscan : null, onLoadMore: loadMore, overscan: overscan, pinning: pinningEnabled, autoRowHeight: autoRowHeight && virtualizationEnabled, rowHeight: rowHeight, cellSelection: cellSelection, rowHandleIcon: rowHandleIcon, rowIndexOffset: paginationEnabled ? paginationState.pageIndex * paginationState.pageSize : 0, rowOrder: rowOrder, scrollElement: viewportElement, scrollToIndexRef: scrollToIndexRef, selection: selectionEnabled, tabStop: tabStop, table: table, virtualization: virtualizationEnabled })] }), serverSide && (jsx("div", { className: "data-grid-loading", hidden: !loading || appending, children: jsx("div", { className: "spinner-border", role: "status", children: jsx("span", { className: "visually-hidden", children: mergedLabels.loading }) }) })), Boolean(infiniteScroll) && (jsx("div", { className: "data-grid-loading-more", hidden: !loading || !appending, 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
1082
|
});
|
|
898
1083
|
CDataGrid.displayName = 'CDataGrid';
|
|
899
1084
|
|