@coreui/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 +5 -4
- package/dist/css/data-grid.css +149 -31
- package/dist/css/data-grid.css.map +1 -1
- package/dist/css/data-grid.min.css +2 -2
- package/dist/css/data-grid.min.css.map +1 -1
- package/dist/js/clipboard.cjs +56 -0
- package/dist/js/clipboard.cjs.map +1 -0
- package/dist/js/clipboard.mjs +54 -0
- package/dist/js/clipboard.mjs.map +1 -0
- package/dist/js/csv.cjs +36 -59
- package/dist/js/csv.cjs.map +1 -1
- package/dist/js/csv.mjs +35 -55
- package/dist/js/csv.mjs.map +1 -1
- package/dist/js/data-grid.esm.js +14078 -9332
- package/dist/js/data-grid.esm.js.map +1 -1
- package/dist/js/data-grid.esm.min.js +6 -6
- package/dist/js/data-grid.esm.min.js.map +1 -1
- package/dist/js/data-grid.js +14377 -9634
- package/dist/js/data-grid.js.map +1 -1
- package/dist/js/data-grid.lite.esm.js +10476 -0
- package/dist/js/data-grid.lite.esm.js.map +1 -0
- package/dist/js/data-grid.lite.esm.min.js +8 -0
- package/dist/js/data-grid.lite.esm.min.js.map +1 -0
- package/dist/js/data-grid.lite.js +10489 -0
- package/dist/js/data-grid.lite.js.map +1 -0
- package/dist/js/data-grid.lite.min.js +8 -0
- package/dist/js/data-grid.lite.min.js.map +1 -0
- package/dist/js/data-grid.lite.mjs +10476 -0
- package/dist/js/data-grid.lite.mjs.map +1 -0
- package/dist/js/data-grid.min.js +6 -6
- package/dist/js/data-grid.min.js.map +1 -1
- package/dist/js/data-grid.mjs +14078 -9332
- package/dist/js/data-grid.mjs.map +1 -1
- package/dist/js/drag.cjs +459 -0
- package/dist/js/drag.cjs.map +1 -0
- package/dist/js/drag.mjs +456 -0
- package/dist/js/drag.mjs.map +1 -0
- package/dist/js/export-table-CNAKStzj.mjs +26 -0
- package/dist/js/export-table-CNAKStzj.mjs.map +1 -0
- package/dist/js/export-table-doIwvE5U.cjs +55 -0
- package/dist/js/export-table-doIwvE5U.cjs.map +1 -0
- package/dist/js/filters.cjs +89 -132
- package/dist/js/filters.cjs.map +1 -1
- package/dist/js/filters.mjs +88 -128
- package/dist/js/filters.mjs.map +1 -1
- package/dist/js/labels.cjs +65 -70
- package/dist/js/labels.cjs.map +1 -1
- package/dist/js/labels.mjs +64 -66
- package/dist/js/labels.mjs.map +1 -1
- package/dist/js/print.cjs +66 -0
- package/dist/js/print.cjs.map +1 -0
- package/dist/js/print.mjs +64 -0
- package/dist/js/print.mjs.map +1 -0
- package/dist/js/validate.cjs +43 -0
- package/dist/js/validate.cjs.map +1 -0
- package/dist/js/validate.mjs +42 -0
- package/dist/js/validate.mjs.map +1 -0
- package/dist/js/xlsx.cjs +240 -0
- package/dist/js/xlsx.cjs.map +1 -0
- package/dist/js/xlsx.mjs +238 -0
- package/dist/js/xlsx.mjs.map +1 -0
- package/dist/types/base-component.d.ts +28 -18
- package/dist/types/data-grid.d.ts +26 -13
- package/dist/types/dom/data.d.ts +15 -0
- package/dist/types/dom/event-handler.d.ts +21 -10
- package/dist/types/dom/manipulator.d.ts +15 -8
- package/dist/types/engine/data-source.d.ts +1 -1
- package/dist/types/engine/features-lite.d.ts +25 -0
- package/dist/types/engine/features.d.ts +35 -0
- package/dist/types/engine/table.d.ts +3 -3
- package/dist/types/engine/validate.d.mts +1 -0
- package/dist/types/engine/validate.d.ts +20 -0
- package/dist/types/engine/virtualizer.d.ts +2 -0
- package/dist/types/icons.d.ts +6 -4
- package/dist/types/index-lite.d.mts +1 -0
- package/dist/types/index-lite.d.ts +19 -0
- package/dist/types/index.d.ts +11 -2
- package/dist/types/types.d.ts +53 -31
- package/dist/types/ui/body.d.ts +6 -3
- package/dist/types/ui/header.d.ts +3 -2
- package/dist/types/ui/loading.d.ts +1 -1
- package/dist/types/ui/pinning.d.ts +1 -1
- package/dist/types/ui/toolbar.d.ts +10 -2
- package/dist/types/util/clipboard.d.mts +1 -0
- package/dist/types/util/clipboard.d.ts +29 -0
- package/dist/types/util/config.d.ts +27 -10
- package/dist/types/util/csv.d.ts +5 -5
- package/dist/types/util/drag.d.mts +1 -0
- package/dist/types/util/drag.d.ts +48 -0
- package/dist/types/util/export-table.d.ts +47 -0
- package/dist/types/util/index.d.ts +66 -10
- package/dist/types/util/print.d.mts +1 -0
- package/dist/types/util/print.d.ts +32 -0
- package/dist/types/util/sanitizer.d.ts +15 -11
- package/dist/types/util/xlsx.d.mts +1 -0
- package/dist/types/util/xlsx.d.ts +17 -0
- package/dist/types/util/zip.d.ts +11 -0
- package/package.json +88 -10
- package/scss/data-grid.scss +204 -16
- package/src/{base-component.js → base-component.ts} +21 -17
- package/src/data-grid.ts +507 -200
- package/src/dom/{data.js → data.ts} +13 -10
- package/src/dom/event-handler.ts +380 -0
- package/src/dom/{manipulator.js → manipulator.ts} +11 -11
- package/src/engine/data-source.ts +7 -3
- package/src/engine/features-lite.ts +47 -0
- package/src/engine/features.ts +68 -0
- package/src/engine/table.ts +80 -47
- package/src/engine/validate.ts +64 -0
- package/src/engine/virtualizer.ts +17 -1
- package/src/icons.ts +11 -5
- package/src/index-lite.ts +85 -0
- package/src/index.ts +30 -2
- package/src/labels.ts +6 -4
- package/src/types.ts +87 -32
- package/src/ui/body.ts +73 -17
- package/src/ui/header.ts +51 -26
- package/src/ui/loading.ts +2 -2
- package/src/ui/pinning.ts +16 -16
- package/src/ui/toolbar.ts +27 -3
- package/src/util/clipboard.ts +112 -0
- package/src/util/{config.js → config.ts} +25 -11
- package/src/util/csv.ts +11 -30
- package/src/util/drag.ts +798 -0
- package/src/util/export-table.ts +86 -0
- package/src/util/{index.js → index.ts} +55 -35
- package/src/util/print.ts +125 -0
- package/src/util/{sanitizer.js → sanitizer.ts} +17 -13
- package/src/util/xlsx.ts +183 -0
- package/src/util/zip.ts +170 -0
- package/src/base-component.d.ts +0 -19
- package/src/dom/event-handler.d.ts +0 -11
- package/src/dom/event-handler.js +0 -331
- package/src/dom/manipulator.d.ts +0 -9
- package/src/util/config.d.ts +0 -11
- package/src/util/index.d.ts +0 -10
- package/src/util/sanitizer.d.ts +0 -11
package/src/data-grid.ts
CHANGED
|
@@ -5,26 +5,34 @@
|
|
|
5
5
|
* --------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import { batch } from '@tanstack/store'
|
|
9
9
|
import BaseComponent from './base-component.js'
|
|
10
|
+
import type { ComponentConfig } from './util/config.js'
|
|
10
11
|
import EventHandler from './dom/event-handler.js'
|
|
11
12
|
import {
|
|
12
13
|
createActiveCellManager, resolveNavigation, type ActiveCell, type ActiveCellContext, type ActiveCellManager
|
|
13
14
|
} from './engine/active-cell'
|
|
14
15
|
import { createDataSource, type GridDataSource } from './engine/data-source'
|
|
16
|
+
import { copyCellSelection, type ClipboardColumn } from './util/clipboard'
|
|
17
|
+
import { printGrid, type PrintColumn } from './util/print'
|
|
18
|
+
import {
|
|
19
|
+
createColumnDragController, createRowDragController, type DragController
|
|
20
|
+
} from './util/drag'
|
|
15
21
|
import { createScheduler, type Scheduler } from './engine/scheduler'
|
|
16
|
-
import { createGridTable } from './engine/table'
|
|
22
|
+
import { createGridTable, subscribeToTableState } from './engine/table'
|
|
17
23
|
import { createGridVirtualizer, type GridVirtualizer } from './engine/virtualizer'
|
|
18
24
|
import {
|
|
19
|
-
DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON,
|
|
20
|
-
|
|
25
|
+
DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON, DEFAULT_MOVE_END_ICON, DEFAULT_MOVE_START_ICON,
|
|
26
|
+
DEFAULT_PIN_END_ICON, DEFAULT_PIN_START_ICON, DEFAULT_ROW_HANDLE_ICON, DEFAULT_SORT_ASCENDING_ICON,
|
|
27
|
+
DEFAULT_SORT_DESCENDING_ICON,
|
|
21
28
|
DEFAULT_SORT_NEUTRAL_ICON, DEFAULT_TOOLBAR_COLUMNS_ICON, DEFAULT_TOOLBAR_EXPORT_ICON,
|
|
22
|
-
DEFAULT_TOOLBAR_REDO_ICON, DEFAULT_TOOLBAR_UNDO_ICON, DEFAULT_UNPIN_ICON
|
|
29
|
+
DEFAULT_TOOLBAR_PRINT_ICON, DEFAULT_TOOLBAR_REDO_ICON, DEFAULT_TOOLBAR_UNDO_ICON, DEFAULT_UNPIN_ICON
|
|
23
30
|
} from './icons'
|
|
24
31
|
import { DEFAULT_LABELS, interpolate } from './labels'
|
|
25
32
|
import type {
|
|
26
33
|
DataGridColumn, DataGridColumnFilterValue, DataGridEditableOptions, DataGridEditor, DataGridEditorContext,
|
|
27
|
-
DataGridItem, DataGridLabels, DataGridMenuAction, DataGridOptions, DataGridSlot,
|
|
34
|
+
DataGridFeatures, DataGridItem, DataGridLabels, DataGridMenuAction, DataGridOptions, DataGridSlot,
|
|
35
|
+
DataGridSlotContext, DataGridState, DataGridTable, DataGridTableState, TableFeatures
|
|
28
36
|
} from './types'
|
|
29
37
|
import { createBody, type GridBody } from './ui/body'
|
|
30
38
|
import { createBuiltInEditor } from './ui/editor'
|
|
@@ -41,7 +49,6 @@ import {
|
|
|
41
49
|
downloadCsv, exportCsv, type CsvDownloadOptions, type CsvOptions
|
|
42
50
|
} from './util/csv'
|
|
43
51
|
import { debounce } from './util/debounce'
|
|
44
|
-
import { defineJQueryPlugin } from './util/index.js'
|
|
45
52
|
import { DefaultAllowlist, sanitizeHtml, SVGAllowlist } from './util/sanitizer.js'
|
|
46
53
|
|
|
47
54
|
/**
|
|
@@ -62,7 +69,9 @@ const EVENT_EDIT_COMMIT = `editCommit${EVENT_KEY}`
|
|
|
62
69
|
const EVENT_EDIT_START = `editStart${EVENT_KEY}`
|
|
63
70
|
const EVENT_FILTER_CHANGE = `filterChange${EVENT_KEY}`
|
|
64
71
|
const EVENT_INPUT = `input${EVENT_KEY}`
|
|
72
|
+
const EVENT_CELL_COPY = `cellCopy${EVENT_KEY}`
|
|
65
73
|
const EVENT_ORDER_CHANGE = `orderChange${EVENT_KEY}`
|
|
74
|
+
const EVENT_ROW_ORDER_CHANGE = `rowOrderChange${EVENT_KEY}`
|
|
66
75
|
const EVENT_PAGINATION_CHANGE = `paginationChange${EVENT_KEY}`
|
|
67
76
|
const EVENT_PINNING_CHANGE = `pinningChange${EVENT_KEY}`
|
|
68
77
|
const EVENT_SELECTION_CHANGE = `selectionChange${EVENT_KEY}`
|
|
@@ -74,27 +83,23 @@ const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
|
|
|
74
83
|
const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`
|
|
75
84
|
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
|
|
76
85
|
|
|
77
|
-
const EVENT_DRAGEND = `dragend${EVENT_KEY}`
|
|
78
|
-
const EVENT_DRAGLEAVE = `dragleave${EVENT_KEY}`
|
|
79
|
-
const EVENT_DRAGOVER = `dragover${EVENT_KEY}`
|
|
80
|
-
const EVENT_DRAGSTART = `dragstart${EVENT_KEY}`
|
|
81
|
-
const EVENT_DROP = `drop${EVENT_KEY}`
|
|
82
86
|
const EVENT_MOUSEDOWN = `mousedown${EVENT_KEY}`
|
|
87
|
+
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
|
|
83
88
|
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
|
|
84
89
|
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
|
|
85
90
|
|
|
91
|
+
const CLASS_NAME_AUTO_ROW_HEIGHT = 'data-grid-auto-row-height'
|
|
86
92
|
const CLASS_NAME_DATA_GRID = 'data-grid'
|
|
87
|
-
const CLASS_NAME_DRAGGING = 'data-grid-dragging'
|
|
88
93
|
const CLASS_NAME_EDITOR_POPUP = 'data-grid-editor-popup'
|
|
89
|
-
const
|
|
94
|
+
const CLASS_NAME_LOADING_MORE = 'data-grid-loading-more'
|
|
90
95
|
const CLASS_NAME_PAGINATION_TOP = 'data-grid-pagination-top'
|
|
91
96
|
const CLASS_NAME_PINNED = 'data-grid-pinned'
|
|
92
97
|
const CLASS_NAME_RESIZABLE = 'data-grid-resizable'
|
|
93
98
|
const CLASS_NAME_TABLE = 'table'
|
|
94
99
|
const CLASS_NAME_VIEWPORT = 'data-grid-viewport'
|
|
95
100
|
|
|
101
|
+
const SELECTOR_BODY_CELL = 'tbody td[data-coreui-column]'
|
|
96
102
|
const SELECTOR_COLUMN_FILTER = 'input[data-coreui-column-filter]'
|
|
97
|
-
const SELECTOR_DRAGGABLE = 'th[data-coreui-draggable]'
|
|
98
103
|
const SELECTOR_MENU_BUTTON = 'button[data-coreui-menu]'
|
|
99
104
|
const SELECTOR_FILTER_BUTTON = 'button[data-coreui-filter]'
|
|
100
105
|
const SELECTOR_GLOBAL_FILTER = 'input[data-coreui-global-filter]'
|
|
@@ -106,6 +111,7 @@ const SELECTOR_SELECT_ALL = 'input[data-coreui-select-all]'
|
|
|
106
111
|
const SELECTOR_SORT_BUTTON = '.data-grid-sort-button'
|
|
107
112
|
const SELECTOR_TOOLBAR_COLUMNS = 'button[data-coreui-toolbar-columns]'
|
|
108
113
|
const SELECTOR_TOOLBAR_EXPORT = 'button[data-coreui-toolbar-export]'
|
|
114
|
+
const SELECTOR_TOOLBAR_PRINT = 'button[data-coreui-toolbar-print]'
|
|
109
115
|
const SELECTOR_TOOLBAR_REDO = 'button[data-coreui-toolbar-redo]'
|
|
110
116
|
const SELECTOR_TOOLBAR_UNDO = 'button[data-coreui-toolbar-undo]'
|
|
111
117
|
|
|
@@ -128,6 +134,13 @@ const STATE_STORAGE_PREFIX = 'coreui-data-grid:'
|
|
|
128
134
|
const STATE_AUTOSAVE_DEBOUNCE_MS = 250
|
|
129
135
|
const HISTORY_LIMIT = 100
|
|
130
136
|
|
|
137
|
+
const CELL_SELECTION_DIRECTIONS: Record<string, 'up' | 'down' | 'left' | 'right' | undefined> = {
|
|
138
|
+
ArrowDown: 'down',
|
|
139
|
+
ArrowLeft: 'left',
|
|
140
|
+
ArrowRight: 'right',
|
|
141
|
+
ArrowUp: 'up'
|
|
142
|
+
}
|
|
143
|
+
|
|
131
144
|
const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]'
|
|
132
145
|
|
|
133
146
|
const applyTabIndex = (cellElement: HTMLElement, isTabbable: boolean) => {
|
|
@@ -138,7 +151,9 @@ const applyTabIndex = (cellElement: HTMLElement, isTabbable: boolean) => {
|
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
const Default: DataGridOptions = {
|
|
154
|
+
autoRowHeight: false,
|
|
141
155
|
cellNavigation: false,
|
|
156
|
+
cellSelection: false,
|
|
142
157
|
columnFilters: false,
|
|
143
158
|
columnMenu: false,
|
|
144
159
|
columnMenuIcon: DEFAULT_COLUMN_MENU_ICON,
|
|
@@ -150,20 +165,25 @@ const Default: DataGridOptions = {
|
|
|
150
165
|
dataProvider: null,
|
|
151
166
|
editing: false,
|
|
152
167
|
empty: 'No items found',
|
|
168
|
+
features: null,
|
|
153
169
|
filterIcon: DEFAULT_FILTER_ICON,
|
|
154
170
|
globalFilter: false,
|
|
155
171
|
hideColumnIcon: DEFAULT_HIDE_COLUMN_ICON,
|
|
156
172
|
history: false,
|
|
173
|
+
infiniteScroll: false,
|
|
157
174
|
itemKey: null,
|
|
158
175
|
items: [],
|
|
159
176
|
labels: {},
|
|
160
|
-
|
|
161
|
-
|
|
177
|
+
moveEndIcon: DEFAULT_MOVE_END_ICON,
|
|
178
|
+
moveStartIcon: DEFAULT_MOVE_START_ICON,
|
|
162
179
|
overscan: 10,
|
|
163
180
|
pagination: false,
|
|
164
|
-
|
|
165
|
-
|
|
181
|
+
pinEndIcon: DEFAULT_PIN_END_ICON,
|
|
182
|
+
pinStartIcon: DEFAULT_PIN_START_ICON,
|
|
183
|
+
printTitle: null,
|
|
184
|
+
rowHandleIcon: DEFAULT_ROW_HANDLE_ICON,
|
|
166
185
|
rowHeight: 44,
|
|
186
|
+
rowOrder: false,
|
|
167
187
|
rowSelection: false,
|
|
168
188
|
sanitize: true,
|
|
169
189
|
sanitizeFn: null,
|
|
@@ -177,6 +197,7 @@ const Default: DataGridOptions = {
|
|
|
177
197
|
toolbar: false,
|
|
178
198
|
toolbarColumnsIcon: DEFAULT_TOOLBAR_COLUMNS_ICON,
|
|
179
199
|
toolbarExportIcon: DEFAULT_TOOLBAR_EXPORT_ICON,
|
|
200
|
+
toolbarPrintIcon: DEFAULT_TOOLBAR_PRINT_ICON,
|
|
180
201
|
toolbarRedoIcon: DEFAULT_TOOLBAR_REDO_ICON,
|
|
181
202
|
toolbarUndoIcon: DEFAULT_TOOLBAR_UNDO_ICON,
|
|
182
203
|
unpinIcon: DEFAULT_UNPIN_ICON,
|
|
@@ -184,7 +205,9 @@ const Default: DataGridOptions = {
|
|
|
184
205
|
}
|
|
185
206
|
|
|
186
207
|
const DefaultType = {
|
|
208
|
+
autoRowHeight: 'boolean',
|
|
187
209
|
cellNavigation: 'boolean',
|
|
210
|
+
cellSelection: 'boolean',
|
|
188
211
|
columnFilters: 'boolean',
|
|
189
212
|
columnMenu: '(boolean|function)',
|
|
190
213
|
columnMenuIcon: 'string',
|
|
@@ -196,20 +219,25 @@ const DefaultType = {
|
|
|
196
219
|
dataProvider: '(function|null)',
|
|
197
220
|
editing: 'boolean',
|
|
198
221
|
empty: '(string|function)',
|
|
222
|
+
features: '(object|null)',
|
|
199
223
|
filterIcon: 'string',
|
|
200
224
|
globalFilter: 'boolean',
|
|
201
225
|
hideColumnIcon: 'string',
|
|
202
226
|
history: 'boolean',
|
|
227
|
+
infiniteScroll: '(boolean|object)',
|
|
203
228
|
itemKey: '(function|null)',
|
|
204
229
|
items: 'array',
|
|
205
230
|
labels: 'object',
|
|
206
|
-
|
|
207
|
-
|
|
231
|
+
moveEndIcon: 'string',
|
|
232
|
+
moveStartIcon: 'string',
|
|
208
233
|
overscan: 'number',
|
|
209
234
|
pagination: '(boolean|object)',
|
|
210
|
-
|
|
211
|
-
|
|
235
|
+
pinEndIcon: 'string',
|
|
236
|
+
pinStartIcon: 'string',
|
|
237
|
+
printTitle: '(string|null)',
|
|
238
|
+
rowHandleIcon: 'string',
|
|
212
239
|
rowHeight: 'number',
|
|
240
|
+
rowOrder: 'boolean',
|
|
213
241
|
rowSelection: '(boolean|object)',
|
|
214
242
|
sanitize: 'boolean',
|
|
215
243
|
sanitizeFn: '(function|null)',
|
|
@@ -223,6 +251,7 @@ const DefaultType = {
|
|
|
223
251
|
toolbar: '(boolean|object)',
|
|
224
252
|
toolbarColumnsIcon: 'string',
|
|
225
253
|
toolbarExportIcon: 'string',
|
|
254
|
+
toolbarPrintIcon: 'string',
|
|
226
255
|
toolbarRedoIcon: 'string',
|
|
227
256
|
toolbarUndoIcon: 'string',
|
|
228
257
|
unpinIcon: 'string',
|
|
@@ -233,9 +262,13 @@ const DefaultType = {
|
|
|
233
262
|
* Class definition
|
|
234
263
|
*/
|
|
235
264
|
|
|
236
|
-
class DataGrid extends BaseComponent {
|
|
265
|
+
class DataGrid<TFeatures extends TableFeatures = DataGridFeatures> extends BaseComponent {
|
|
237
266
|
declare _config: DataGridOptions
|
|
238
|
-
|
|
267
|
+
// Typed on the full set even when the runtime set is smaller - internals
|
|
268
|
+
// capability-guard every optional-feature call, and the public `table`
|
|
269
|
+
// getter narrows back to the instance's TFeatures.
|
|
270
|
+
private _table!: DataGridTable
|
|
271
|
+
private _unsubscribeState: (() => void) | null = null
|
|
239
272
|
private _scheduler!: Scheduler
|
|
240
273
|
private _virtualizer: GridVirtualizer | null = null
|
|
241
274
|
private _header!: GridHeader
|
|
@@ -245,7 +278,13 @@ class DataGrid extends BaseComponent {
|
|
|
245
278
|
private _emptySlot: DataGridSlot | null = null
|
|
246
279
|
private _dataSource: GridDataSource | null = null
|
|
247
280
|
private _loading: GridLoading | null = null
|
|
281
|
+
private _loadingMore: GridLoading | null = null
|
|
248
282
|
private _loadFailed = false
|
|
283
|
+
// Infinite scroll accumulates every loaded page here; the table's data is
|
|
284
|
+
// this buffer, not the last response.
|
|
285
|
+
private _loadedItems: DataGridItem[] = []
|
|
286
|
+
private _loadMorePending = false
|
|
287
|
+
private _pageResetPending = false
|
|
249
288
|
private _dataVersion = 0
|
|
250
289
|
private _viewportElement!: HTMLDivElement
|
|
251
290
|
private _tableElement!: HTMLTableElement
|
|
@@ -272,14 +311,15 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
272
311
|
private _editorErrorElement: HTMLElement | null = null
|
|
273
312
|
private _editorFocusPending = false
|
|
274
313
|
private _columnsByKey = new Map<string, DataGridColumn>()
|
|
275
|
-
private
|
|
314
|
+
private _columnDrag: DragController | null = null
|
|
315
|
+
private _rowDrag: DragController | null = null
|
|
276
316
|
private _menu: GridMenu | null = null
|
|
277
317
|
private _tooltip: GridTooltip | null = null
|
|
278
318
|
private _filterMenu: GridFilterMenu | null = null
|
|
279
319
|
private _columnChooser: GridColumnChooser | null = null
|
|
280
320
|
private _toolbarConfig: ResolvedToolbar | null = null
|
|
281
321
|
|
|
282
|
-
constructor(element: Element | string, config?: Partial<DataGridOptions
|
|
322
|
+
constructor(element: Element | string, config?: Partial<DataGridOptions<TFeatures>>) {
|
|
283
323
|
super(element, config)
|
|
284
324
|
|
|
285
325
|
if (!this._element) {
|
|
@@ -305,8 +345,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
305
345
|
}
|
|
306
346
|
|
|
307
347
|
// Getters
|
|
308
|
-
get table():
|
|
309
|
-
|
|
348
|
+
get table(): DataGridTable<TFeatures> {
|
|
349
|
+
// TFeatures is invariant, so the internal full-set alias needs the double
|
|
350
|
+
// cast back to the instance's actual set.
|
|
351
|
+
return this._table as unknown as DataGridTable<TFeatures>
|
|
310
352
|
}
|
|
311
353
|
|
|
312
354
|
static get Default(): DataGridOptions {
|
|
@@ -321,19 +363,22 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
321
363
|
return NAME
|
|
322
364
|
}
|
|
323
365
|
|
|
324
|
-
static getInstance(element: Element | string | null): DataGrid | null {
|
|
325
|
-
return super.getInstance(element) as DataGrid | null
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
static getOrCreateInstance(element: Element | string, config?: Partial<DataGridOptions>): DataGrid {
|
|
329
|
-
return super.getOrCreateInstance(element, config) as DataGrid
|
|
330
|
-
}
|
|
331
|
-
|
|
332
366
|
// Public
|
|
333
367
|
getSelectedItems(): DataGridItem[] {
|
|
368
|
+
if (typeof this._table.getSelectedRowModel !== 'function') {
|
|
369
|
+
return []
|
|
370
|
+
}
|
|
371
|
+
|
|
334
372
|
return this._table.getSelectedRowModel().rows.map(row => row.original)
|
|
335
373
|
}
|
|
336
374
|
|
|
375
|
+
// Prints every sorted and filtered row, not the virtual window or the
|
|
376
|
+
// current page: the grid renders a plain complete table, prints it, and
|
|
377
|
+
// throws it away.
|
|
378
|
+
print(): void {
|
|
379
|
+
printGrid(this._table, this._printColumns(), { title: this._config.printTitle ?? undefined })
|
|
380
|
+
}
|
|
381
|
+
|
|
337
382
|
getCsv(options?: CsvOptions): string {
|
|
338
383
|
return exportCsv(this._table, options)
|
|
339
384
|
}
|
|
@@ -345,22 +390,66 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
345
390
|
// A serializable snapshot of every user-adjustable state slice. Feed it
|
|
346
391
|
// back through restoreState() - scroll position and edit drafts excluded.
|
|
347
392
|
getState(): DataGridState {
|
|
348
|
-
|
|
393
|
+
// A reduced feature set has no atom for its absent slices - the snapshot
|
|
394
|
+
// keeps its full shape with neutral values so persisted state stays
|
|
395
|
+
// portable between feature sets.
|
|
396
|
+
const { state } = this._table.store
|
|
349
397
|
return {
|
|
350
|
-
columnFilters: state.columnFilters,
|
|
351
|
-
columnOrder: state.columnOrder,
|
|
352
|
-
columnPinning: state.columnPinning,
|
|
353
|
-
columnSizing: state.columnSizing,
|
|
354
|
-
columnVisibility: state.columnVisibility,
|
|
398
|
+
columnFilters: state.columnFilters ?? [],
|
|
399
|
+
columnOrder: state.columnOrder ?? [],
|
|
400
|
+
columnPinning: state.columnPinning ?? { start: [], end: [] },
|
|
401
|
+
columnSizing: state.columnSizing ?? {},
|
|
402
|
+
columnVisibility: state.columnVisibility ?? {},
|
|
355
403
|
globalFilter: (state.globalFilter as string | undefined) ?? '',
|
|
356
|
-
pagination: state.pagination,
|
|
357
|
-
rowSelection: state.rowSelection,
|
|
358
|
-
sorting: state.sorting
|
|
404
|
+
pagination: state.pagination ?? { pageIndex: 0, pageSize: Number.MAX_SAFE_INTEGER },
|
|
405
|
+
rowSelection: state.rowSelection ?? {},
|
|
406
|
+
sorting: state.sorting ?? []
|
|
359
407
|
}
|
|
360
408
|
}
|
|
361
409
|
|
|
362
410
|
restoreState(state: Partial<DataGridState>): void {
|
|
363
|
-
|
|
411
|
+
// v9 owns one atom per slice and has no bulk state setter; batching keeps
|
|
412
|
+
// the writes to a single store emission, so the change events still fire
|
|
413
|
+
// once for the whole restore.
|
|
414
|
+
// The setter-presence checks let a snapshot taken on one feature set
|
|
415
|
+
// restore into a grid running a smaller one - absent slices are skipped.
|
|
416
|
+
batch(() => {
|
|
417
|
+
if (state.columnFilters !== undefined && typeof this._table.setColumnFilters === 'function') {
|
|
418
|
+
this._table.setColumnFilters(state.columnFilters)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (state.columnOrder !== undefined && typeof this._table.setColumnOrder === 'function') {
|
|
422
|
+
this._table.setColumnOrder(state.columnOrder)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (state.columnPinning !== undefined && typeof this._table.setColumnPinning === 'function') {
|
|
426
|
+
this._table.setColumnPinning(state.columnPinning)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (state.columnSizing !== undefined && typeof this._table.setColumnSizing === 'function') {
|
|
430
|
+
this._table.setColumnSizing(state.columnSizing)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (state.columnVisibility !== undefined && typeof this._table.setColumnVisibility === 'function') {
|
|
434
|
+
this._table.setColumnVisibility(state.columnVisibility)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (state.globalFilter !== undefined && typeof this._table.setGlobalFilter === 'function') {
|
|
438
|
+
this._table.setGlobalFilter(state.globalFilter)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (state.pagination !== undefined && typeof this._table.setPagination === 'function') {
|
|
442
|
+
this._table.setPagination(state.pagination)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (state.rowSelection !== undefined && typeof this._table.setRowSelection === 'function') {
|
|
446
|
+
this._table.setRowSelection(state.rowSelection)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (state.sorting !== undefined && typeof this._table.setSorting === 'function') {
|
|
450
|
+
this._table.setSorting(state.sorting)
|
|
451
|
+
}
|
|
452
|
+
})
|
|
364
453
|
|
|
365
454
|
// The built-in search input is uncontrolled - sync it to the restored
|
|
366
455
|
// filter so the UI matches the state.
|
|
@@ -424,19 +513,23 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
424
513
|
this._stopEditing({ commit: false })
|
|
425
514
|
this._config.items = items
|
|
426
515
|
this._dataVersion++
|
|
516
|
+
this._virtualizer?.remeasure()
|
|
427
517
|
this._table.setOptions(previous => ({ ...previous, data: items }))
|
|
428
518
|
this._scheduler.schedule()
|
|
429
519
|
}
|
|
430
520
|
|
|
431
521
|
// Full rebuild: recreates the table, wiping sorting/filters/selection/scroll.
|
|
432
522
|
// Use setItems() to swap data while keeping user state.
|
|
433
|
-
update(config: Partial<DataGridOptions
|
|
434
|
-
this._config = this._getConfig({ ...this._config, ...config })
|
|
523
|
+
update(config: Partial<DataGridOptions<TFeatures>>): void {
|
|
524
|
+
this._config = this._getConfig({ ...this._config, ...config }) as DataGridOptions
|
|
435
525
|
this._labels = { ...DEFAULT_LABELS, ...this._config.labels }
|
|
436
526
|
|
|
437
527
|
this._teardownUi()
|
|
438
528
|
this._selectionAnchorIndex = null
|
|
439
529
|
this._loadFailed = false
|
|
530
|
+
this._pageResetPending = false
|
|
531
|
+
this._loadedItems = []
|
|
532
|
+
this._loadMorePending = false
|
|
440
533
|
this._dataVersion++
|
|
441
534
|
this._table = this._createTable()
|
|
442
535
|
this._render()
|
|
@@ -445,6 +538,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
445
538
|
|
|
446
539
|
dispose(): void {
|
|
447
540
|
this._teardownUi()
|
|
541
|
+
this._unsubscribeState?.()
|
|
542
|
+
this._unsubscribeState = null
|
|
448
543
|
this._scheduler.dispose()
|
|
449
544
|
this._tooltip?.dispose()
|
|
450
545
|
this._tooltip = null
|
|
@@ -460,19 +555,25 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
460
555
|
|
|
461
556
|
// Private
|
|
462
557
|
// Editing needs the active-cell substrate (Enter/F2 target the active cell).
|
|
463
|
-
_configAfterMerge(config:
|
|
464
|
-
|
|
558
|
+
_configAfterMerge(config: ComponentConfig): ComponentConfig {
|
|
559
|
+
// Both need the active-cell substrate: editing targets it with Enter/F2,
|
|
560
|
+
// cell selection anchors ranges on it.
|
|
561
|
+
if (config.editing || config.cellSelection) {
|
|
465
562
|
config.cellNavigation = true
|
|
466
563
|
}
|
|
467
564
|
|
|
468
565
|
return config
|
|
469
566
|
}
|
|
470
567
|
|
|
471
|
-
private _createTable():
|
|
472
|
-
|
|
568
|
+
private _createTable(): DataGridTable {
|
|
569
|
+
this._unsubscribeState?.()
|
|
570
|
+
const table = createGridTable(this._config)
|
|
571
|
+
this._unsubscribeState = subscribeToTableState(table, (previous, next) => {
|
|
473
572
|
this._notifyStateChange(previous, next)
|
|
474
573
|
this._scheduler.schedule()
|
|
475
574
|
})
|
|
575
|
+
|
|
576
|
+
return table
|
|
476
577
|
}
|
|
477
578
|
|
|
478
579
|
private _teardownUi(): void {
|
|
@@ -505,6 +606,21 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
505
606
|
this._loading = null
|
|
506
607
|
}
|
|
507
608
|
|
|
609
|
+
if (this._loadingMore) {
|
|
610
|
+
this._loadingMore.element.remove()
|
|
611
|
+
this._loadingMore = null
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (this._columnDrag) {
|
|
615
|
+
this._columnDrag.dispose()
|
|
616
|
+
this._columnDrag = null
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (this._rowDrag) {
|
|
620
|
+
this._rowDrag.dispose()
|
|
621
|
+
this._rowDrag = null
|
|
622
|
+
}
|
|
623
|
+
|
|
508
624
|
if (this._virtualizer) {
|
|
509
625
|
this._virtualizer.destroy()
|
|
510
626
|
this._virtualizer = null
|
|
@@ -581,7 +697,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
581
697
|
return
|
|
582
698
|
}
|
|
583
699
|
|
|
584
|
-
if (
|
|
700
|
+
if (
|
|
701
|
+
!toolbarConfig.columns && !toolbarConfig.export && !toolbarConfig.print &&
|
|
702
|
+
!toolbarConfig.search && !toolbarConfig.history
|
|
703
|
+
) {
|
|
585
704
|
return
|
|
586
705
|
}
|
|
587
706
|
|
|
@@ -591,11 +710,13 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
591
710
|
icons: {
|
|
592
711
|
columns: this._sanitizeIcon(this._config.toolbarColumnsIcon),
|
|
593
712
|
export: this._sanitizeIcon(this._config.toolbarExportIcon),
|
|
713
|
+
print: this._sanitizeIcon(this._config.toolbarPrintIcon),
|
|
594
714
|
redo: this._sanitizeIcon(this._config.toolbarRedoIcon),
|
|
595
715
|
undo: this._sanitizeIcon(this._config.toolbarUndoIcon)
|
|
596
716
|
},
|
|
597
717
|
history: toolbarConfig.history,
|
|
598
718
|
labels: this._labels,
|
|
719
|
+
print: toolbarConfig.print,
|
|
599
720
|
search: toolbarConfig.search
|
|
600
721
|
})
|
|
601
722
|
this._element.append(this._toolbar.element)
|
|
@@ -620,6 +741,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
620
741
|
createActiveCellManager(() => this._scheduler.schedule()) :
|
|
621
742
|
null
|
|
622
743
|
this._element.classList.add(CLASS_NAME_DATA_GRID)
|
|
744
|
+
this._element.classList.toggle(CLASS_NAME_AUTO_ROW_HEIGHT, Boolean(this._config.autoRowHeight))
|
|
623
745
|
this._element.classList.toggle(CLASS_NAME_RESIZABLE, Boolean(this._config.columnSizing))
|
|
624
746
|
this._element.classList.toggle(CLASS_NAME_PINNED, Boolean(this._config.columnPinning))
|
|
625
747
|
if (this._config.sorterVisibility) {
|
|
@@ -637,9 +759,11 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
637
759
|
|
|
638
760
|
const selectionEnabled = Boolean(this._config.rowSelection)
|
|
639
761
|
const selectionOptions = typeof this._config.rowSelection === 'object' ? this._config.rowSelection : {}
|
|
640
|
-
// Server-side mode implies pagination
|
|
641
|
-
//
|
|
642
|
-
const paginationEnabled =
|
|
762
|
+
// Server-side mode implies pagination unless it scrolls infinitely, where
|
|
763
|
+
// the pages accumulate behind the virtualizer instead of replacing.
|
|
764
|
+
const paginationEnabled =
|
|
765
|
+
Boolean(this._config.pagination) ||
|
|
766
|
+
(Boolean(this._config.dataProvider) && !this._config.infiniteScroll)
|
|
643
767
|
const paginationOptions = typeof this._config.pagination === 'object' ? this._config.pagination : {}
|
|
644
768
|
|
|
645
769
|
if (this._config.columnMenu) {
|
|
@@ -654,8 +778,15 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
654
778
|
root: this._element as HTMLElement,
|
|
655
779
|
labels: this._labels,
|
|
656
780
|
getColumn: columnKey => this._columnsByKey.get(columnKey) ?? null,
|
|
657
|
-
getFacetedValues: columnKey =>
|
|
658
|
-
|
|
781
|
+
getFacetedValues: columnKey => {
|
|
782
|
+
const column = this._table.getColumn(columnKey)
|
|
783
|
+
// Only filterType 'select' menus ask for values, and the coverage
|
|
784
|
+
// assertion ties that filter type to the faceting features - this
|
|
785
|
+
// guard covers select columns added later through update().
|
|
786
|
+
return typeof column?.getFacetedUniqueValues === 'function' ?
|
|
787
|
+
[...column.getFacetedUniqueValues().keys()] :
|
|
788
|
+
[]
|
|
789
|
+
},
|
|
659
790
|
getFilterValue: columnKey =>
|
|
660
791
|
this._table.getColumn(columnKey)?.getFilterValue() as DataGridColumnFilterValue | undefined,
|
|
661
792
|
applyFilter: (columnKey, value) => {
|
|
@@ -686,26 +817,22 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
686
817
|
isSortable: column => this._isColumnSortable(column),
|
|
687
818
|
labels: this._labels,
|
|
688
819
|
resizable: Boolean(this._config.columnSizing),
|
|
820
|
+
rowOrder: this._config.rowOrder,
|
|
689
821
|
selection: selectionEnabled,
|
|
690
822
|
selectAll: selectionEnabled && selectionOptions.selectAll !== false,
|
|
691
823
|
table: this._table
|
|
692
824
|
})
|
|
693
825
|
this._body = createBody({
|
|
826
|
+
autoRowHeight: this._config.autoRowHeight && this._config.virtualization,
|
|
694
827
|
cellNavigation: this._config.cellNavigation,
|
|
828
|
+
cellSelection: this._config.cellSelection,
|
|
695
829
|
getDataVersion: () => this._dataVersion,
|
|
696
830
|
getEditingCell: () =>
|
|
697
831
|
(this._editingCell ? { rowId: this._editingCell.rowId, columnId: this._editingCell.columnId } : null),
|
|
698
832
|
// Popup editors overlay the cell; its content stays in place.
|
|
699
833
|
getEditorElement: () => (this._editor && !this._editor.popup ? this._editor.element : null),
|
|
700
834
|
getPinning: () => (this._config.columnPinning ? this._getPinning() : null),
|
|
701
|
-
getRowIndexOffset: () =>
|
|
702
|
-
if (!this._config.pagination && !this._config.dataProvider) {
|
|
703
|
-
return 0
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
const { pageIndex, pageSize } = this._table.getState().pagination
|
|
707
|
-
return pageIndex * pageSize
|
|
708
|
-
},
|
|
835
|
+
getRowIndexOffset: () => this._rowIndexOffset(),
|
|
709
836
|
getRows: () => this._table.getRowModel().rows,
|
|
710
837
|
getVisibleColumns: () => this._visibleColumns(),
|
|
711
838
|
getVirtualizer: () => this._virtualizer?.instance ?? null,
|
|
@@ -722,6 +849,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
722
849
|
|
|
723
850
|
return typeof this._config.empty === 'function' ? this._config.empty() : this._config.empty
|
|
724
851
|
},
|
|
852
|
+
rowHandleIcon: this._sanitizeIcon(this._config.rowHandleIcon),
|
|
853
|
+
rowOrder: this._config.rowOrder,
|
|
725
854
|
sanitizeContent: html =>
|
|
726
855
|
(this._config.sanitize ? sanitizeHtml(html, DefaultAllowlist, this._config.sanitizeFn) : html),
|
|
727
856
|
selection: selectionEnabled
|
|
@@ -750,6 +879,13 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
750
879
|
this._viewportElement.append(this._loading.element)
|
|
751
880
|
}
|
|
752
881
|
|
|
882
|
+
if (this._config.infiniteScroll) {
|
|
883
|
+
// A second indicator: the overlay covers the grid, which is wrong once
|
|
884
|
+
// rows are already on screen and only the next page is arriving.
|
|
885
|
+
this._loadingMore = createLoading(this._labels, CLASS_NAME_LOADING_MORE)
|
|
886
|
+
this._viewportElement.append(this._loadingMore.element)
|
|
887
|
+
}
|
|
888
|
+
|
|
753
889
|
this._element.append(this._viewportElement)
|
|
754
890
|
|
|
755
891
|
if (paginationEnabled) {
|
|
@@ -788,6 +924,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
788
924
|
}
|
|
789
925
|
} else if (this._config.virtualization) {
|
|
790
926
|
this._virtualizer = createGridVirtualizer({
|
|
927
|
+
autoRowHeight: this._config.autoRowHeight,
|
|
791
928
|
count: this._table.getRowModel().rows.length,
|
|
792
929
|
getScrollElement: () => this._viewportElement,
|
|
793
930
|
onChange: () => this._scheduler.schedule(),
|
|
@@ -796,6 +933,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
796
933
|
})
|
|
797
934
|
}
|
|
798
935
|
|
|
936
|
+
this._initColumnDrag()
|
|
937
|
+
this._initRowDrag()
|
|
799
938
|
this._update()
|
|
800
939
|
}
|
|
801
940
|
|
|
@@ -818,6 +957,18 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
818
957
|
}
|
|
819
958
|
})
|
|
820
959
|
|
|
960
|
+
// Drag-select: table-core owns the session (it attaches its own document
|
|
961
|
+
// mouseup), so the grid only routes the two events at the right cell.
|
|
962
|
+
EventHandler.on(this._element, EVENT_MOUSEDOWN, SELECTOR_BODY_CELL, event => {
|
|
963
|
+
const cell = this._tableCellFromTarget(event)
|
|
964
|
+
cell?.getSelectionStartHandler()(event)
|
|
965
|
+
})
|
|
966
|
+
|
|
967
|
+
EventHandler.on(this._element, EVENT_MOUSEENTER, SELECTOR_BODY_CELL, event => {
|
|
968
|
+
const cell = this._tableCellFromTarget(event)
|
|
969
|
+
cell?.getSelectionExtendHandler()(event)
|
|
970
|
+
})
|
|
971
|
+
|
|
821
972
|
EventHandler.on(this._element, EVENT_DBLCLICK, event => {
|
|
822
973
|
if (!this._config.editing) {
|
|
823
974
|
return
|
|
@@ -872,7 +1023,12 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
872
1023
|
})
|
|
873
1024
|
|
|
874
1025
|
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_TOOLBAR_EXPORT, () => {
|
|
875
|
-
|
|
1026
|
+
const { exporter, ...exportOptions } = this._toolbarConfig?.exportOptions ?? {};
|
|
1027
|
+
(exporter ?? downloadCsv)(this._table, { scope: 'filtered', ...exportOptions })
|
|
1028
|
+
})
|
|
1029
|
+
|
|
1030
|
+
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_TOOLBAR_PRINT, () => {
|
|
1031
|
+
this.print()
|
|
876
1032
|
})
|
|
877
1033
|
|
|
878
1034
|
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_TOOLBAR_UNDO, () => {
|
|
@@ -883,42 +1039,6 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
883
1039
|
this.redo()
|
|
884
1040
|
})
|
|
885
1041
|
|
|
886
|
-
EventHandler.on(this._element, EVENT_DRAGSTART, SELECTOR_DRAGGABLE, event => {
|
|
887
|
-
this._onColumnDragStart(event)
|
|
888
|
-
})
|
|
889
|
-
|
|
890
|
-
EventHandler.on(this._element, EVENT_DRAGOVER, SELECTOR_DRAGGABLE, event => {
|
|
891
|
-
if (!this._dragColumn) {
|
|
892
|
-
return
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
const target = (event.delegateTarget ?? event.target) as HTMLElement
|
|
896
|
-
const targetKey = target.dataset.coreuiColumn
|
|
897
|
-
// A drop is only valid within the drag source's pinned region; leaving
|
|
898
|
-
// other targets un-prevented shows the browser's no-drop cursor there.
|
|
899
|
-
const source = this._table.getColumn(this._dragColumn)
|
|
900
|
-
const targetColumn = targetKey ? this._table.getColumn(targetKey) : undefined
|
|
901
|
-
if (!source || !targetColumn || targetKey === this._dragColumn || source.getIsPinned() !== targetColumn.getIsPinned()) {
|
|
902
|
-
return
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
event.preventDefault()
|
|
906
|
-
target.classList.add(CLASS_NAME_DROP_TARGET)
|
|
907
|
-
})
|
|
908
|
-
|
|
909
|
-
EventHandler.on(this._element, EVENT_DRAGLEAVE, SELECTOR_DRAGGABLE, event => {
|
|
910
|
-
const target = (event.delegateTarget ?? event.target) as HTMLElement
|
|
911
|
-
target.classList.remove(CLASS_NAME_DROP_TARGET)
|
|
912
|
-
})
|
|
913
|
-
|
|
914
|
-
EventHandler.on(this._element, EVENT_DROP, SELECTOR_DRAGGABLE, event => {
|
|
915
|
-
this._onColumnDrop(event)
|
|
916
|
-
})
|
|
917
|
-
|
|
918
|
-
EventHandler.on(this._element, EVENT_DRAGEND, SELECTOR_DRAGGABLE, () => {
|
|
919
|
-
this._endColumnDrag()
|
|
920
|
-
})
|
|
921
|
-
|
|
922
1042
|
EventHandler.on(this._element, EVENT_MOUSEDOWN, SELECTOR_RESIZER, event => {
|
|
923
1043
|
this._startColumnResize(event)
|
|
924
1044
|
})
|
|
@@ -1147,6 +1267,22 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1147
1267
|
return
|
|
1148
1268
|
}
|
|
1149
1269
|
|
|
1270
|
+
if (this._config.cellSelection && (event.ctrlKey || event.metaKey) && found.cell.rowIndex >= 0) {
|
|
1271
|
+
const key = event.key.toLowerCase()
|
|
1272
|
+
if (key === 'a') {
|
|
1273
|
+
event.preventDefault()
|
|
1274
|
+
this._table.selectAllCells()
|
|
1275
|
+
this._scheduler.schedule()
|
|
1276
|
+
return
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (key === 'c') {
|
|
1280
|
+
event.preventDefault()
|
|
1281
|
+
this._copyCellSelection()
|
|
1282
|
+
return
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1150
1286
|
const result = resolveNavigation(event.key, event.ctrlKey || event.metaKey, found.cell, this._activeCellContext())
|
|
1151
1287
|
if (!result) {
|
|
1152
1288
|
return
|
|
@@ -1154,6 +1290,17 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1154
1290
|
|
|
1155
1291
|
event.preventDefault()
|
|
1156
1292
|
|
|
1293
|
+
// Shift+arrow grows the range from its anchor and leaves focus put; a
|
|
1294
|
+
// plain arrow moves focus and collapses the range to the new cell.
|
|
1295
|
+
if (this._config.cellSelection && found.cell.rowIndex >= 0 && result.cell.rowIndex >= 0) {
|
|
1296
|
+
const direction = CELL_SELECTION_DIRECTIONS[event.key]
|
|
1297
|
+
if (event.shiftKey && direction) {
|
|
1298
|
+
this._table.extendCellSelection(direction)
|
|
1299
|
+
this._scheduler.schedule()
|
|
1300
|
+
return
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1157
1304
|
if (result.pageDelta === 1 && this._table.getCanNextPage()) {
|
|
1158
1305
|
this._table.nextPage()
|
|
1159
1306
|
} else if (result.pageDelta === -1 && this._table.getCanPreviousPage()) {
|
|
@@ -1163,6 +1310,24 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1163
1310
|
this._setActiveCell(result.cell)
|
|
1164
1311
|
}
|
|
1165
1312
|
|
|
1313
|
+
// The table-core Cell behind a DOM cell, or null when cell selection is off
|
|
1314
|
+
// or the target is not a data cell.
|
|
1315
|
+
private _tableCellFromTarget(event: Event & Record<string, unknown>) {
|
|
1316
|
+
if (!this._config.cellSelection) {
|
|
1317
|
+
return null
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const element = (event.delegateTarget ?? event.target) as HTMLElement
|
|
1321
|
+
const columnId = element.dataset.coreuiColumn
|
|
1322
|
+
const rowIndex = Number(element.closest<HTMLElement>('tr')?.dataset.coreuiIndex ?? -1)
|
|
1323
|
+
if (!columnId || rowIndex < 0) {
|
|
1324
|
+
return null
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
const row = this._table.getRowModel().rows[rowIndex]
|
|
1328
|
+
return row?.getAllCells().find(cell => cell.column.id === columnId) ?? null
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1166
1331
|
private _descendIntoCell(cellElement: HTMLElement): void {
|
|
1167
1332
|
cellElement.querySelector<HTMLElement>(FOCUSABLE_SELECTOR)?.focus({ preventScroll: true })
|
|
1168
1333
|
}
|
|
@@ -1429,6 +1594,15 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1429
1594
|
|
|
1430
1595
|
private _setActiveCell(cell: ActiveCell): void {
|
|
1431
1596
|
this._activeCell?.set(cell, { focus: true })
|
|
1597
|
+
// The active cell is the grid's single focus owner; cell selection follows
|
|
1598
|
+
// it rather than keeping a second anchor of its own.
|
|
1599
|
+
if (this._config.cellSelection && cell.rowIndex >= 0) {
|
|
1600
|
+
const row = this._table.getRowModel().rows[cell.rowIndex]
|
|
1601
|
+
if (row) {
|
|
1602
|
+
this._table.setFocusedCell(row.id, cell.columnId)
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1432
1606
|
if (this._virtualizer && cell.rowIndex >= 0) {
|
|
1433
1607
|
// No-op when the row is already in view (align: auto); otherwise the
|
|
1434
1608
|
// focus request stays pending until the row renders on the next flush.
|
|
@@ -1436,6 +1610,28 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1436
1610
|
}
|
|
1437
1611
|
}
|
|
1438
1612
|
|
|
1613
|
+
// The columns the selection's column indexes address, with the formatter
|
|
1614
|
+
// each cell displays through — a copied date pastes as the date on screen.
|
|
1615
|
+
private _printColumns(): PrintColumn[] {
|
|
1616
|
+
return this._visibleColumns().map(column => ({
|
|
1617
|
+
id: column.key,
|
|
1618
|
+
label: column.label ?? column.key,
|
|
1619
|
+
formatter: column.formatter
|
|
1620
|
+
}))
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
private _clipboardColumns(): ClipboardColumn[] {
|
|
1624
|
+
return this._visibleColumns().map(column => ({ id: column.key, formatter: column.formatter }))
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
private _copyCellSelection(): void {
|
|
1628
|
+
copyCellSelection(this._table, this._clipboardColumns()).then(text => {
|
|
1629
|
+
if (text !== '') {
|
|
1630
|
+
EventHandler.trigger(this._element, EVENT_CELL_COPY, { text })
|
|
1631
|
+
}
|
|
1632
|
+
}, () => {})
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1439
1635
|
private _activeCellContext(): ActiveCellContext {
|
|
1440
1636
|
const rowCount = this._table.getRowModel().rows.length
|
|
1441
1637
|
const paginated = Boolean(this._config.pagination) || Boolean(this._config.dataProvider)
|
|
@@ -1445,7 +1641,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1445
1641
|
columnIds: this._visibleLeafColumns().map(column => column.id),
|
|
1446
1642
|
rowCount,
|
|
1447
1643
|
pageStep: paginated ?
|
|
1448
|
-
this._table.
|
|
1644
|
+
this._table.store.state.pagination.pageSize :
|
|
1449
1645
|
Math.max(1, viewportRows || rowCount),
|
|
1450
1646
|
paginated
|
|
1451
1647
|
}
|
|
@@ -1563,23 +1759,23 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1563
1759
|
|
|
1564
1760
|
if (this._config.columnPinning) {
|
|
1565
1761
|
const pinned = column.getIsPinned()
|
|
1566
|
-
if (pinned !== '
|
|
1762
|
+
if (pinned !== 'start') {
|
|
1567
1763
|
actions.push({
|
|
1568
|
-
key: 'pin-
|
|
1569
|
-
label: this._labels.
|
|
1764
|
+
key: 'pin-start',
|
|
1765
|
+
label: this._labels.pinStart,
|
|
1570
1766
|
group: 'pinning',
|
|
1571
|
-
icon: this._sanitizeIcon(this._config.
|
|
1572
|
-
run: () => column.pin('
|
|
1767
|
+
icon: this._sanitizeIcon(this._config.pinStartIcon),
|
|
1768
|
+
run: () => column.pin('start')
|
|
1573
1769
|
})
|
|
1574
1770
|
}
|
|
1575
1771
|
|
|
1576
|
-
if (pinned !== '
|
|
1772
|
+
if (pinned !== 'end') {
|
|
1577
1773
|
actions.push({
|
|
1578
|
-
key: 'pin-
|
|
1579
|
-
label: this._labels.
|
|
1774
|
+
key: 'pin-end',
|
|
1775
|
+
label: this._labels.pinEnd,
|
|
1580
1776
|
group: 'pinning',
|
|
1581
|
-
icon: this._sanitizeIcon(this._config.
|
|
1582
|
-
run: () => column.pin('
|
|
1777
|
+
icon: this._sanitizeIcon(this._config.pinEndIcon),
|
|
1778
|
+
run: () => column.pin('end')
|
|
1583
1779
|
})
|
|
1584
1780
|
}
|
|
1585
1781
|
|
|
@@ -1595,23 +1791,23 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1595
1791
|
}
|
|
1596
1792
|
|
|
1597
1793
|
if (this._config.columnOrder && config.movable !== false) {
|
|
1598
|
-
const region = this._visibleLeafColumns().filter(item =>
|
|
1794
|
+
const region = this._visibleLeafColumns().filter(item => this._pinnedRegion(item) === this._pinnedRegion(column))
|
|
1599
1795
|
const index = region.findIndex(item => item.id === columnKey)
|
|
1600
1796
|
actions.push(
|
|
1601
1797
|
{
|
|
1602
|
-
key: 'move-
|
|
1603
|
-
label: this._labels.
|
|
1798
|
+
key: 'move-start',
|
|
1799
|
+
label: this._labels.moveStart,
|
|
1604
1800
|
disabled: index <= 0,
|
|
1605
1801
|
group: 'ordering',
|
|
1606
|
-
icon: this._sanitizeIcon(this._config.
|
|
1802
|
+
icon: this._sanitizeIcon(this._config.moveStartIcon),
|
|
1607
1803
|
run: () => this._moveColumn(columnKey, -1)
|
|
1608
1804
|
},
|
|
1609
1805
|
{
|
|
1610
|
-
key: 'move-
|
|
1611
|
-
label: this._labels.
|
|
1806
|
+
key: 'move-end',
|
|
1807
|
+
label: this._labels.moveEnd,
|
|
1612
1808
|
disabled: index === region.length - 1,
|
|
1613
1809
|
group: 'ordering',
|
|
1614
|
-
icon: this._sanitizeIcon(this._config.
|
|
1810
|
+
icon: this._sanitizeIcon(this._config.moveEndIcon),
|
|
1615
1811
|
run: () => this._moveColumn(columnKey, 1)
|
|
1616
1812
|
}
|
|
1617
1813
|
)
|
|
@@ -1644,7 +1840,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1644
1840
|
// missing ids at the end when they are shown again.
|
|
1645
1841
|
private _orderedLeafIds(): string[] {
|
|
1646
1842
|
const allIds = this._table.getAllLeafColumns().map(column => column.id)
|
|
1647
|
-
const order = this._table.
|
|
1843
|
+
const order = this._table.store.state.columnOrder ?? []
|
|
1648
1844
|
if (order.length === 0) {
|
|
1649
1845
|
return allIds
|
|
1650
1846
|
}
|
|
@@ -1661,10 +1857,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1661
1857
|
return
|
|
1662
1858
|
}
|
|
1663
1859
|
|
|
1664
|
-
const region =
|
|
1860
|
+
const region = this._pinnedRegion(column)
|
|
1665
1861
|
const ids = this._orderedLeafIds()
|
|
1666
1862
|
const regionIds = this._visibleLeafColumns()
|
|
1667
|
-
.filter(item =>
|
|
1863
|
+
.filter(item => this._pinnedRegion(item) === region)
|
|
1668
1864
|
.map(item => item.id)
|
|
1669
1865
|
const neighbor = regionIds[regionIds.indexOf(columnKey) + direction]
|
|
1670
1866
|
if (!neighbor) {
|
|
@@ -1678,64 +1874,92 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1678
1874
|
this._table.setColumnOrder(ids)
|
|
1679
1875
|
}
|
|
1680
1876
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
if ((event.target as HTMLElement).closest?.(SELECTOR_RESIZER)) {
|
|
1686
|
-
event.preventDefault()
|
|
1877
|
+
// Pointer-based, not HTML5 drag events: the controller owns the ghost, the
|
|
1878
|
+
// drop indicator and the edge auto-scroll, and behaves the same on touch.
|
|
1879
|
+
private _initColumnDrag(): void {
|
|
1880
|
+
if (!this._config.columnOrder) {
|
|
1687
1881
|
return
|
|
1688
1882
|
}
|
|
1689
1883
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1884
|
+
this._columnDrag = createColumnDragController({
|
|
1885
|
+
root: this._element as HTMLElement,
|
|
1886
|
+
getScrollElement: () => this._viewportElement,
|
|
1887
|
+
canDrop: (sourceKey, targetKey) => {
|
|
1888
|
+
const source = this._table.getColumn(sourceKey)
|
|
1889
|
+
const target = this._table.getColumn(targetKey)
|
|
1890
|
+
return Boolean(source && target) &&
|
|
1891
|
+
sourceKey !== targetKey &&
|
|
1892
|
+
this._pinnedRegion(source!) === this._pinnedRegion(target!)
|
|
1893
|
+
},
|
|
1894
|
+
onReorder: (sourceKey, targetKey, after) => this._moveColumnTo(sourceKey, targetKey, after)
|
|
1895
|
+
})
|
|
1896
|
+
}
|
|
1693
1897
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1898
|
+
// Rows are indexed within the current row model; the offset turns that into
|
|
1899
|
+
// a position in `items`.
|
|
1900
|
+
private _rowIndexOffset(): number {
|
|
1901
|
+
// The infinite buffer always starts at the first row, so its indices are
|
|
1902
|
+
// already absolute.
|
|
1903
|
+
if (this._config.infiniteScroll || (!this._config.pagination && !this._config.dataProvider)) {
|
|
1904
|
+
return 0
|
|
1698
1905
|
}
|
|
1699
|
-
}
|
|
1700
1906
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
const targetKey = target.dataset.coreuiColumn
|
|
1705
|
-
const sourceKey = this._dragColumn
|
|
1706
|
-
this._endColumnDrag()
|
|
1907
|
+
const { pageIndex, pageSize } = this._table.store.state.pagination
|
|
1908
|
+
return pageIndex * pageSize
|
|
1909
|
+
}
|
|
1707
1910
|
|
|
1708
|
-
|
|
1911
|
+
private _initRowDrag(): void {
|
|
1912
|
+
if (!this._config.rowOrder) {
|
|
1709
1913
|
return
|
|
1710
1914
|
}
|
|
1711
1915
|
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1916
|
+
this._rowDrag = createRowDragController({
|
|
1917
|
+
root: this._element as HTMLElement,
|
|
1918
|
+
getScrollElement: () => this._viewportElement,
|
|
1919
|
+
onReorder: (sourceIndex, targetIndex, after) => this._moveRowTo(sourceIndex, targetIndex, after)
|
|
1920
|
+
})
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// The grid never mutates `items` — it hands the reordered array to the app
|
|
1924
|
+
// through rowOrderChange, the same contract inline editing uses.
|
|
1925
|
+
private _moveRowTo(sourceIndex: number, targetIndex: number, after: boolean): void {
|
|
1926
|
+
const offset = this._rowIndexOffset()
|
|
1927
|
+
const items = [...this._config.items]
|
|
1928
|
+
const from = offset + sourceIndex
|
|
1929
|
+
const [item] = items.splice(from, 1)
|
|
1930
|
+
if (item === undefined) {
|
|
1717
1931
|
return
|
|
1718
1932
|
}
|
|
1719
1933
|
|
|
1934
|
+
// Resolved AFTER the removal, so the index still points at the target
|
|
1935
|
+
// whichever direction the row travelled.
|
|
1936
|
+
const target = offset + targetIndex
|
|
1937
|
+
const to = target > from ? target - 1 : target
|
|
1938
|
+
const newIndex = after ? to + 1 : to
|
|
1939
|
+
items.splice(newIndex, 0, item)
|
|
1940
|
+
|
|
1941
|
+
EventHandler.trigger(this._element, EVENT_ROW_ORDER_CHANGE, {
|
|
1942
|
+
items, item, oldIndex: from, newIndex
|
|
1943
|
+
})
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
private _moveColumnTo(sourceKey: string, targetKey: string, after: boolean): void {
|
|
1720
1947
|
const ids = this._orderedLeafIds()
|
|
1721
1948
|
const from = ids.indexOf(sourceKey)
|
|
1722
|
-
|
|
1723
|
-
if (from === -1 || to === -1) {
|
|
1949
|
+
if (from === -1) {
|
|
1724
1950
|
return
|
|
1725
1951
|
}
|
|
1726
1952
|
|
|
1727
|
-
// Removing the source first makes inserting at the target's original index
|
|
1728
|
-
// land after it when dragging right and before it when dragging left.
|
|
1729
1953
|
ids.splice(from, 1)
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
this._dragColumn = null
|
|
1736
|
-
for (const cell of this._element.querySelectorAll(`.${CLASS_NAME_DRAGGING}, .${CLASS_NAME_DROP_TARGET}`)) {
|
|
1737
|
-
cell.classList.remove(CLASS_NAME_DRAGGING, CLASS_NAME_DROP_TARGET)
|
|
1954
|
+
// Resolved AFTER the removal, so the index still points at the target
|
|
1955
|
+
// whichever direction the column travelled.
|
|
1956
|
+
const to = ids.indexOf(targetKey)
|
|
1957
|
+
if (to === -1) {
|
|
1958
|
+
return
|
|
1738
1959
|
}
|
|
1960
|
+
|
|
1961
|
+
ids.splice(after ? to + 1 : to, 0, sourceKey)
|
|
1962
|
+
this._table.setColumnOrder(ids)
|
|
1739
1963
|
}
|
|
1740
1964
|
|
|
1741
1965
|
private _startColumnResize(event: Event & Record<string, unknown>): void {
|
|
@@ -1797,7 +2021,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1797
2021
|
this._dataSource = createDataSource({
|
|
1798
2022
|
provider: request => dataProvider(request),
|
|
1799
2023
|
getRequest: () => {
|
|
1800
|
-
const state = this._table.
|
|
2024
|
+
const { state } = this._table.store
|
|
1801
2025
|
return {
|
|
1802
2026
|
columnFilters: state.columnFilters,
|
|
1803
2027
|
globalFilter: (state.globalFilter as string | undefined) ?? '',
|
|
@@ -1805,13 +2029,29 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1805
2029
|
sorting: state.sorting
|
|
1806
2030
|
}
|
|
1807
2031
|
},
|
|
1808
|
-
onLoad: response => {
|
|
2032
|
+
onLoad: (response, request) => {
|
|
1809
2033
|
this._stopEditing({ commit: false })
|
|
1810
2034
|
this._loadFailed = false
|
|
2035
|
+
this._loadMorePending = false
|
|
1811
2036
|
this._dataVersion++
|
|
2037
|
+
|
|
2038
|
+
let { items } = response
|
|
2039
|
+
if (this._config.infiniteScroll) {
|
|
2040
|
+
const firstPage = request.pagination.pageIndex === 0
|
|
2041
|
+
items = firstPage ? response.items : [...this._loadedItems, ...response.items]
|
|
2042
|
+
this._loadedItems = items
|
|
2043
|
+
// Appended rows land at fresh indices; a first page puts different
|
|
2044
|
+
// data at indices the virtualizer has already measured — and shrinks
|
|
2045
|
+
// the buffer back under a viewport that may be scrolled far past it.
|
|
2046
|
+
if (firstPage) {
|
|
2047
|
+
this._viewportElement.scrollTop = 0
|
|
2048
|
+
this._virtualizer?.remeasure()
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
|
|
1812
2052
|
this._table.setOptions(previous => ({
|
|
1813
2053
|
...previous,
|
|
1814
|
-
data:
|
|
2054
|
+
data: items,
|
|
1815
2055
|
rowCount: response.totalRows
|
|
1816
2056
|
}))
|
|
1817
2057
|
EventHandler.trigger(this._element, EVENT_DATA_LOAD, {
|
|
@@ -1824,16 +2064,17 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1824
2064
|
onError: error => {
|
|
1825
2065
|
this._stopEditing({ commit: false })
|
|
1826
2066
|
this._loadFailed = true
|
|
2067
|
+
this._loadMorePending = false
|
|
2068
|
+
this._loadedItems = []
|
|
1827
2069
|
this._dataVersion++
|
|
1828
2070
|
this._table.setOptions(previous => ({ ...previous, data: [], rowCount: 0 }))
|
|
1829
2071
|
EventHandler.trigger(this._element, EVENT_DATA_ERROR, { error })
|
|
1830
2072
|
this._scheduler.schedule()
|
|
1831
2073
|
},
|
|
1832
2074
|
onLoadingChange: loading => {
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2075
|
+
const appending = Boolean(this._config.infiniteScroll) && this._loadedItems.length > 0
|
|
2076
|
+
this._loading?.setVisible(loading && !appending)
|
|
2077
|
+
this._loadingMore?.setVisible(loading && appending)
|
|
1837
2078
|
this._viewportElement.setAttribute('aria-busy', String(loading))
|
|
1838
2079
|
}
|
|
1839
2080
|
})
|
|
@@ -1841,14 +2082,55 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1841
2082
|
this._dataSource.load()
|
|
1842
2083
|
}
|
|
1843
2084
|
|
|
2085
|
+
// The trigger is the last RENDERED row index, not a scroll ratio: measured
|
|
2086
|
+
// row heights make the ratio meaningless (the total size keeps moving as
|
|
2087
|
+
// rows are measured, so a ratio either fires early or never).
|
|
2088
|
+
private _maybeLoadMore(): void {
|
|
2089
|
+
if (!this._config.infiniteScroll || !this._dataSource || !this._virtualizer || this._loadMorePending) {
|
|
2090
|
+
return
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
const loaded = this._table.getRowModel().rows.length
|
|
2094
|
+
if (loaded === 0 || loaded >= this._getTotalRows()) {
|
|
2095
|
+
return
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
const options = typeof this._config.infiniteScroll === 'object' ? this._config.infiniteScroll : {}
|
|
2099
|
+
const threshold = options.threshold ?? this._config.overscan
|
|
2100
|
+
const rendered = this._virtualizer.instance.getVirtualItems()
|
|
2101
|
+
const last = rendered[rendered.length - 1]
|
|
2102
|
+
if (!last || last.index < loaded - 1 - threshold) {
|
|
2103
|
+
return
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
this._loadMorePending = true
|
|
2107
|
+
this._table.setPageIndex(this._table.store.state.pagination.pageIndex + 1)
|
|
2108
|
+
}
|
|
2109
|
+
|
|
1844
2110
|
// Visible leaf columns in VISUAL order: left-pinned, center (columnOrder),
|
|
1845
2111
|
// right-pinned — getVisibleLeafColumns() alone does not regroup pinning.
|
|
2112
|
+
// Without the pinning feature there are no regions to regroup, and without
|
|
2113
|
+
// visibility every leaf column is visible.
|
|
1846
2114
|
private _visibleLeafColumns() {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
2115
|
+
if (typeof this._table.getCenterVisibleLeafColumns === 'function') {
|
|
2116
|
+
return [
|
|
2117
|
+
...this._table.getStartVisibleLeafColumns(),
|
|
2118
|
+
...this._table.getCenterVisibleLeafColumns(),
|
|
2119
|
+
...this._table.getEndVisibleLeafColumns()
|
|
2120
|
+
]
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
if (typeof this._table.getVisibleLeafColumns === 'function') {
|
|
2124
|
+
return this._table.getVisibleLeafColumns()
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
return this._table.getAllLeafColumns()
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// Ordering paths compare pinned regions even on grids that never enabled
|
|
2131
|
+
// pinning - without the feature every column sits in the unpinned region.
|
|
2132
|
+
private _pinnedRegion(column: { getIsPinned?: () => 'start' | 'end' | false }): 'start' | 'end' | false {
|
|
2133
|
+
return typeof column.getIsPinned === 'function' ? column.getIsPinned() : false
|
|
1852
2134
|
}
|
|
1853
2135
|
|
|
1854
2136
|
private _chooserColumns(): ColumnChooserColumn[] {
|
|
@@ -1893,14 +2175,14 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1893
2175
|
}
|
|
1894
2176
|
|
|
1895
2177
|
private _getFilteredColumns(): string[] {
|
|
1896
|
-
return this._table.
|
|
2178
|
+
return this._table.store.state.columnFilters.map(filter => filter.id)
|
|
1897
2179
|
}
|
|
1898
2180
|
|
|
1899
2181
|
private _getPinning(): GridPinning {
|
|
1900
|
-
// Visible columns only -
|
|
2182
|
+
// Visible columns only - getStartLeafColumns/getPinnedIndex include hidden
|
|
1901
2183
|
// columns, which would leave the edge class on a column nobody sees.
|
|
1902
|
-
const leftColumns = this._table.
|
|
1903
|
-
const rightColumns = this._table.
|
|
2184
|
+
const leftColumns = this._table.getStartLeafColumns().filter(column => column.getIsVisible())
|
|
2185
|
+
const rightColumns = this._table.getEndLeafColumns().filter(column => column.getIsVisible())
|
|
1904
2186
|
// The selection cell is not a TanStack column; anchor it to the left as
|
|
1905
2187
|
// soon as any data column is pinned there so the checkbox travels with them.
|
|
1906
2188
|
const selectPinned = Boolean(this._config.rowSelection) && leftColumns.length > 0
|
|
@@ -1908,8 +2190,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1908
2190
|
|
|
1909
2191
|
for (const [index, column] of leftColumns.entries()) {
|
|
1910
2192
|
columns[column.id] = {
|
|
1911
|
-
side: '
|
|
1912
|
-
offset: column.getStart('
|
|
2193
|
+
side: 'start',
|
|
2194
|
+
offset: column.getStart('start'),
|
|
1913
2195
|
edge: index === leftColumns.length - 1,
|
|
1914
2196
|
afterSelect: selectPinned
|
|
1915
2197
|
}
|
|
@@ -1917,8 +2199,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1917
2199
|
|
|
1918
2200
|
for (const [index, column] of rightColumns.entries()) {
|
|
1919
2201
|
columns[column.id] = {
|
|
1920
|
-
side: '
|
|
1921
|
-
offset: column.getAfter('
|
|
2202
|
+
side: 'end',
|
|
2203
|
+
offset: column.getAfter('end'),
|
|
1922
2204
|
edge: index === 0,
|
|
1923
2205
|
afterSelect: false
|
|
1924
2206
|
}
|
|
@@ -1928,7 +2210,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1928
2210
|
columns,
|
|
1929
2211
|
select: selectPinned ?
|
|
1930
2212
|
{
|
|
1931
|
-
side: '
|
|
2213
|
+
side: 'start', offset: 0, edge: false, afterSelect: false
|
|
1932
2214
|
} :
|
|
1933
2215
|
null
|
|
1934
2216
|
}
|
|
@@ -1986,10 +2268,14 @@ null
|
|
|
1986
2268
|
return this._table.options.rowCount ?? 0
|
|
1987
2269
|
}
|
|
1988
2270
|
|
|
2271
|
+
if (typeof this._table.getFilteredRowModel !== 'function') {
|
|
2272
|
+
return this._table.getCoreRowModel().rows.length
|
|
2273
|
+
}
|
|
2274
|
+
|
|
1989
2275
|
return this._table.getFilteredRowModel().rows.length
|
|
1990
2276
|
}
|
|
1991
2277
|
|
|
1992
|
-
private _notifyStateChange(previous:
|
|
2278
|
+
private _notifyStateChange(previous: DataGridTableState, next: DataGridTableState): void {
|
|
1993
2279
|
// TanStack's resize handler keeps document-level listeners until mouseup;
|
|
1994
2280
|
// a dispose mid-drag would re-enter here against a torn-down instance.
|
|
1995
2281
|
if (!this._element) {
|
|
@@ -2025,16 +2311,22 @@ null
|
|
|
2025
2311
|
// auto-reset the page index, which replaces the state object without
|
|
2026
2312
|
// changing it - identity alone would kill an edit right after its
|
|
2027
2313
|
// editCommit handler applied the change.
|
|
2314
|
+
// Infinite scroll is the exception: its page bumps only append, so every
|
|
2315
|
+
// row already on screen keeps its index, its measurement and its focus.
|
|
2028
2316
|
if (
|
|
2029
2317
|
previous.sorting !== next.sorting ||
|
|
2030
2318
|
previous.columnFilters !== next.columnFilters ||
|
|
2031
2319
|
previous.globalFilter !== next.globalFilter ||
|
|
2032
|
-
|
|
2033
|
-
|
|
2320
|
+
(!this._config.infiniteScroll &&
|
|
2321
|
+
(previous.pagination?.pageIndex !== next.pagination?.pageIndex ||
|
|
2322
|
+
previous.pagination?.pageSize !== next.pagination?.pageSize))
|
|
2034
2323
|
) {
|
|
2035
2324
|
this._stopEditing({ commit: true, force: true })
|
|
2036
2325
|
this._selectionAnchorIndex = null
|
|
2037
2326
|
this._activeCell?.reset()
|
|
2327
|
+
// Row measurements are cached by index, so the same index now holding a
|
|
2328
|
+
// different row makes them stale.
|
|
2329
|
+
this._virtualizer?.remeasure()
|
|
2038
2330
|
}
|
|
2039
2331
|
|
|
2040
2332
|
if (previous.sorting !== next.sorting) {
|
|
@@ -2064,7 +2356,9 @@ null
|
|
|
2064
2356
|
})
|
|
2065
2357
|
}
|
|
2066
2358
|
|
|
2067
|
-
|
|
2359
|
+
// No paginationChange under infinite scroll — pagination is internal
|
|
2360
|
+
// there (no widget, not user-adjustable); dataLoad is the page signal.
|
|
2361
|
+
if (previous.pagination !== next.pagination && !this._config.infiniteScroll) {
|
|
2068
2362
|
EventHandler.trigger(this._element, EVENT_PAGINATION_CHANGE, {
|
|
2069
2363
|
pagination: next.pagination
|
|
2070
2364
|
})
|
|
@@ -2097,15 +2391,26 @@ null
|
|
|
2097
2391
|
if (this._dataSource) {
|
|
2098
2392
|
const refetchNeeded = filtersChanged || previous.sorting !== next.sorting
|
|
2099
2393
|
|
|
2100
|
-
if (refetchNeeded
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2394
|
+
if (refetchNeeded) {
|
|
2395
|
+
if (next.pagination.pageIndex !== 0) {
|
|
2396
|
+
// Back to page one. The write lands synchronously, so the request the
|
|
2397
|
+
// debounced fetch reads already carries index 0 - but it also echoes
|
|
2398
|
+
// back here as a pagination change, which must not fetch again.
|
|
2399
|
+
this._pageResetPending = true
|
|
2400
|
+
this._table.setPageIndex(0)
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
this._dataSource.load()
|
|
2104
2404
|
return
|
|
2105
2405
|
}
|
|
2106
2406
|
|
|
2107
|
-
if (
|
|
2108
|
-
this.
|
|
2407
|
+
if (previous.pagination !== next.pagination) {
|
|
2408
|
+
const echoedPageReset = this._pageResetPending && next.pagination.pageIndex === 0
|
|
2409
|
+
this._pageResetPending = false
|
|
2410
|
+
|
|
2411
|
+
if (!echoedPageReset) {
|
|
2412
|
+
this._dataSource.load()
|
|
2413
|
+
}
|
|
2109
2414
|
}
|
|
2110
2415
|
}
|
|
2111
2416
|
}
|
|
@@ -2136,14 +2441,16 @@ null
|
|
|
2136
2441
|
order: this._visibleLeafColumns().map(column => column.id),
|
|
2137
2442
|
pinning: this._config.columnPinning ? this._getPinning() : null,
|
|
2138
2443
|
resizingColumn: this._config.columnSizing ?
|
|
2139
|
-
this._table.
|
|
2444
|
+
this._table.store.state.columnResizing.isResizingColumn :
|
|
2140
2445
|
false,
|
|
2141
2446
|
sizes: (this._config.columnSizing || this._config.columnPinning) ? this._getColumnSizes() : null,
|
|
2142
|
-
sorting: this._table.
|
|
2447
|
+
sorting: this._table.store.state.sorting ?? [],
|
|
2143
2448
|
selectAll: this._config.rowSelection ?
|
|
2144
2449
|
{
|
|
2145
2450
|
checked: this._table.getIsAllRowsSelected(),
|
|
2146
|
-
|
|
2451
|
+
// getIsSomeRowsSelected() means "at least one" - the all-selected
|
|
2452
|
+
// case has to be excluded for the tri-state checkbox.
|
|
2453
|
+
indeterminate: this._table.getIsSomeRowsSelected() && !this._table.getIsAllRowsSelected()
|
|
2147
2454
|
} :
|
|
2148
2455
|
null
|
|
2149
2456
|
})
|
|
@@ -2169,10 +2476,12 @@ null
|
|
|
2169
2476
|
for (const instance of this._paginations) {
|
|
2170
2477
|
instance.refresh()
|
|
2171
2478
|
}
|
|
2479
|
+
|
|
2480
|
+
this._maybeLoadMore()
|
|
2172
2481
|
}
|
|
2173
2482
|
|
|
2174
2483
|
private _paginationState() {
|
|
2175
|
-
const { pagination } = this._table.
|
|
2484
|
+
const { pagination } = this._table.store.state
|
|
2176
2485
|
return {
|
|
2177
2486
|
pageCount: this._table.getPageCount(),
|
|
2178
2487
|
pageIndex: pagination.pageIndex,
|
|
@@ -2203,6 +2512,4 @@ null
|
|
|
2203
2512
|
}
|
|
2204
2513
|
}
|
|
2205
2514
|
|
|
2206
|
-
defineJQueryPlugin(DataGrid)
|
|
2207
|
-
|
|
2208
2515
|
export default DataGrid
|