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