@coreui/data-grid 0.1.0-rc.0 → 0.3.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 +9 -6
- package/dist/css/data-grid.css +156 -38
- 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/css/data-grid.standalone.css +1066 -0
- package/dist/css/data-grid.standalone.css.map +1 -0
- package/dist/css/data-grid.standalone.min.css +5 -0
- package/dist/css/data-grid.standalone.min.css.map +1 -0
- 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 +14108 -9331
- 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 +14408 -9634
- package/dist/js/data-grid.js.map +1 -1
- package/dist/js/data-grid.lite.esm.js +10507 -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 +10520 -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 +10507 -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 +14108 -9331
- package/dist/js/data-grid.mjs.map +1 -1
- package/dist/js/direction.cjs +26 -0
- package/dist/js/direction.cjs.map +1 -0
- package/dist/js/direction.mjs +24 -0
- package/dist/js/direction.mjs.map +1 -0
- package/dist/js/drag.cjs +467 -0
- package/dist/js/drag.cjs.map +1 -0
- package/dist/js/drag.mjs +464 -0
- package/dist/js/drag.mjs.map +1 -0
- package/dist/js/export-table-BdabEbJG.mjs +26 -0
- package/dist/js/export-table-BdabEbJG.mjs.map +1 -0
- package/dist/js/export-table-D762wrZ1.cjs +55 -0
- package/dist/js/export-table-D762wrZ1.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/active-cell.d.ts +1 -0
- 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/direction.d.mts +1 -0
- package/dist/types/util/direction.d.ts +17 -0
- 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 +101 -10
- package/scss/_base.scss +13 -0
- package/scss/_config.scss +14 -0
- package/scss/_functions.scss +1 -0
- package/scss/_mixins.scss +1 -0
- package/scss/base/_buttons.scss +113 -0
- package/scss/base/_forms.scss +157 -0
- package/scss/base/_helpers.scss +24 -0
- package/scss/base/_pagination.scss +100 -0
- package/scss/base/_reboot.scss +93 -0
- package/scss/base/_root.scss +85 -0
- package/scss/base/_spinner.scss +45 -0
- package/scss/base/_table.scss +78 -0
- package/scss/components/_data-grid.scss +807 -0
- package/scss/data-grid.scss +7 -555
- package/scss/data-grid.standalone.scss +16 -0
- package/scss/functions/_defaults.scss +25 -0
- package/scss/mixins/_tokens.scss +12 -0
- package/src/{base-component.js → base-component.ts} +21 -17
- package/src/data-grid.ts +523 -201
- 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/active-cell.ts +10 -3
- 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 +86 -45
- 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/direction.ts +37 -0
- package/src/util/drag.ts +832 -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,35 @@
|
|
|
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'
|
|
23
|
+
import { isElementRTL, syncResizeDirection } from './util/direction'
|
|
17
24
|
import { createGridVirtualizer, type GridVirtualizer } from './engine/virtualizer'
|
|
18
25
|
import {
|
|
19
|
-
DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON,
|
|
20
|
-
|
|
26
|
+
DEFAULT_COLUMN_MENU_ICON, DEFAULT_FILTER_ICON, DEFAULT_HIDE_COLUMN_ICON, DEFAULT_MOVE_END_ICON, DEFAULT_MOVE_START_ICON,
|
|
27
|
+
DEFAULT_PIN_END_ICON, DEFAULT_PIN_START_ICON, DEFAULT_ROW_HANDLE_ICON, DEFAULT_SORT_ASCENDING_ICON,
|
|
28
|
+
DEFAULT_SORT_DESCENDING_ICON,
|
|
21
29
|
DEFAULT_SORT_NEUTRAL_ICON, DEFAULT_TOOLBAR_COLUMNS_ICON, DEFAULT_TOOLBAR_EXPORT_ICON,
|
|
22
|
-
DEFAULT_TOOLBAR_REDO_ICON, DEFAULT_TOOLBAR_UNDO_ICON, DEFAULT_UNPIN_ICON
|
|
30
|
+
DEFAULT_TOOLBAR_PRINT_ICON, DEFAULT_TOOLBAR_REDO_ICON, DEFAULT_TOOLBAR_UNDO_ICON, DEFAULT_UNPIN_ICON
|
|
23
31
|
} from './icons'
|
|
24
32
|
import { DEFAULT_LABELS, interpolate } from './labels'
|
|
25
33
|
import type {
|
|
26
34
|
DataGridColumn, DataGridColumnFilterValue, DataGridEditableOptions, DataGridEditor, DataGridEditorContext,
|
|
27
|
-
DataGridItem, DataGridLabels, DataGridMenuAction, DataGridOptions, DataGridSlot,
|
|
35
|
+
DataGridFeatures, DataGridItem, DataGridLabels, DataGridMenuAction, DataGridOptions, DataGridSlot,
|
|
36
|
+
DataGridSlotContext, DataGridState, DataGridTable, DataGridTableState, TableFeatures
|
|
28
37
|
} from './types'
|
|
29
38
|
import { createBody, type GridBody } from './ui/body'
|
|
30
39
|
import { createBuiltInEditor } from './ui/editor'
|
|
@@ -41,7 +50,6 @@ import {
|
|
|
41
50
|
downloadCsv, exportCsv, type CsvDownloadOptions, type CsvOptions
|
|
42
51
|
} from './util/csv'
|
|
43
52
|
import { debounce } from './util/debounce'
|
|
44
|
-
import { defineJQueryPlugin } from './util/index.js'
|
|
45
53
|
import { DefaultAllowlist, sanitizeHtml, SVGAllowlist } from './util/sanitizer.js'
|
|
46
54
|
|
|
47
55
|
/**
|
|
@@ -62,7 +70,9 @@ const EVENT_EDIT_COMMIT = `editCommit${EVENT_KEY}`
|
|
|
62
70
|
const EVENT_EDIT_START = `editStart${EVENT_KEY}`
|
|
63
71
|
const EVENT_FILTER_CHANGE = `filterChange${EVENT_KEY}`
|
|
64
72
|
const EVENT_INPUT = `input${EVENT_KEY}`
|
|
73
|
+
const EVENT_CELL_COPY = `cellCopy${EVENT_KEY}`
|
|
65
74
|
const EVENT_ORDER_CHANGE = `orderChange${EVENT_KEY}`
|
|
75
|
+
const EVENT_ROW_ORDER_CHANGE = `rowOrderChange${EVENT_KEY}`
|
|
66
76
|
const EVENT_PAGINATION_CHANGE = `paginationChange${EVENT_KEY}`
|
|
67
77
|
const EVENT_PINNING_CHANGE = `pinningChange${EVENT_KEY}`
|
|
68
78
|
const EVENT_SELECTION_CHANGE = `selectionChange${EVENT_KEY}`
|
|
@@ -74,27 +84,23 @@ const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
|
|
|
74
84
|
const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`
|
|
75
85
|
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
|
|
76
86
|
|
|
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
87
|
const EVENT_MOUSEDOWN = `mousedown${EVENT_KEY}`
|
|
88
|
+
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
|
|
83
89
|
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
|
|
84
90
|
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
|
|
85
91
|
|
|
92
|
+
const CLASS_NAME_AUTO_ROW_HEIGHT = 'data-grid-auto-row-height'
|
|
86
93
|
const CLASS_NAME_DATA_GRID = 'data-grid'
|
|
87
|
-
const CLASS_NAME_DRAGGING = 'data-grid-dragging'
|
|
88
94
|
const CLASS_NAME_EDITOR_POPUP = 'data-grid-editor-popup'
|
|
89
|
-
const
|
|
95
|
+
const CLASS_NAME_LOADING_MORE = 'data-grid-loading-more'
|
|
90
96
|
const CLASS_NAME_PAGINATION_TOP = 'data-grid-pagination-top'
|
|
91
97
|
const CLASS_NAME_PINNED = 'data-grid-pinned'
|
|
92
98
|
const CLASS_NAME_RESIZABLE = 'data-grid-resizable'
|
|
93
99
|
const CLASS_NAME_TABLE = 'table'
|
|
94
100
|
const CLASS_NAME_VIEWPORT = 'data-grid-viewport'
|
|
95
101
|
|
|
102
|
+
const SELECTOR_BODY_CELL = 'tbody td[data-coreui-column]'
|
|
96
103
|
const SELECTOR_COLUMN_FILTER = 'input[data-coreui-column-filter]'
|
|
97
|
-
const SELECTOR_DRAGGABLE = 'th[data-coreui-draggable]'
|
|
98
104
|
const SELECTOR_MENU_BUTTON = 'button[data-coreui-menu]'
|
|
99
105
|
const SELECTOR_FILTER_BUTTON = 'button[data-coreui-filter]'
|
|
100
106
|
const SELECTOR_GLOBAL_FILTER = 'input[data-coreui-global-filter]'
|
|
@@ -106,6 +112,7 @@ const SELECTOR_SELECT_ALL = 'input[data-coreui-select-all]'
|
|
|
106
112
|
const SELECTOR_SORT_BUTTON = '.data-grid-sort-button'
|
|
107
113
|
const SELECTOR_TOOLBAR_COLUMNS = 'button[data-coreui-toolbar-columns]'
|
|
108
114
|
const SELECTOR_TOOLBAR_EXPORT = 'button[data-coreui-toolbar-export]'
|
|
115
|
+
const SELECTOR_TOOLBAR_PRINT = 'button[data-coreui-toolbar-print]'
|
|
109
116
|
const SELECTOR_TOOLBAR_REDO = 'button[data-coreui-toolbar-redo]'
|
|
110
117
|
const SELECTOR_TOOLBAR_UNDO = 'button[data-coreui-toolbar-undo]'
|
|
111
118
|
|
|
@@ -128,6 +135,19 @@ const STATE_STORAGE_PREFIX = 'coreui-data-grid:'
|
|
|
128
135
|
const STATE_AUTOSAVE_DEBOUNCE_MS = 250
|
|
129
136
|
const HISTORY_LIMIT = 100
|
|
130
137
|
|
|
138
|
+
// Under RTL the two horizontal keys point at the opposite logical neighbour.
|
|
139
|
+
const INVERTED_ARROWS: Record<string, string | undefined> = {
|
|
140
|
+
ArrowLeft: 'ArrowRight',
|
|
141
|
+
ArrowRight: 'ArrowLeft'
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const CELL_SELECTION_DIRECTIONS: Record<string, 'up' | 'down' | 'left' | 'right' | undefined> = {
|
|
145
|
+
ArrowDown: 'down',
|
|
146
|
+
ArrowLeft: 'left',
|
|
147
|
+
ArrowRight: 'right',
|
|
148
|
+
ArrowUp: 'up'
|
|
149
|
+
}
|
|
150
|
+
|
|
131
151
|
const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]'
|
|
132
152
|
|
|
133
153
|
const applyTabIndex = (cellElement: HTMLElement, isTabbable: boolean) => {
|
|
@@ -138,7 +158,9 @@ const applyTabIndex = (cellElement: HTMLElement, isTabbable: boolean) => {
|
|
|
138
158
|
}
|
|
139
159
|
|
|
140
160
|
const Default: DataGridOptions = {
|
|
161
|
+
autoRowHeight: false,
|
|
141
162
|
cellNavigation: false,
|
|
163
|
+
cellSelection: false,
|
|
142
164
|
columnFilters: false,
|
|
143
165
|
columnMenu: false,
|
|
144
166
|
columnMenuIcon: DEFAULT_COLUMN_MENU_ICON,
|
|
@@ -150,20 +172,25 @@ const Default: DataGridOptions = {
|
|
|
150
172
|
dataProvider: null,
|
|
151
173
|
editing: false,
|
|
152
174
|
empty: 'No items found',
|
|
175
|
+
features: null,
|
|
153
176
|
filterIcon: DEFAULT_FILTER_ICON,
|
|
154
177
|
globalFilter: false,
|
|
155
178
|
hideColumnIcon: DEFAULT_HIDE_COLUMN_ICON,
|
|
156
179
|
history: false,
|
|
180
|
+
infiniteScroll: false,
|
|
157
181
|
itemKey: null,
|
|
158
182
|
items: [],
|
|
159
183
|
labels: {},
|
|
160
|
-
|
|
161
|
-
|
|
184
|
+
moveEndIcon: DEFAULT_MOVE_END_ICON,
|
|
185
|
+
moveStartIcon: DEFAULT_MOVE_START_ICON,
|
|
162
186
|
overscan: 10,
|
|
163
187
|
pagination: false,
|
|
164
|
-
|
|
165
|
-
|
|
188
|
+
pinEndIcon: DEFAULT_PIN_END_ICON,
|
|
189
|
+
pinStartIcon: DEFAULT_PIN_START_ICON,
|
|
190
|
+
printTitle: null,
|
|
191
|
+
rowHandleIcon: DEFAULT_ROW_HANDLE_ICON,
|
|
166
192
|
rowHeight: 44,
|
|
193
|
+
rowOrder: false,
|
|
167
194
|
rowSelection: false,
|
|
168
195
|
sanitize: true,
|
|
169
196
|
sanitizeFn: null,
|
|
@@ -177,6 +204,7 @@ const Default: DataGridOptions = {
|
|
|
177
204
|
toolbar: false,
|
|
178
205
|
toolbarColumnsIcon: DEFAULT_TOOLBAR_COLUMNS_ICON,
|
|
179
206
|
toolbarExportIcon: DEFAULT_TOOLBAR_EXPORT_ICON,
|
|
207
|
+
toolbarPrintIcon: DEFAULT_TOOLBAR_PRINT_ICON,
|
|
180
208
|
toolbarRedoIcon: DEFAULT_TOOLBAR_REDO_ICON,
|
|
181
209
|
toolbarUndoIcon: DEFAULT_TOOLBAR_UNDO_ICON,
|
|
182
210
|
unpinIcon: DEFAULT_UNPIN_ICON,
|
|
@@ -184,7 +212,9 @@ const Default: DataGridOptions = {
|
|
|
184
212
|
}
|
|
185
213
|
|
|
186
214
|
const DefaultType = {
|
|
215
|
+
autoRowHeight: 'boolean',
|
|
187
216
|
cellNavigation: 'boolean',
|
|
217
|
+
cellSelection: 'boolean',
|
|
188
218
|
columnFilters: 'boolean',
|
|
189
219
|
columnMenu: '(boolean|function)',
|
|
190
220
|
columnMenuIcon: 'string',
|
|
@@ -196,20 +226,25 @@ const DefaultType = {
|
|
|
196
226
|
dataProvider: '(function|null)',
|
|
197
227
|
editing: 'boolean',
|
|
198
228
|
empty: '(string|function)',
|
|
229
|
+
features: '(object|null)',
|
|
199
230
|
filterIcon: 'string',
|
|
200
231
|
globalFilter: 'boolean',
|
|
201
232
|
hideColumnIcon: 'string',
|
|
202
233
|
history: 'boolean',
|
|
234
|
+
infiniteScroll: '(boolean|object)',
|
|
203
235
|
itemKey: '(function|null)',
|
|
204
236
|
items: 'array',
|
|
205
237
|
labels: 'object',
|
|
206
|
-
|
|
207
|
-
|
|
238
|
+
moveEndIcon: 'string',
|
|
239
|
+
moveStartIcon: 'string',
|
|
208
240
|
overscan: 'number',
|
|
209
241
|
pagination: '(boolean|object)',
|
|
210
|
-
|
|
211
|
-
|
|
242
|
+
pinEndIcon: 'string',
|
|
243
|
+
pinStartIcon: 'string',
|
|
244
|
+
printTitle: '(string|null)',
|
|
245
|
+
rowHandleIcon: 'string',
|
|
212
246
|
rowHeight: 'number',
|
|
247
|
+
rowOrder: 'boolean',
|
|
213
248
|
rowSelection: '(boolean|object)',
|
|
214
249
|
sanitize: 'boolean',
|
|
215
250
|
sanitizeFn: '(function|null)',
|
|
@@ -223,6 +258,7 @@ const DefaultType = {
|
|
|
223
258
|
toolbar: '(boolean|object)',
|
|
224
259
|
toolbarColumnsIcon: 'string',
|
|
225
260
|
toolbarExportIcon: 'string',
|
|
261
|
+
toolbarPrintIcon: 'string',
|
|
226
262
|
toolbarRedoIcon: 'string',
|
|
227
263
|
toolbarUndoIcon: 'string',
|
|
228
264
|
unpinIcon: 'string',
|
|
@@ -233,9 +269,13 @@ const DefaultType = {
|
|
|
233
269
|
* Class definition
|
|
234
270
|
*/
|
|
235
271
|
|
|
236
|
-
class DataGrid extends BaseComponent {
|
|
272
|
+
class DataGrid<TFeatures extends TableFeatures = DataGridFeatures> extends BaseComponent {
|
|
237
273
|
declare _config: DataGridOptions
|
|
238
|
-
|
|
274
|
+
// Typed on the full set even when the runtime set is smaller - internals
|
|
275
|
+
// capability-guard every optional-feature call, and the public `table`
|
|
276
|
+
// getter narrows back to the instance's TFeatures.
|
|
277
|
+
private _table!: DataGridTable
|
|
278
|
+
private _unsubscribeState: (() => void) | null = null
|
|
239
279
|
private _scheduler!: Scheduler
|
|
240
280
|
private _virtualizer: GridVirtualizer | null = null
|
|
241
281
|
private _header!: GridHeader
|
|
@@ -245,7 +285,13 @@ class DataGrid extends BaseComponent {
|
|
|
245
285
|
private _emptySlot: DataGridSlot | null = null
|
|
246
286
|
private _dataSource: GridDataSource | null = null
|
|
247
287
|
private _loading: GridLoading | null = null
|
|
288
|
+
private _loadingMore: GridLoading | null = null
|
|
248
289
|
private _loadFailed = false
|
|
290
|
+
// Infinite scroll accumulates every loaded page here; the table's data is
|
|
291
|
+
// this buffer, not the last response.
|
|
292
|
+
private _loadedItems: DataGridItem[] = []
|
|
293
|
+
private _loadMorePending = false
|
|
294
|
+
private _pageResetPending = false
|
|
249
295
|
private _dataVersion = 0
|
|
250
296
|
private _viewportElement!: HTMLDivElement
|
|
251
297
|
private _tableElement!: HTMLTableElement
|
|
@@ -272,14 +318,15 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
272
318
|
private _editorErrorElement: HTMLElement | null = null
|
|
273
319
|
private _editorFocusPending = false
|
|
274
320
|
private _columnsByKey = new Map<string, DataGridColumn>()
|
|
275
|
-
private
|
|
321
|
+
private _columnDrag: DragController | null = null
|
|
322
|
+
private _rowDrag: DragController | null = null
|
|
276
323
|
private _menu: GridMenu | null = null
|
|
277
324
|
private _tooltip: GridTooltip | null = null
|
|
278
325
|
private _filterMenu: GridFilterMenu | null = null
|
|
279
326
|
private _columnChooser: GridColumnChooser | null = null
|
|
280
327
|
private _toolbarConfig: ResolvedToolbar | null = null
|
|
281
328
|
|
|
282
|
-
constructor(element: Element | string, config?: Partial<DataGridOptions
|
|
329
|
+
constructor(element: Element | string, config?: Partial<DataGridOptions<TFeatures>>) {
|
|
283
330
|
super(element, config)
|
|
284
331
|
|
|
285
332
|
if (!this._element) {
|
|
@@ -305,8 +352,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
305
352
|
}
|
|
306
353
|
|
|
307
354
|
// Getters
|
|
308
|
-
get table():
|
|
309
|
-
|
|
355
|
+
get table(): DataGridTable<TFeatures> {
|
|
356
|
+
// TFeatures is invariant, so the internal full-set alias needs the double
|
|
357
|
+
// cast back to the instance's actual set.
|
|
358
|
+
return this._table as unknown as DataGridTable<TFeatures>
|
|
310
359
|
}
|
|
311
360
|
|
|
312
361
|
static get Default(): DataGridOptions {
|
|
@@ -321,19 +370,22 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
321
370
|
return NAME
|
|
322
371
|
}
|
|
323
372
|
|
|
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
373
|
// Public
|
|
333
374
|
getSelectedItems(): DataGridItem[] {
|
|
375
|
+
if (typeof this._table.getSelectedRowModel !== 'function') {
|
|
376
|
+
return []
|
|
377
|
+
}
|
|
378
|
+
|
|
334
379
|
return this._table.getSelectedRowModel().rows.map(row => row.original)
|
|
335
380
|
}
|
|
336
381
|
|
|
382
|
+
// Prints every sorted and filtered row, not the virtual window or the
|
|
383
|
+
// current page: the grid renders a plain complete table, prints it, and
|
|
384
|
+
// throws it away.
|
|
385
|
+
print(): void {
|
|
386
|
+
printGrid(this._table, this._printColumns(), { title: this._config.printTitle ?? undefined })
|
|
387
|
+
}
|
|
388
|
+
|
|
337
389
|
getCsv(options?: CsvOptions): string {
|
|
338
390
|
return exportCsv(this._table, options)
|
|
339
391
|
}
|
|
@@ -345,22 +397,66 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
345
397
|
// A serializable snapshot of every user-adjustable state slice. Feed it
|
|
346
398
|
// back through restoreState() - scroll position and edit drafts excluded.
|
|
347
399
|
getState(): DataGridState {
|
|
348
|
-
|
|
400
|
+
// A reduced feature set has no atom for its absent slices - the snapshot
|
|
401
|
+
// keeps its full shape with neutral values so persisted state stays
|
|
402
|
+
// portable between feature sets.
|
|
403
|
+
const { state } = this._table.store
|
|
349
404
|
return {
|
|
350
|
-
columnFilters: state.columnFilters,
|
|
351
|
-
columnOrder: state.columnOrder,
|
|
352
|
-
columnPinning: state.columnPinning,
|
|
353
|
-
columnSizing: state.columnSizing,
|
|
354
|
-
columnVisibility: state.columnVisibility,
|
|
405
|
+
columnFilters: state.columnFilters ?? [],
|
|
406
|
+
columnOrder: state.columnOrder ?? [],
|
|
407
|
+
columnPinning: state.columnPinning ?? { start: [], end: [] },
|
|
408
|
+
columnSizing: state.columnSizing ?? {},
|
|
409
|
+
columnVisibility: state.columnVisibility ?? {},
|
|
355
410
|
globalFilter: (state.globalFilter as string | undefined) ?? '',
|
|
356
|
-
pagination: state.pagination,
|
|
357
|
-
rowSelection: state.rowSelection,
|
|
358
|
-
sorting: state.sorting
|
|
411
|
+
pagination: state.pagination ?? { pageIndex: 0, pageSize: Number.MAX_SAFE_INTEGER },
|
|
412
|
+
rowSelection: state.rowSelection ?? {},
|
|
413
|
+
sorting: state.sorting ?? []
|
|
359
414
|
}
|
|
360
415
|
}
|
|
361
416
|
|
|
362
417
|
restoreState(state: Partial<DataGridState>): void {
|
|
363
|
-
|
|
418
|
+
// v9 owns one atom per slice and has no bulk state setter; batching keeps
|
|
419
|
+
// the writes to a single store emission, so the change events still fire
|
|
420
|
+
// once for the whole restore.
|
|
421
|
+
// The setter-presence checks let a snapshot taken on one feature set
|
|
422
|
+
// restore into a grid running a smaller one - absent slices are skipped.
|
|
423
|
+
batch(() => {
|
|
424
|
+
if (state.columnFilters !== undefined && typeof this._table.setColumnFilters === 'function') {
|
|
425
|
+
this._table.setColumnFilters(state.columnFilters)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (state.columnOrder !== undefined && typeof this._table.setColumnOrder === 'function') {
|
|
429
|
+
this._table.setColumnOrder(state.columnOrder)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (state.columnPinning !== undefined && typeof this._table.setColumnPinning === 'function') {
|
|
433
|
+
this._table.setColumnPinning(state.columnPinning)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (state.columnSizing !== undefined && typeof this._table.setColumnSizing === 'function') {
|
|
437
|
+
this._table.setColumnSizing(state.columnSizing)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (state.columnVisibility !== undefined && typeof this._table.setColumnVisibility === 'function') {
|
|
441
|
+
this._table.setColumnVisibility(state.columnVisibility)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (state.globalFilter !== undefined && typeof this._table.setGlobalFilter === 'function') {
|
|
445
|
+
this._table.setGlobalFilter(state.globalFilter)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (state.pagination !== undefined && typeof this._table.setPagination === 'function') {
|
|
449
|
+
this._table.setPagination(state.pagination)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (state.rowSelection !== undefined && typeof this._table.setRowSelection === 'function') {
|
|
453
|
+
this._table.setRowSelection(state.rowSelection)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (state.sorting !== undefined && typeof this._table.setSorting === 'function') {
|
|
457
|
+
this._table.setSorting(state.sorting)
|
|
458
|
+
}
|
|
459
|
+
})
|
|
364
460
|
|
|
365
461
|
// The built-in search input is uncontrolled - sync it to the restored
|
|
366
462
|
// filter so the UI matches the state.
|
|
@@ -424,19 +520,23 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
424
520
|
this._stopEditing({ commit: false })
|
|
425
521
|
this._config.items = items
|
|
426
522
|
this._dataVersion++
|
|
523
|
+
this._virtualizer?.remeasure()
|
|
427
524
|
this._table.setOptions(previous => ({ ...previous, data: items }))
|
|
428
525
|
this._scheduler.schedule()
|
|
429
526
|
}
|
|
430
527
|
|
|
431
528
|
// Full rebuild: recreates the table, wiping sorting/filters/selection/scroll.
|
|
432
529
|
// Use setItems() to swap data while keeping user state.
|
|
433
|
-
update(config: Partial<DataGridOptions
|
|
434
|
-
this._config = this._getConfig({ ...this._config, ...config })
|
|
530
|
+
update(config: Partial<DataGridOptions<TFeatures>>): void {
|
|
531
|
+
this._config = this._getConfig({ ...this._config, ...config }) as DataGridOptions
|
|
435
532
|
this._labels = { ...DEFAULT_LABELS, ...this._config.labels }
|
|
436
533
|
|
|
437
534
|
this._teardownUi()
|
|
438
535
|
this._selectionAnchorIndex = null
|
|
439
536
|
this._loadFailed = false
|
|
537
|
+
this._pageResetPending = false
|
|
538
|
+
this._loadedItems = []
|
|
539
|
+
this._loadMorePending = false
|
|
440
540
|
this._dataVersion++
|
|
441
541
|
this._table = this._createTable()
|
|
442
542
|
this._render()
|
|
@@ -445,6 +545,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
445
545
|
|
|
446
546
|
dispose(): void {
|
|
447
547
|
this._teardownUi()
|
|
548
|
+
this._unsubscribeState?.()
|
|
549
|
+
this._unsubscribeState = null
|
|
448
550
|
this._scheduler.dispose()
|
|
449
551
|
this._tooltip?.dispose()
|
|
450
552
|
this._tooltip = null
|
|
@@ -460,19 +562,25 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
460
562
|
|
|
461
563
|
// Private
|
|
462
564
|
// Editing needs the active-cell substrate (Enter/F2 target the active cell).
|
|
463
|
-
_configAfterMerge(config:
|
|
464
|
-
|
|
565
|
+
_configAfterMerge(config: ComponentConfig): ComponentConfig {
|
|
566
|
+
// Both need the active-cell substrate: editing targets it with Enter/F2,
|
|
567
|
+
// cell selection anchors ranges on it.
|
|
568
|
+
if (config.editing || config.cellSelection) {
|
|
465
569
|
config.cellNavigation = true
|
|
466
570
|
}
|
|
467
571
|
|
|
468
572
|
return config
|
|
469
573
|
}
|
|
470
574
|
|
|
471
|
-
private _createTable():
|
|
472
|
-
|
|
575
|
+
private _createTable(): DataGridTable {
|
|
576
|
+
this._unsubscribeState?.()
|
|
577
|
+
const table = createGridTable(this._config, () => this._element)
|
|
578
|
+
this._unsubscribeState = subscribeToTableState(table, (previous, next) => {
|
|
473
579
|
this._notifyStateChange(previous, next)
|
|
474
580
|
this._scheduler.schedule()
|
|
475
581
|
})
|
|
582
|
+
|
|
583
|
+
return table
|
|
476
584
|
}
|
|
477
585
|
|
|
478
586
|
private _teardownUi(): void {
|
|
@@ -505,6 +613,21 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
505
613
|
this._loading = null
|
|
506
614
|
}
|
|
507
615
|
|
|
616
|
+
if (this._loadingMore) {
|
|
617
|
+
this._loadingMore.element.remove()
|
|
618
|
+
this._loadingMore = null
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (this._columnDrag) {
|
|
622
|
+
this._columnDrag.dispose()
|
|
623
|
+
this._columnDrag = null
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (this._rowDrag) {
|
|
627
|
+
this._rowDrag.dispose()
|
|
628
|
+
this._rowDrag = null
|
|
629
|
+
}
|
|
630
|
+
|
|
508
631
|
if (this._virtualizer) {
|
|
509
632
|
this._virtualizer.destroy()
|
|
510
633
|
this._virtualizer = null
|
|
@@ -581,7 +704,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
581
704
|
return
|
|
582
705
|
}
|
|
583
706
|
|
|
584
|
-
if (
|
|
707
|
+
if (
|
|
708
|
+
!toolbarConfig.columns && !toolbarConfig.export && !toolbarConfig.print &&
|
|
709
|
+
!toolbarConfig.search && !toolbarConfig.history
|
|
710
|
+
) {
|
|
585
711
|
return
|
|
586
712
|
}
|
|
587
713
|
|
|
@@ -591,11 +717,13 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
591
717
|
icons: {
|
|
592
718
|
columns: this._sanitizeIcon(this._config.toolbarColumnsIcon),
|
|
593
719
|
export: this._sanitizeIcon(this._config.toolbarExportIcon),
|
|
720
|
+
print: this._sanitizeIcon(this._config.toolbarPrintIcon),
|
|
594
721
|
redo: this._sanitizeIcon(this._config.toolbarRedoIcon),
|
|
595
722
|
undo: this._sanitizeIcon(this._config.toolbarUndoIcon)
|
|
596
723
|
},
|
|
597
724
|
history: toolbarConfig.history,
|
|
598
725
|
labels: this._labels,
|
|
726
|
+
print: toolbarConfig.print,
|
|
599
727
|
search: toolbarConfig.search
|
|
600
728
|
})
|
|
601
729
|
this._element.append(this._toolbar.element)
|
|
@@ -620,6 +748,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
620
748
|
createActiveCellManager(() => this._scheduler.schedule()) :
|
|
621
749
|
null
|
|
622
750
|
this._element.classList.add(CLASS_NAME_DATA_GRID)
|
|
751
|
+
this._element.classList.toggle(CLASS_NAME_AUTO_ROW_HEIGHT, Boolean(this._config.autoRowHeight))
|
|
623
752
|
this._element.classList.toggle(CLASS_NAME_RESIZABLE, Boolean(this._config.columnSizing))
|
|
624
753
|
this._element.classList.toggle(CLASS_NAME_PINNED, Boolean(this._config.columnPinning))
|
|
625
754
|
if (this._config.sorterVisibility) {
|
|
@@ -637,9 +766,11 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
637
766
|
|
|
638
767
|
const selectionEnabled = Boolean(this._config.rowSelection)
|
|
639
768
|
const selectionOptions = typeof this._config.rowSelection === 'object' ? this._config.rowSelection : {}
|
|
640
|
-
// Server-side mode implies pagination
|
|
641
|
-
//
|
|
642
|
-
const paginationEnabled =
|
|
769
|
+
// Server-side mode implies pagination unless it scrolls infinitely, where
|
|
770
|
+
// the pages accumulate behind the virtualizer instead of replacing.
|
|
771
|
+
const paginationEnabled =
|
|
772
|
+
Boolean(this._config.pagination) ||
|
|
773
|
+
(Boolean(this._config.dataProvider) && !this._config.infiniteScroll)
|
|
643
774
|
const paginationOptions = typeof this._config.pagination === 'object' ? this._config.pagination : {}
|
|
644
775
|
|
|
645
776
|
if (this._config.columnMenu) {
|
|
@@ -654,8 +785,15 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
654
785
|
root: this._element as HTMLElement,
|
|
655
786
|
labels: this._labels,
|
|
656
787
|
getColumn: columnKey => this._columnsByKey.get(columnKey) ?? null,
|
|
657
|
-
getFacetedValues: columnKey =>
|
|
658
|
-
|
|
788
|
+
getFacetedValues: columnKey => {
|
|
789
|
+
const column = this._table.getColumn(columnKey)
|
|
790
|
+
// Only filterType 'select' menus ask for values, and the coverage
|
|
791
|
+
// assertion ties that filter type to the faceting features - this
|
|
792
|
+
// guard covers select columns added later through update().
|
|
793
|
+
return typeof column?.getFacetedUniqueValues === 'function' ?
|
|
794
|
+
[...column.getFacetedUniqueValues().keys()] :
|
|
795
|
+
[]
|
|
796
|
+
},
|
|
659
797
|
getFilterValue: columnKey =>
|
|
660
798
|
this._table.getColumn(columnKey)?.getFilterValue() as DataGridColumnFilterValue | undefined,
|
|
661
799
|
applyFilter: (columnKey, value) => {
|
|
@@ -686,26 +824,22 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
686
824
|
isSortable: column => this._isColumnSortable(column),
|
|
687
825
|
labels: this._labels,
|
|
688
826
|
resizable: Boolean(this._config.columnSizing),
|
|
827
|
+
rowOrder: this._config.rowOrder,
|
|
689
828
|
selection: selectionEnabled,
|
|
690
829
|
selectAll: selectionEnabled && selectionOptions.selectAll !== false,
|
|
691
830
|
table: this._table
|
|
692
831
|
})
|
|
693
832
|
this._body = createBody({
|
|
833
|
+
autoRowHeight: this._config.autoRowHeight && this._config.virtualization,
|
|
694
834
|
cellNavigation: this._config.cellNavigation,
|
|
835
|
+
cellSelection: this._config.cellSelection,
|
|
695
836
|
getDataVersion: () => this._dataVersion,
|
|
696
837
|
getEditingCell: () =>
|
|
697
838
|
(this._editingCell ? { rowId: this._editingCell.rowId, columnId: this._editingCell.columnId } : null),
|
|
698
839
|
// Popup editors overlay the cell; its content stays in place.
|
|
699
840
|
getEditorElement: () => (this._editor && !this._editor.popup ? this._editor.element : null),
|
|
700
841
|
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
|
-
},
|
|
842
|
+
getRowIndexOffset: () => this._rowIndexOffset(),
|
|
709
843
|
getRows: () => this._table.getRowModel().rows,
|
|
710
844
|
getVisibleColumns: () => this._visibleColumns(),
|
|
711
845
|
getVirtualizer: () => this._virtualizer?.instance ?? null,
|
|
@@ -722,6 +856,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
722
856
|
|
|
723
857
|
return typeof this._config.empty === 'function' ? this._config.empty() : this._config.empty
|
|
724
858
|
},
|
|
859
|
+
rowHandleIcon: this._sanitizeIcon(this._config.rowHandleIcon),
|
|
860
|
+
rowOrder: this._config.rowOrder,
|
|
725
861
|
sanitizeContent: html =>
|
|
726
862
|
(this._config.sanitize ? sanitizeHtml(html, DefaultAllowlist, this._config.sanitizeFn) : html),
|
|
727
863
|
selection: selectionEnabled
|
|
@@ -750,6 +886,13 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
750
886
|
this._viewportElement.append(this._loading.element)
|
|
751
887
|
}
|
|
752
888
|
|
|
889
|
+
if (this._config.infiniteScroll) {
|
|
890
|
+
// A second indicator: the overlay covers the grid, which is wrong once
|
|
891
|
+
// rows are already on screen and only the next page is arriving.
|
|
892
|
+
this._loadingMore = createLoading(this._labels, CLASS_NAME_LOADING_MORE)
|
|
893
|
+
this._viewportElement.append(this._loadingMore.element)
|
|
894
|
+
}
|
|
895
|
+
|
|
753
896
|
this._element.append(this._viewportElement)
|
|
754
897
|
|
|
755
898
|
if (paginationEnabled) {
|
|
@@ -788,6 +931,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
788
931
|
}
|
|
789
932
|
} else if (this._config.virtualization) {
|
|
790
933
|
this._virtualizer = createGridVirtualizer({
|
|
934
|
+
autoRowHeight: this._config.autoRowHeight,
|
|
791
935
|
count: this._table.getRowModel().rows.length,
|
|
792
936
|
getScrollElement: () => this._viewportElement,
|
|
793
937
|
onChange: () => this._scheduler.schedule(),
|
|
@@ -796,6 +940,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
796
940
|
})
|
|
797
941
|
}
|
|
798
942
|
|
|
943
|
+
this._initColumnDrag()
|
|
944
|
+
this._initRowDrag()
|
|
799
945
|
this._update()
|
|
800
946
|
}
|
|
801
947
|
|
|
@@ -818,6 +964,18 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
818
964
|
}
|
|
819
965
|
})
|
|
820
966
|
|
|
967
|
+
// Drag-select: table-core owns the session (it attaches its own document
|
|
968
|
+
// mouseup), so the grid only routes the two events at the right cell.
|
|
969
|
+
EventHandler.on(this._element, EVENT_MOUSEDOWN, SELECTOR_BODY_CELL, event => {
|
|
970
|
+
const cell = this._tableCellFromTarget(event)
|
|
971
|
+
cell?.getSelectionStartHandler()(event)
|
|
972
|
+
})
|
|
973
|
+
|
|
974
|
+
EventHandler.on(this._element, EVENT_MOUSEENTER, SELECTOR_BODY_CELL, event => {
|
|
975
|
+
const cell = this._tableCellFromTarget(event)
|
|
976
|
+
cell?.getSelectionExtendHandler()(event)
|
|
977
|
+
})
|
|
978
|
+
|
|
821
979
|
EventHandler.on(this._element, EVENT_DBLCLICK, event => {
|
|
822
980
|
if (!this._config.editing) {
|
|
823
981
|
return
|
|
@@ -872,7 +1030,12 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
872
1030
|
})
|
|
873
1031
|
|
|
874
1032
|
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_TOOLBAR_EXPORT, () => {
|
|
875
|
-
|
|
1033
|
+
const { exporter, ...exportOptions } = this._toolbarConfig?.exportOptions ?? {};
|
|
1034
|
+
(exporter ?? downloadCsv)(this._table, { scope: 'filtered', ...exportOptions })
|
|
1035
|
+
})
|
|
1036
|
+
|
|
1037
|
+
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_TOOLBAR_PRINT, () => {
|
|
1038
|
+
this.print()
|
|
876
1039
|
})
|
|
877
1040
|
|
|
878
1041
|
EventHandler.on(this._element, EVENT_CLICK, SELECTOR_TOOLBAR_UNDO, () => {
|
|
@@ -883,42 +1046,6 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
883
1046
|
this.redo()
|
|
884
1047
|
})
|
|
885
1048
|
|
|
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
1049
|
EventHandler.on(this._element, EVENT_MOUSEDOWN, SELECTOR_RESIZER, event => {
|
|
923
1050
|
this._startColumnResize(event)
|
|
924
1051
|
})
|
|
@@ -1147,6 +1274,22 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1147
1274
|
return
|
|
1148
1275
|
}
|
|
1149
1276
|
|
|
1277
|
+
if (this._config.cellSelection && (event.ctrlKey || event.metaKey) && found.cell.rowIndex >= 0) {
|
|
1278
|
+
const key = event.key.toLowerCase()
|
|
1279
|
+
if (key === 'a') {
|
|
1280
|
+
event.preventDefault()
|
|
1281
|
+
this._table.selectAllCells()
|
|
1282
|
+
this._scheduler.schedule()
|
|
1283
|
+
return
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
if (key === 'c') {
|
|
1287
|
+
event.preventDefault()
|
|
1288
|
+
this._copyCellSelection()
|
|
1289
|
+
return
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1150
1293
|
const result = resolveNavigation(event.key, event.ctrlKey || event.metaKey, found.cell, this._activeCellContext())
|
|
1151
1294
|
if (!result) {
|
|
1152
1295
|
return
|
|
@@ -1154,6 +1297,19 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1154
1297
|
|
|
1155
1298
|
event.preventDefault()
|
|
1156
1299
|
|
|
1300
|
+
// Shift+arrow grows the range from its anchor and leaves focus put; a
|
|
1301
|
+
// plain arrow moves focus and collapses the range to the new cell.
|
|
1302
|
+
if (this._config.cellSelection && found.cell.rowIndex >= 0 && result.cell.rowIndex >= 0) {
|
|
1303
|
+
const direction = CELL_SELECTION_DIRECTIONS[
|
|
1304
|
+
isElementRTL(this._element) ? (INVERTED_ARROWS[event.key] ?? event.key) : event.key
|
|
1305
|
+
]
|
|
1306
|
+
if (event.shiftKey && direction) {
|
|
1307
|
+
this._table.extendCellSelection(direction)
|
|
1308
|
+
this._scheduler.schedule()
|
|
1309
|
+
return
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1157
1313
|
if (result.pageDelta === 1 && this._table.getCanNextPage()) {
|
|
1158
1314
|
this._table.nextPage()
|
|
1159
1315
|
} else if (result.pageDelta === -1 && this._table.getCanPreviousPage()) {
|
|
@@ -1163,6 +1319,24 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1163
1319
|
this._setActiveCell(result.cell)
|
|
1164
1320
|
}
|
|
1165
1321
|
|
|
1322
|
+
// The table-core Cell behind a DOM cell, or null when cell selection is off
|
|
1323
|
+
// or the target is not a data cell.
|
|
1324
|
+
private _tableCellFromTarget(event: Event & Record<string, unknown>) {
|
|
1325
|
+
if (!this._config.cellSelection) {
|
|
1326
|
+
return null
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
const element = (event.delegateTarget ?? event.target) as HTMLElement
|
|
1330
|
+
const columnId = element.dataset.coreuiColumn
|
|
1331
|
+
const rowIndex = Number(element.closest<HTMLElement>('tr')?.dataset.coreuiIndex ?? -1)
|
|
1332
|
+
if (!columnId || rowIndex < 0) {
|
|
1333
|
+
return null
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
const row = this._table.getRowModel().rows[rowIndex]
|
|
1337
|
+
return row?.getAllCells().find(cell => cell.column.id === columnId) ?? null
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1166
1340
|
private _descendIntoCell(cellElement: HTMLElement): void {
|
|
1167
1341
|
cellElement.querySelector<HTMLElement>(FOCUSABLE_SELECTOR)?.focus({ preventScroll: true })
|
|
1168
1342
|
}
|
|
@@ -1429,6 +1603,15 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1429
1603
|
|
|
1430
1604
|
private _setActiveCell(cell: ActiveCell): void {
|
|
1431
1605
|
this._activeCell?.set(cell, { focus: true })
|
|
1606
|
+
// The active cell is the grid's single focus owner; cell selection follows
|
|
1607
|
+
// it rather than keeping a second anchor of its own.
|
|
1608
|
+
if (this._config.cellSelection && cell.rowIndex >= 0) {
|
|
1609
|
+
const row = this._table.getRowModel().rows[cell.rowIndex]
|
|
1610
|
+
if (row) {
|
|
1611
|
+
this._table.setFocusedCell(row.id, cell.columnId)
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1432
1615
|
if (this._virtualizer && cell.rowIndex >= 0) {
|
|
1433
1616
|
// No-op when the row is already in view (align: auto); otherwise the
|
|
1434
1617
|
// focus request stays pending until the row renders on the next flush.
|
|
@@ -1436,6 +1619,28 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1436
1619
|
}
|
|
1437
1620
|
}
|
|
1438
1621
|
|
|
1622
|
+
// The columns the selection's column indexes address, with the formatter
|
|
1623
|
+
// each cell displays through — a copied date pastes as the date on screen.
|
|
1624
|
+
private _printColumns(): PrintColumn[] {
|
|
1625
|
+
return this._visibleColumns().map(column => ({
|
|
1626
|
+
id: column.key,
|
|
1627
|
+
label: column.label ?? column.key,
|
|
1628
|
+
formatter: column.formatter
|
|
1629
|
+
}))
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
private _clipboardColumns(): ClipboardColumn[] {
|
|
1633
|
+
return this._visibleColumns().map(column => ({ id: column.key, formatter: column.formatter }))
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
private _copyCellSelection(): void {
|
|
1637
|
+
copyCellSelection(this._table, this._clipboardColumns()).then(text => {
|
|
1638
|
+
if (text !== '') {
|
|
1639
|
+
EventHandler.trigger(this._element, EVENT_CELL_COPY, { text })
|
|
1640
|
+
}
|
|
1641
|
+
}, () => {})
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1439
1644
|
private _activeCellContext(): ActiveCellContext {
|
|
1440
1645
|
const rowCount = this._table.getRowModel().rows.length
|
|
1441
1646
|
const paginated = Boolean(this._config.pagination) || Boolean(this._config.dataProvider)
|
|
@@ -1445,9 +1650,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1445
1650
|
columnIds: this._visibleLeafColumns().map(column => column.id),
|
|
1446
1651
|
rowCount,
|
|
1447
1652
|
pageStep: paginated ?
|
|
1448
|
-
this._table.
|
|
1653
|
+
this._table.store.state.pagination.pageSize :
|
|
1449
1654
|
Math.max(1, viewportRows || rowCount),
|
|
1450
|
-
paginated
|
|
1655
|
+
paginated,
|
|
1656
|
+
rtl: isElementRTL(this._element)
|
|
1451
1657
|
}
|
|
1452
1658
|
}
|
|
1453
1659
|
|
|
@@ -1563,23 +1769,23 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1563
1769
|
|
|
1564
1770
|
if (this._config.columnPinning) {
|
|
1565
1771
|
const pinned = column.getIsPinned()
|
|
1566
|
-
if (pinned !== '
|
|
1772
|
+
if (pinned !== 'start') {
|
|
1567
1773
|
actions.push({
|
|
1568
|
-
key: 'pin-
|
|
1569
|
-
label: this._labels.
|
|
1774
|
+
key: 'pin-start',
|
|
1775
|
+
label: this._labels.pinStart,
|
|
1570
1776
|
group: 'pinning',
|
|
1571
|
-
icon: this._sanitizeIcon(this._config.
|
|
1572
|
-
run: () => column.pin('
|
|
1777
|
+
icon: this._sanitizeIcon(this._config.pinStartIcon),
|
|
1778
|
+
run: () => column.pin('start')
|
|
1573
1779
|
})
|
|
1574
1780
|
}
|
|
1575
1781
|
|
|
1576
|
-
if (pinned !== '
|
|
1782
|
+
if (pinned !== 'end') {
|
|
1577
1783
|
actions.push({
|
|
1578
|
-
key: 'pin-
|
|
1579
|
-
label: this._labels.
|
|
1784
|
+
key: 'pin-end',
|
|
1785
|
+
label: this._labels.pinEnd,
|
|
1580
1786
|
group: 'pinning',
|
|
1581
|
-
icon: this._sanitizeIcon(this._config.
|
|
1582
|
-
run: () => column.pin('
|
|
1787
|
+
icon: this._sanitizeIcon(this._config.pinEndIcon),
|
|
1788
|
+
run: () => column.pin('end')
|
|
1583
1789
|
})
|
|
1584
1790
|
}
|
|
1585
1791
|
|
|
@@ -1595,23 +1801,23 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1595
1801
|
}
|
|
1596
1802
|
|
|
1597
1803
|
if (this._config.columnOrder && config.movable !== false) {
|
|
1598
|
-
const region = this._visibleLeafColumns().filter(item =>
|
|
1804
|
+
const region = this._visibleLeafColumns().filter(item => this._pinnedRegion(item) === this._pinnedRegion(column))
|
|
1599
1805
|
const index = region.findIndex(item => item.id === columnKey)
|
|
1600
1806
|
actions.push(
|
|
1601
1807
|
{
|
|
1602
|
-
key: 'move-
|
|
1603
|
-
label: this._labels.
|
|
1808
|
+
key: 'move-start',
|
|
1809
|
+
label: this._labels.moveStart,
|
|
1604
1810
|
disabled: index <= 0,
|
|
1605
1811
|
group: 'ordering',
|
|
1606
|
-
icon: this._sanitizeIcon(this._config.
|
|
1812
|
+
icon: this._sanitizeIcon(this._config.moveStartIcon),
|
|
1607
1813
|
run: () => this._moveColumn(columnKey, -1)
|
|
1608
1814
|
},
|
|
1609
1815
|
{
|
|
1610
|
-
key: 'move-
|
|
1611
|
-
label: this._labels.
|
|
1816
|
+
key: 'move-end',
|
|
1817
|
+
label: this._labels.moveEnd,
|
|
1612
1818
|
disabled: index === region.length - 1,
|
|
1613
1819
|
group: 'ordering',
|
|
1614
|
-
icon: this._sanitizeIcon(this._config.
|
|
1820
|
+
icon: this._sanitizeIcon(this._config.moveEndIcon),
|
|
1615
1821
|
run: () => this._moveColumn(columnKey, 1)
|
|
1616
1822
|
}
|
|
1617
1823
|
)
|
|
@@ -1644,7 +1850,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1644
1850
|
// missing ids at the end when they are shown again.
|
|
1645
1851
|
private _orderedLeafIds(): string[] {
|
|
1646
1852
|
const allIds = this._table.getAllLeafColumns().map(column => column.id)
|
|
1647
|
-
const order = this._table.
|
|
1853
|
+
const order = this._table.store.state.columnOrder ?? []
|
|
1648
1854
|
if (order.length === 0) {
|
|
1649
1855
|
return allIds
|
|
1650
1856
|
}
|
|
@@ -1661,10 +1867,10 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1661
1867
|
return
|
|
1662
1868
|
}
|
|
1663
1869
|
|
|
1664
|
-
const region =
|
|
1870
|
+
const region = this._pinnedRegion(column)
|
|
1665
1871
|
const ids = this._orderedLeafIds()
|
|
1666
1872
|
const regionIds = this._visibleLeafColumns()
|
|
1667
|
-
.filter(item =>
|
|
1873
|
+
.filter(item => this._pinnedRegion(item) === region)
|
|
1668
1874
|
.map(item => item.id)
|
|
1669
1875
|
const neighbor = regionIds[regionIds.indexOf(columnKey) + direction]
|
|
1670
1876
|
if (!neighbor) {
|
|
@@ -1678,64 +1884,92 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1678
1884
|
this._table.setColumnOrder(ids)
|
|
1679
1885
|
}
|
|
1680
1886
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
if ((event.target as HTMLElement).closest?.(SELECTOR_RESIZER)) {
|
|
1686
|
-
event.preventDefault()
|
|
1887
|
+
// Pointer-based, not HTML5 drag events: the controller owns the ghost, the
|
|
1888
|
+
// drop indicator and the edge auto-scroll, and behaves the same on touch.
|
|
1889
|
+
private _initColumnDrag(): void {
|
|
1890
|
+
if (!this._config.columnOrder) {
|
|
1687
1891
|
return
|
|
1688
1892
|
}
|
|
1689
1893
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1894
|
+
this._columnDrag = createColumnDragController({
|
|
1895
|
+
root: this._element as HTMLElement,
|
|
1896
|
+
getScrollElement: () => this._viewportElement,
|
|
1897
|
+
canDrop: (sourceKey, targetKey) => {
|
|
1898
|
+
const source = this._table.getColumn(sourceKey)
|
|
1899
|
+
const target = this._table.getColumn(targetKey)
|
|
1900
|
+
return Boolean(source && target) &&
|
|
1901
|
+
sourceKey !== targetKey &&
|
|
1902
|
+
this._pinnedRegion(source!) === this._pinnedRegion(target!)
|
|
1903
|
+
},
|
|
1904
|
+
onReorder: (sourceKey, targetKey, after) => this._moveColumnTo(sourceKey, targetKey, after)
|
|
1905
|
+
})
|
|
1906
|
+
}
|
|
1693
1907
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1908
|
+
// Rows are indexed within the current row model; the offset turns that into
|
|
1909
|
+
// a position in `items`.
|
|
1910
|
+
private _rowIndexOffset(): number {
|
|
1911
|
+
// The infinite buffer always starts at the first row, so its indices are
|
|
1912
|
+
// already absolute.
|
|
1913
|
+
if (this._config.infiniteScroll || (!this._config.pagination && !this._config.dataProvider)) {
|
|
1914
|
+
return 0
|
|
1698
1915
|
}
|
|
1699
|
-
}
|
|
1700
1916
|
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
const targetKey = target.dataset.coreuiColumn
|
|
1705
|
-
const sourceKey = this._dragColumn
|
|
1706
|
-
this._endColumnDrag()
|
|
1917
|
+
const { pageIndex, pageSize } = this._table.store.state.pagination
|
|
1918
|
+
return pageIndex * pageSize
|
|
1919
|
+
}
|
|
1707
1920
|
|
|
1708
|
-
|
|
1921
|
+
private _initRowDrag(): void {
|
|
1922
|
+
if (!this._config.rowOrder) {
|
|
1709
1923
|
return
|
|
1710
1924
|
}
|
|
1711
1925
|
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1926
|
+
this._rowDrag = createRowDragController({
|
|
1927
|
+
root: this._element as HTMLElement,
|
|
1928
|
+
getScrollElement: () => this._viewportElement,
|
|
1929
|
+
onReorder: (sourceIndex, targetIndex, after) => this._moveRowTo(sourceIndex, targetIndex, after)
|
|
1930
|
+
})
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// The grid never mutates `items` — it hands the reordered array to the app
|
|
1934
|
+
// through rowOrderChange, the same contract inline editing uses.
|
|
1935
|
+
private _moveRowTo(sourceIndex: number, targetIndex: number, after: boolean): void {
|
|
1936
|
+
const offset = this._rowIndexOffset()
|
|
1937
|
+
const items = [...this._config.items]
|
|
1938
|
+
const from = offset + sourceIndex
|
|
1939
|
+
const [item] = items.splice(from, 1)
|
|
1940
|
+
if (item === undefined) {
|
|
1717
1941
|
return
|
|
1718
1942
|
}
|
|
1719
1943
|
|
|
1944
|
+
// Resolved AFTER the removal, so the index still points at the target
|
|
1945
|
+
// whichever direction the row travelled.
|
|
1946
|
+
const target = offset + targetIndex
|
|
1947
|
+
const to = target > from ? target - 1 : target
|
|
1948
|
+
const newIndex = after ? to + 1 : to
|
|
1949
|
+
items.splice(newIndex, 0, item)
|
|
1950
|
+
|
|
1951
|
+
EventHandler.trigger(this._element, EVENT_ROW_ORDER_CHANGE, {
|
|
1952
|
+
items, item, oldIndex: from, newIndex
|
|
1953
|
+
})
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
private _moveColumnTo(sourceKey: string, targetKey: string, after: boolean): void {
|
|
1720
1957
|
const ids = this._orderedLeafIds()
|
|
1721
1958
|
const from = ids.indexOf(sourceKey)
|
|
1722
|
-
|
|
1723
|
-
if (from === -1 || to === -1) {
|
|
1959
|
+
if (from === -1) {
|
|
1724
1960
|
return
|
|
1725
1961
|
}
|
|
1726
1962
|
|
|
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
1963
|
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)
|
|
1964
|
+
// Resolved AFTER the removal, so the index still points at the target
|
|
1965
|
+
// whichever direction the column travelled.
|
|
1966
|
+
const to = ids.indexOf(targetKey)
|
|
1967
|
+
if (to === -1) {
|
|
1968
|
+
return
|
|
1738
1969
|
}
|
|
1970
|
+
|
|
1971
|
+
ids.splice(after ? to + 1 : to, 0, sourceKey)
|
|
1972
|
+
this._table.setColumnOrder(ids)
|
|
1739
1973
|
}
|
|
1740
1974
|
|
|
1741
1975
|
private _startColumnResize(event: Event & Record<string, unknown>): void {
|
|
@@ -1751,6 +1985,11 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1751
1985
|
event.preventDefault()
|
|
1752
1986
|
}
|
|
1753
1987
|
|
|
1988
|
+
// The direction table-core snapshotted at construction may be stale — an
|
|
1989
|
+
// app that swaps language flips dir on a live grid. A resize is the only
|
|
1990
|
+
// moment it matters, and it is rare enough to re-read here.
|
|
1991
|
+
syncResizeDirection(this._table, this._element)
|
|
1992
|
+
|
|
1754
1993
|
const header = this._table.getHeaderGroups()[0]?.headers.find(item => item.column.id === key)
|
|
1755
1994
|
header?.getResizeHandler()(event)
|
|
1756
1995
|
}
|
|
@@ -1797,7 +2036,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1797
2036
|
this._dataSource = createDataSource({
|
|
1798
2037
|
provider: request => dataProvider(request),
|
|
1799
2038
|
getRequest: () => {
|
|
1800
|
-
const state = this._table.
|
|
2039
|
+
const { state } = this._table.store
|
|
1801
2040
|
return {
|
|
1802
2041
|
columnFilters: state.columnFilters,
|
|
1803
2042
|
globalFilter: (state.globalFilter as string | undefined) ?? '',
|
|
@@ -1805,13 +2044,29 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1805
2044
|
sorting: state.sorting
|
|
1806
2045
|
}
|
|
1807
2046
|
},
|
|
1808
|
-
onLoad: response => {
|
|
2047
|
+
onLoad: (response, request) => {
|
|
1809
2048
|
this._stopEditing({ commit: false })
|
|
1810
2049
|
this._loadFailed = false
|
|
2050
|
+
this._loadMorePending = false
|
|
1811
2051
|
this._dataVersion++
|
|
2052
|
+
|
|
2053
|
+
let { items } = response
|
|
2054
|
+
if (this._config.infiniteScroll) {
|
|
2055
|
+
const firstPage = request.pagination.pageIndex === 0
|
|
2056
|
+
items = firstPage ? response.items : [...this._loadedItems, ...response.items]
|
|
2057
|
+
this._loadedItems = items
|
|
2058
|
+
// Appended rows land at fresh indices; a first page puts different
|
|
2059
|
+
// data at indices the virtualizer has already measured — and shrinks
|
|
2060
|
+
// the buffer back under a viewport that may be scrolled far past it.
|
|
2061
|
+
if (firstPage) {
|
|
2062
|
+
this._viewportElement.scrollTop = 0
|
|
2063
|
+
this._virtualizer?.remeasure()
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
1812
2067
|
this._table.setOptions(previous => ({
|
|
1813
2068
|
...previous,
|
|
1814
|
-
data:
|
|
2069
|
+
data: items,
|
|
1815
2070
|
rowCount: response.totalRows
|
|
1816
2071
|
}))
|
|
1817
2072
|
EventHandler.trigger(this._element, EVENT_DATA_LOAD, {
|
|
@@ -1824,16 +2079,17 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1824
2079
|
onError: error => {
|
|
1825
2080
|
this._stopEditing({ commit: false })
|
|
1826
2081
|
this._loadFailed = true
|
|
2082
|
+
this._loadMorePending = false
|
|
2083
|
+
this._loadedItems = []
|
|
1827
2084
|
this._dataVersion++
|
|
1828
2085
|
this._table.setOptions(previous => ({ ...previous, data: [], rowCount: 0 }))
|
|
1829
2086
|
EventHandler.trigger(this._element, EVENT_DATA_ERROR, { error })
|
|
1830
2087
|
this._scheduler.schedule()
|
|
1831
2088
|
},
|
|
1832
2089
|
onLoadingChange: loading => {
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2090
|
+
const appending = Boolean(this._config.infiniteScroll) && this._loadedItems.length > 0
|
|
2091
|
+
this._loading?.setVisible(loading && !appending)
|
|
2092
|
+
this._loadingMore?.setVisible(loading && appending)
|
|
1837
2093
|
this._viewportElement.setAttribute('aria-busy', String(loading))
|
|
1838
2094
|
}
|
|
1839
2095
|
})
|
|
@@ -1841,14 +2097,55 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1841
2097
|
this._dataSource.load()
|
|
1842
2098
|
}
|
|
1843
2099
|
|
|
2100
|
+
// The trigger is the last RENDERED row index, not a scroll ratio: measured
|
|
2101
|
+
// row heights make the ratio meaningless (the total size keeps moving as
|
|
2102
|
+
// rows are measured, so a ratio either fires early or never).
|
|
2103
|
+
private _maybeLoadMore(): void {
|
|
2104
|
+
if (!this._config.infiniteScroll || !this._dataSource || !this._virtualizer || this._loadMorePending) {
|
|
2105
|
+
return
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
const loaded = this._table.getRowModel().rows.length
|
|
2109
|
+
if (loaded === 0 || loaded >= this._getTotalRows()) {
|
|
2110
|
+
return
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
const options = typeof this._config.infiniteScroll === 'object' ? this._config.infiniteScroll : {}
|
|
2114
|
+
const threshold = options.threshold ?? this._config.overscan
|
|
2115
|
+
const rendered = this._virtualizer.instance.getVirtualItems()
|
|
2116
|
+
const last = rendered[rendered.length - 1]
|
|
2117
|
+
if (!last || last.index < loaded - 1 - threshold) {
|
|
2118
|
+
return
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
this._loadMorePending = true
|
|
2122
|
+
this._table.setPageIndex(this._table.store.state.pagination.pageIndex + 1)
|
|
2123
|
+
}
|
|
2124
|
+
|
|
1844
2125
|
// Visible leaf columns in VISUAL order: left-pinned, center (columnOrder),
|
|
1845
2126
|
// right-pinned — getVisibleLeafColumns() alone does not regroup pinning.
|
|
2127
|
+
// Without the pinning feature there are no regions to regroup, and without
|
|
2128
|
+
// visibility every leaf column is visible.
|
|
1846
2129
|
private _visibleLeafColumns() {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
2130
|
+
if (typeof this._table.getCenterVisibleLeafColumns === 'function') {
|
|
2131
|
+
return [
|
|
2132
|
+
...this._table.getStartVisibleLeafColumns(),
|
|
2133
|
+
...this._table.getCenterVisibleLeafColumns(),
|
|
2134
|
+
...this._table.getEndVisibleLeafColumns()
|
|
2135
|
+
]
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
if (typeof this._table.getVisibleLeafColumns === 'function') {
|
|
2139
|
+
return this._table.getVisibleLeafColumns()
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
return this._table.getAllLeafColumns()
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// Ordering paths compare pinned regions even on grids that never enabled
|
|
2146
|
+
// pinning - without the feature every column sits in the unpinned region.
|
|
2147
|
+
private _pinnedRegion(column: { getIsPinned?: () => 'start' | 'end' | false }): 'start' | 'end' | false {
|
|
2148
|
+
return typeof column.getIsPinned === 'function' ? column.getIsPinned() : false
|
|
1852
2149
|
}
|
|
1853
2150
|
|
|
1854
2151
|
private _chooserColumns(): ColumnChooserColumn[] {
|
|
@@ -1893,14 +2190,14 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1893
2190
|
}
|
|
1894
2191
|
|
|
1895
2192
|
private _getFilteredColumns(): string[] {
|
|
1896
|
-
return this._table.
|
|
2193
|
+
return this._table.store.state.columnFilters.map(filter => filter.id)
|
|
1897
2194
|
}
|
|
1898
2195
|
|
|
1899
2196
|
private _getPinning(): GridPinning {
|
|
1900
|
-
// Visible columns only -
|
|
2197
|
+
// Visible columns only - getStartLeafColumns/getPinnedIndex include hidden
|
|
1901
2198
|
// columns, which would leave the edge class on a column nobody sees.
|
|
1902
|
-
const leftColumns = this._table.
|
|
1903
|
-
const rightColumns = this._table.
|
|
2199
|
+
const leftColumns = this._table.getStartLeafColumns().filter(column => column.getIsVisible())
|
|
2200
|
+
const rightColumns = this._table.getEndLeafColumns().filter(column => column.getIsVisible())
|
|
1904
2201
|
// The selection cell is not a TanStack column; anchor it to the left as
|
|
1905
2202
|
// soon as any data column is pinned there so the checkbox travels with them.
|
|
1906
2203
|
const selectPinned = Boolean(this._config.rowSelection) && leftColumns.length > 0
|
|
@@ -1908,8 +2205,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1908
2205
|
|
|
1909
2206
|
for (const [index, column] of leftColumns.entries()) {
|
|
1910
2207
|
columns[column.id] = {
|
|
1911
|
-
side: '
|
|
1912
|
-
offset: column.getStart('
|
|
2208
|
+
side: 'start',
|
|
2209
|
+
offset: column.getStart('start'),
|
|
1913
2210
|
edge: index === leftColumns.length - 1,
|
|
1914
2211
|
afterSelect: selectPinned
|
|
1915
2212
|
}
|
|
@@ -1917,8 +2214,8 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1917
2214
|
|
|
1918
2215
|
for (const [index, column] of rightColumns.entries()) {
|
|
1919
2216
|
columns[column.id] = {
|
|
1920
|
-
side: '
|
|
1921
|
-
offset: column.getAfter('
|
|
2217
|
+
side: 'end',
|
|
2218
|
+
offset: column.getAfter('end'),
|
|
1922
2219
|
edge: index === 0,
|
|
1923
2220
|
afterSelect: false
|
|
1924
2221
|
}
|
|
@@ -1928,7 +2225,7 @@ private _undoStack: DataGridEditCommitEntry[] = []
|
|
|
1928
2225
|
columns,
|
|
1929
2226
|
select: selectPinned ?
|
|
1930
2227
|
{
|
|
1931
|
-
side: '
|
|
2228
|
+
side: 'start', offset: 0, edge: false, afterSelect: false
|
|
1932
2229
|
} :
|
|
1933
2230
|
null
|
|
1934
2231
|
}
|
|
@@ -1986,10 +2283,14 @@ null
|
|
|
1986
2283
|
return this._table.options.rowCount ?? 0
|
|
1987
2284
|
}
|
|
1988
2285
|
|
|
2286
|
+
if (typeof this._table.getFilteredRowModel !== 'function') {
|
|
2287
|
+
return this._table.getCoreRowModel().rows.length
|
|
2288
|
+
}
|
|
2289
|
+
|
|
1989
2290
|
return this._table.getFilteredRowModel().rows.length
|
|
1990
2291
|
}
|
|
1991
2292
|
|
|
1992
|
-
private _notifyStateChange(previous:
|
|
2293
|
+
private _notifyStateChange(previous: DataGridTableState, next: DataGridTableState): void {
|
|
1993
2294
|
// TanStack's resize handler keeps document-level listeners until mouseup;
|
|
1994
2295
|
// a dispose mid-drag would re-enter here against a torn-down instance.
|
|
1995
2296
|
if (!this._element) {
|
|
@@ -2025,16 +2326,22 @@ null
|
|
|
2025
2326
|
// auto-reset the page index, which replaces the state object without
|
|
2026
2327
|
// changing it - identity alone would kill an edit right after its
|
|
2027
2328
|
// editCommit handler applied the change.
|
|
2329
|
+
// Infinite scroll is the exception: its page bumps only append, so every
|
|
2330
|
+
// row already on screen keeps its index, its measurement and its focus.
|
|
2028
2331
|
if (
|
|
2029
2332
|
previous.sorting !== next.sorting ||
|
|
2030
2333
|
previous.columnFilters !== next.columnFilters ||
|
|
2031
2334
|
previous.globalFilter !== next.globalFilter ||
|
|
2032
|
-
|
|
2033
|
-
|
|
2335
|
+
(!this._config.infiniteScroll &&
|
|
2336
|
+
(previous.pagination?.pageIndex !== next.pagination?.pageIndex ||
|
|
2337
|
+
previous.pagination?.pageSize !== next.pagination?.pageSize))
|
|
2034
2338
|
) {
|
|
2035
2339
|
this._stopEditing({ commit: true, force: true })
|
|
2036
2340
|
this._selectionAnchorIndex = null
|
|
2037
2341
|
this._activeCell?.reset()
|
|
2342
|
+
// Row measurements are cached by index, so the same index now holding a
|
|
2343
|
+
// different row makes them stale.
|
|
2344
|
+
this._virtualizer?.remeasure()
|
|
2038
2345
|
}
|
|
2039
2346
|
|
|
2040
2347
|
if (previous.sorting !== next.sorting) {
|
|
@@ -2064,7 +2371,9 @@ null
|
|
|
2064
2371
|
})
|
|
2065
2372
|
}
|
|
2066
2373
|
|
|
2067
|
-
|
|
2374
|
+
// No paginationChange under infinite scroll — pagination is internal
|
|
2375
|
+
// there (no widget, not user-adjustable); dataLoad is the page signal.
|
|
2376
|
+
if (previous.pagination !== next.pagination && !this._config.infiniteScroll) {
|
|
2068
2377
|
EventHandler.trigger(this._element, EVENT_PAGINATION_CHANGE, {
|
|
2069
2378
|
pagination: next.pagination
|
|
2070
2379
|
})
|
|
@@ -2097,15 +2406,26 @@ null
|
|
|
2097
2406
|
if (this._dataSource) {
|
|
2098
2407
|
const refetchNeeded = filtersChanged || previous.sorting !== next.sorting
|
|
2099
2408
|
|
|
2100
|
-
if (refetchNeeded
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2409
|
+
if (refetchNeeded) {
|
|
2410
|
+
if (next.pagination.pageIndex !== 0) {
|
|
2411
|
+
// Back to page one. The write lands synchronously, so the request the
|
|
2412
|
+
// debounced fetch reads already carries index 0 - but it also echoes
|
|
2413
|
+
// back here as a pagination change, which must not fetch again.
|
|
2414
|
+
this._pageResetPending = true
|
|
2415
|
+
this._table.setPageIndex(0)
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
this._dataSource.load()
|
|
2104
2419
|
return
|
|
2105
2420
|
}
|
|
2106
2421
|
|
|
2107
|
-
if (
|
|
2108
|
-
this.
|
|
2422
|
+
if (previous.pagination !== next.pagination) {
|
|
2423
|
+
const echoedPageReset = this._pageResetPending && next.pagination.pageIndex === 0
|
|
2424
|
+
this._pageResetPending = false
|
|
2425
|
+
|
|
2426
|
+
if (!echoedPageReset) {
|
|
2427
|
+
this._dataSource.load()
|
|
2428
|
+
}
|
|
2109
2429
|
}
|
|
2110
2430
|
}
|
|
2111
2431
|
}
|
|
@@ -2136,14 +2456,16 @@ null
|
|
|
2136
2456
|
order: this._visibleLeafColumns().map(column => column.id),
|
|
2137
2457
|
pinning: this._config.columnPinning ? this._getPinning() : null,
|
|
2138
2458
|
resizingColumn: this._config.columnSizing ?
|
|
2139
|
-
this._table.
|
|
2459
|
+
this._table.store.state.columnResizing.isResizingColumn :
|
|
2140
2460
|
false,
|
|
2141
2461
|
sizes: (this._config.columnSizing || this._config.columnPinning) ? this._getColumnSizes() : null,
|
|
2142
|
-
sorting: this._table.
|
|
2462
|
+
sorting: this._table.store.state.sorting ?? [],
|
|
2143
2463
|
selectAll: this._config.rowSelection ?
|
|
2144
2464
|
{
|
|
2145
2465
|
checked: this._table.getIsAllRowsSelected(),
|
|
2146
|
-
|
|
2466
|
+
// getIsSomeRowsSelected() means "at least one" - the all-selected
|
|
2467
|
+
// case has to be excluded for the tri-state checkbox.
|
|
2468
|
+
indeterminate: this._table.getIsSomeRowsSelected() && !this._table.getIsAllRowsSelected()
|
|
2147
2469
|
} :
|
|
2148
2470
|
null
|
|
2149
2471
|
})
|
|
@@ -2169,10 +2491,12 @@ null
|
|
|
2169
2491
|
for (const instance of this._paginations) {
|
|
2170
2492
|
instance.refresh()
|
|
2171
2493
|
}
|
|
2494
|
+
|
|
2495
|
+
this._maybeLoadMore()
|
|
2172
2496
|
}
|
|
2173
2497
|
|
|
2174
2498
|
private _paginationState() {
|
|
2175
|
-
const { pagination } = this._table.
|
|
2499
|
+
const { pagination } = this._table.store.state
|
|
2176
2500
|
return {
|
|
2177
2501
|
pageCount: this._table.getPageCount(),
|
|
2178
2502
|
pageIndex: pagination.pageIndex,
|
|
@@ -2203,6 +2527,4 @@ null
|
|
|
2203
2527
|
}
|
|
2204
2528
|
}
|
|
2205
2529
|
|
|
2206
|
-
defineJQueryPlugin(DataGrid)
|
|
2207
|
-
|
|
2208
2530
|
export default DataGrid
|