@danielgindi/dgtable.js 2.0.7 → 2.0.9
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 +547 -282
- package/dist/SelectionHelper.d.ts +24 -0
- package/dist/SelectionHelper.d.ts.map +1 -0
- package/dist/by_column_filter.d.ts +14 -0
- package/dist/by_column_filter.d.ts.map +1 -0
- package/dist/cell_preview.d.ts +28 -0
- package/dist/cell_preview.d.ts.map +1 -0
- package/dist/column_collection.d.ts +41 -0
- package/dist/column_collection.d.ts.map +1 -0
- package/dist/column_resize.d.ts +25 -0
- package/dist/column_resize.d.ts.map +1 -0
- package/dist/constants.d.ts +19 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/header_events.d.ts +63 -0
- package/dist/header_events.d.ts.map +1 -0
- package/dist/helpers.d.ts +50 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/internal.d.ts +56 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/lib.cjs.js +6909 -3929
- package/dist/lib.cjs.js.map +1 -1
- package/dist/lib.cjs.min.js +2 -2
- package/dist/lib.cjs.min.js.map +1 -1
- package/dist/lib.es6.js +6912 -3932
- package/dist/lib.es6.js.map +1 -1
- package/dist/lib.es6.min.js +2 -2
- package/dist/lib.es6.min.js.map +1 -1
- package/dist/lib.umd.js +9251 -4346
- package/dist/lib.umd.js.map +1 -1
- package/dist/lib.umd.min.js +2 -2
- package/dist/lib.umd.min.js.map +1 -1
- package/dist/private_types.d.ts +145 -0
- package/dist/private_types.d.ts.map +1 -0
- package/dist/rendering.d.ts +57 -0
- package/dist/rendering.d.ts.map +1 -0
- package/dist/row_collection.d.ts +38 -0
- package/dist/row_collection.d.ts.map +1 -0
- package/dist/types.d.ts +221 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/util.d.ts +9 -0
- package/dist/util.d.ts.map +1 -0
- package/eslint.config.mjs +1 -0
- package/package.json +26 -12
- package/src/SelectionHelper.ts +90 -0
- package/src/by_column_filter.ts +36 -0
- package/src/cell_preview.ts +325 -0
- package/src/column_collection.ts +131 -0
- package/src/column_resize.ts +363 -0
- package/src/constants.ts +22 -0
- package/src/header_events.ts +369 -0
- package/src/helpers.ts +291 -0
- package/src/index.ts +1628 -0
- package/src/internal.ts +263 -0
- package/src/private_types.ts +156 -0
- package/src/rendering.ts +771 -0
- package/src/row_collection.ts +197 -0
- package/src/types.ts +265 -0
- package/src/util.ts +27 -0
- package/tsconfig.json +38 -0
- package/src/SelectionHelper.js +0 -65
- package/src/by_column_filter.js +0 -25
- package/src/column_collection.js +0 -153
- package/src/index.js +0 -3991
- package/src/row_collection.js +0 -183
- package/src/util.js +0 -17
package/src/index.ts
ADDED
|
@@ -0,0 +1,1628 @@
|
|
|
1
|
+
/* eslint-env browser */
|
|
2
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
3
|
+
|
|
4
|
+
import { htmlEncode } from './util';
|
|
5
|
+
import RowCollection from './row_collection';
|
|
6
|
+
import ColumnCollection from './column_collection';
|
|
7
|
+
// @ts-ignore - No type declarations available for this module
|
|
8
|
+
import { getScrollHorz, setScrollHorz } from '@danielgindi/dom-utils/lib/ScrollHelper.js';
|
|
9
|
+
// @ts-ignore - No type declarations available for this module
|
|
10
|
+
import { getElementHeight } from '@danielgindi/dom-utils/lib/Css.js';
|
|
11
|
+
// @ts-ignore - No type declarations available for this module
|
|
12
|
+
import { scopedSelectorAll } from '@danielgindi/dom-utils/lib/DomCompat.js';
|
|
13
|
+
import ByColumnFilter from './by_column_filter';
|
|
14
|
+
// @ts-ignore - No type declarations available for this module
|
|
15
|
+
import DomEventsSink from '@danielgindi/dom-utils/lib/DomEventsSink.js';
|
|
16
|
+
import mitt from 'mitt';
|
|
17
|
+
|
|
18
|
+
// Constants
|
|
19
|
+
import {
|
|
20
|
+
ColumnWidthMode,
|
|
21
|
+
Width,
|
|
22
|
+
} from './constants';
|
|
23
|
+
|
|
24
|
+
// Helpers
|
|
25
|
+
import {
|
|
26
|
+
getTextWidth,
|
|
27
|
+
calculateWidthAvailableForColumns,
|
|
28
|
+
calculateTbodyWidth,
|
|
29
|
+
serializeColumnWidth,
|
|
30
|
+
} from './helpers';
|
|
31
|
+
|
|
32
|
+
// Cell Preview
|
|
33
|
+
import {
|
|
34
|
+
cellMouseOverEvent,
|
|
35
|
+
cellMouseOutEvent,
|
|
36
|
+
hideCellPreview,
|
|
37
|
+
} from './cell_preview';
|
|
38
|
+
|
|
39
|
+
// Column Resize
|
|
40
|
+
import {
|
|
41
|
+
cancelColumnResize,
|
|
42
|
+
} from './column_resize';
|
|
43
|
+
|
|
44
|
+
// Header Events
|
|
45
|
+
import {
|
|
46
|
+
onDragEndColumnHeader,
|
|
47
|
+
} from './header_events';
|
|
48
|
+
|
|
49
|
+
// Rendering
|
|
50
|
+
import {
|
|
51
|
+
renderSkeletonBase,
|
|
52
|
+
renderSkeletonBody,
|
|
53
|
+
renderSkeletonHeaderCells,
|
|
54
|
+
destroyHeaderCells,
|
|
55
|
+
updateVirtualHeight,
|
|
56
|
+
updateLastCellWidthFromScrollbar,
|
|
57
|
+
updateTableWidth,
|
|
58
|
+
resizeColumnElements,
|
|
59
|
+
clearSortArrows,
|
|
60
|
+
showSortArrow,
|
|
61
|
+
} from './rendering';
|
|
62
|
+
|
|
63
|
+
// Internal helpers (not exposed on class)
|
|
64
|
+
import {
|
|
65
|
+
setupHovers,
|
|
66
|
+
parseColumnWidth,
|
|
67
|
+
initColumnFromData,
|
|
68
|
+
ensureVisibleColumns,
|
|
69
|
+
refilter,
|
|
70
|
+
getHtmlForCell,
|
|
71
|
+
} from './internal';
|
|
72
|
+
|
|
73
|
+
// Types
|
|
74
|
+
import {
|
|
75
|
+
DGTableOptions,
|
|
76
|
+
RowData,
|
|
77
|
+
ColumnSortOptions,
|
|
78
|
+
FilterFunction,
|
|
79
|
+
ColumnOptions,
|
|
80
|
+
CellFormatter,
|
|
81
|
+
HeaderCellFormatter,
|
|
82
|
+
OnComparatorRequired,
|
|
83
|
+
CustomSortingProvider,
|
|
84
|
+
SerializedColumn,
|
|
85
|
+
SerializedColumnSort,
|
|
86
|
+
DGTableEventMap,
|
|
87
|
+
} from './types';
|
|
88
|
+
|
|
89
|
+
// Private types
|
|
90
|
+
import type {
|
|
91
|
+
DGTableInternalOptions,
|
|
92
|
+
DGTablePrivateState,
|
|
93
|
+
} from './private_types';
|
|
94
|
+
import {
|
|
95
|
+
IsSafeSymbol,
|
|
96
|
+
} from './private_types';
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
100
|
+
|
|
101
|
+
// noinspection JSUnusedGlobalSymbols
|
|
102
|
+
class DGTable {
|
|
103
|
+
// Static properties
|
|
104
|
+
static VERSION = '@@VERSION';
|
|
105
|
+
static Width = Width;
|
|
106
|
+
|
|
107
|
+
// Instance properties
|
|
108
|
+
VERSION: string;
|
|
109
|
+
el!: HTMLElement;
|
|
110
|
+
_o!: DGTableInternalOptions;
|
|
111
|
+
_p!: DGTablePrivateState;
|
|
112
|
+
__removed?: boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param options - initialization options
|
|
116
|
+
*/
|
|
117
|
+
constructor(options?: DGTableOptions) {
|
|
118
|
+
this.VERSION = DGTable.VERSION;
|
|
119
|
+
|
|
120
|
+
const o = this._o = {} as DGTableInternalOptions;
|
|
121
|
+
const p = this._p = {
|
|
122
|
+
eventsSink: new DomEventsSink(),
|
|
123
|
+
mitt: mitt(),
|
|
124
|
+
tableSkeletonNeedsRendering: true,
|
|
125
|
+
} as DGTablePrivateState;
|
|
126
|
+
|
|
127
|
+
this.el = (options.el && options.el instanceof HTMLElement) ? options.el : document.createElement('div');
|
|
128
|
+
|
|
129
|
+
if (this.el !== options.el) {
|
|
130
|
+
this.el.classList.add(options.className || 'dgtable-wrapper');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
p.eventsSink.add(this.el, 'dragend.colresize', (e: Event) => onDragEndColumnHeader(this, e as DragEvent));
|
|
134
|
+
|
|
135
|
+
// Initialize options with defaults
|
|
136
|
+
o.virtualTable = options.virtualTable === undefined ? true : !!options.virtualTable;
|
|
137
|
+
o.estimatedRowHeight = options.estimatedRowHeight || undefined;
|
|
138
|
+
o.rowsBufferSize = options.rowsBufferSize || 3;
|
|
139
|
+
o.minColumnWidth = Math.max(options.minColumnWidth || 35, 0);
|
|
140
|
+
o.resizeAreaWidth = options.resizeAreaWidth || 8;
|
|
141
|
+
o.resizableColumns = options.resizableColumns === undefined ? true : !!options.resizableColumns;
|
|
142
|
+
o.movableColumns = options.movableColumns === undefined ? true : !!options.movableColumns;
|
|
143
|
+
o.sortableColumns = options.sortableColumns === undefined ? 1 : (parseInt(String(options.sortableColumns), 10) || 1);
|
|
144
|
+
o.adjustColumnWidthForSortArrow = options.adjustColumnWidthForSortArrow === undefined ? true : !!options.adjustColumnWidthForSortArrow;
|
|
145
|
+
o.convertColumnWidthsToRelative = options.convertColumnWidthsToRelative === undefined ? false : !!options.convertColumnWidthsToRelative;
|
|
146
|
+
o.autoFillTableWidth = options.autoFillTableWidth === undefined ? false : !!options.autoFillTableWidth;
|
|
147
|
+
o.allowCancelSort = options.allowCancelSort === undefined ? true : !!options.allowCancelSort;
|
|
148
|
+
o.cellClasses = options.cellClasses === undefined ? '' : options.cellClasses;
|
|
149
|
+
o.resizerClassName = options.resizerClassName === undefined ? 'dgtable-resize' : options.resizerClassName;
|
|
150
|
+
o.tableClassName = options.tableClassName === undefined ? 'dgtable' : options.tableClassName;
|
|
151
|
+
o.allowCellPreview = options.allowCellPreview === undefined ? true : options.allowCellPreview;
|
|
152
|
+
o.allowHeaderCellPreview = options.allowHeaderCellPreview === undefined ? true : options.allowHeaderCellPreview;
|
|
153
|
+
o.cellPreviewClassName = options.cellPreviewClassName === undefined ? 'dgtable-cell-preview' : options.cellPreviewClassName;
|
|
154
|
+
o.cellPreviewAutoBackground = options.cellPreviewAutoBackground === undefined ? true : options.cellPreviewAutoBackground;
|
|
155
|
+
o.onComparatorRequired = options.onComparatorRequired === undefined ? null : options.onComparatorRequired;
|
|
156
|
+
o.customSortingProvider = options.customSortingProvider === undefined ? null : options.customSortingProvider;
|
|
157
|
+
o.width = options.width === undefined ? Width.NONE : options.width;
|
|
158
|
+
o.relativeWidthGrowsToFillWidth = options.relativeWidthGrowsToFillWidth === undefined ? true : !!options.relativeWidthGrowsToFillWidth;
|
|
159
|
+
o.relativeWidthShrinksToFillWidth = options.relativeWidthShrinksToFillWidth === undefined ? false : !!options.relativeWidthShrinksToFillWidth;
|
|
160
|
+
|
|
161
|
+
this.setCellFormatter(options.cellFormatter);
|
|
162
|
+
this.setHeaderCellFormatter(options.headerCellFormatter);
|
|
163
|
+
this.setFilter(options.filter);
|
|
164
|
+
|
|
165
|
+
o.height = options.height;
|
|
166
|
+
|
|
167
|
+
// Prepare columns
|
|
168
|
+
this.setColumns(options.columns || [], false);
|
|
169
|
+
|
|
170
|
+
// Set sorting columns
|
|
171
|
+
let sortColumns = [];
|
|
172
|
+
|
|
173
|
+
if (options.sortColumn) {
|
|
174
|
+
|
|
175
|
+
let tmpSortColumns: (string | ColumnSortOptions)[] = Array.isArray(options.sortColumn)
|
|
176
|
+
? options.sortColumn
|
|
177
|
+
: [options.sortColumn];
|
|
178
|
+
|
|
179
|
+
for (let i = 0, len = tmpSortColumns.length; i < len; i++) {
|
|
180
|
+
let sortColumn = tmpSortColumns[i];
|
|
181
|
+
if (typeof sortColumn === 'string') {
|
|
182
|
+
sortColumn = { column: sortColumn, descending: false };
|
|
183
|
+
}
|
|
184
|
+
let col = p.columns.get(sortColumn.column);
|
|
185
|
+
if (!col) continue;
|
|
186
|
+
|
|
187
|
+
sortColumns.push({
|
|
188
|
+
column: sortColumn.column,
|
|
189
|
+
comparePath: col.comparePath || col.dataPath,
|
|
190
|
+
descending: sortColumn.descending ?? false,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
p.rows = new RowCollection({ sortColumn: sortColumns });
|
|
196
|
+
p.rows.onComparatorRequired = (column, descending, defaultComparator) => {
|
|
197
|
+
if (o.onComparatorRequired) {
|
|
198
|
+
return o.onComparatorRequired(column, descending, defaultComparator);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
p.rows.customSortingProvider = (data, sort) => {
|
|
202
|
+
if (o.customSortingProvider) {
|
|
203
|
+
return o.customSortingProvider(data, sort);
|
|
204
|
+
} else {
|
|
205
|
+
return sort(data);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
p.filteredRows = null;
|
|
210
|
+
|
|
211
|
+
p.scrollbarWidth = 0;
|
|
212
|
+
p._lastVirtualScrollHeight = 0;
|
|
213
|
+
|
|
214
|
+
setupHovers(this);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// =========================================================================
|
|
218
|
+
// PUBLIC API - Events
|
|
219
|
+
// =========================================================================
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Register an event handler.
|
|
223
|
+
* Built-in events have typed handlers. Custom events use `unknown` data type.
|
|
224
|
+
*/
|
|
225
|
+
on<K extends keyof DGTableEventMap>(event: K, handler: (value: DGTableEventMap[K]) => void): this;
|
|
226
|
+
on<T = unknown>(event: string & {}, handler: (value: T) => void): this;
|
|
227
|
+
on(event: string, handler: (value: unknown) => void) {
|
|
228
|
+
this._p.mitt.on(event, handler);
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Register a one-time event handler.
|
|
234
|
+
* Built-in events have typed handlers. Custom events use `unknown` data type.
|
|
235
|
+
*/
|
|
236
|
+
once<K extends keyof DGTableEventMap>(event: K, handler: (value: DGTableEventMap[K]) => void): this;
|
|
237
|
+
once<T = unknown>(event: string & {}, handler: (value: T) => void): this;
|
|
238
|
+
once(event: string, handler: (value: unknown) => void) {
|
|
239
|
+
const wrapped = (value: unknown) => {
|
|
240
|
+
this._p.mitt.off(event, wrapped);
|
|
241
|
+
handler(value);
|
|
242
|
+
};
|
|
243
|
+
this._p.mitt.on(event, wrapped);
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Remove a handler for an event, all handlers for an event, or all handlers completely.
|
|
249
|
+
* Built-in events have typed handlers. Custom events use `unknown` data type.
|
|
250
|
+
*/
|
|
251
|
+
off<K extends keyof DGTableEventMap>(event?: K, handler?: (value: DGTableEventMap[K]) => void): this;
|
|
252
|
+
off<T = unknown>(event?: string & {}, handler?: (value: T) => void): this;
|
|
253
|
+
off(event?: string, handler?: (value: unknown) => void) {
|
|
254
|
+
if (!event && !handler) {
|
|
255
|
+
this._p.mitt.all.clear();
|
|
256
|
+
} else {
|
|
257
|
+
this._p.mitt.off(event, handler);
|
|
258
|
+
}
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Emit an event.
|
|
264
|
+
* Built-in events have typed data. Custom events accept any data type.
|
|
265
|
+
*/
|
|
266
|
+
emit<K extends keyof DGTableEventMap>(event: K, value?: DGTableEventMap[K]): this;
|
|
267
|
+
emit<T = unknown>(event: string & {}, value?: T): this;
|
|
268
|
+
emit(event: string, value?: unknown) {
|
|
269
|
+
this._p.mitt.emit(event, value);
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// =========================================================================
|
|
274
|
+
// PUBLIC API - Lifecycle
|
|
275
|
+
// =========================================================================
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Destroy, releasing all memory, events and DOM elements
|
|
279
|
+
*/
|
|
280
|
+
destroy() {
|
|
281
|
+
const p = this._p;
|
|
282
|
+
const el = this.el;
|
|
283
|
+
|
|
284
|
+
if (this.__removed || !p) {
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (p.resizer) {
|
|
289
|
+
p.resizer.remove();
|
|
290
|
+
p.resizer = null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
p.virtualListHelper?.destroy();
|
|
294
|
+
p.virtualListHelper = null;
|
|
295
|
+
|
|
296
|
+
destroyHeaderCells(this);
|
|
297
|
+
|
|
298
|
+
p.table?.remove();
|
|
299
|
+
p.tbody?.remove();
|
|
300
|
+
|
|
301
|
+
if (p.workerListeners) {
|
|
302
|
+
for (let j = 0; j < p.workerListeners.length; j++) {
|
|
303
|
+
let worker = p.workerListeners[j];
|
|
304
|
+
worker.worker.removeEventListener('message', worker.listener, false);
|
|
305
|
+
}
|
|
306
|
+
p.workerListeners.length = 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
p.rows.length = p.columns.length = 0;
|
|
310
|
+
|
|
311
|
+
if (p._deferredRender) {
|
|
312
|
+
clearTimeout(p._deferredRender);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Cleanup
|
|
316
|
+
for (let prop in this) {
|
|
317
|
+
if (hasOwnProperty.call(this, prop)) {
|
|
318
|
+
this[prop] = null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this.__removed = true;
|
|
323
|
+
|
|
324
|
+
if (el) {
|
|
325
|
+
el.remove();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Backwards compatibility
|
|
332
|
+
close() {
|
|
333
|
+
this.destroy();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Backwards compatibility
|
|
337
|
+
remove() {
|
|
338
|
+
this.destroy();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// =========================================================================
|
|
342
|
+
// PUBLIC API - Rendering
|
|
343
|
+
// =========================================================================
|
|
344
|
+
|
|
345
|
+
/** Render the table */
|
|
346
|
+
render() {
|
|
347
|
+
const o = this._o, p = this._p;
|
|
348
|
+
|
|
349
|
+
if (!this.el.offsetParent) {
|
|
350
|
+
if (!p._deferredRender) {
|
|
351
|
+
p._deferredRender = setTimeout(() => {
|
|
352
|
+
p._deferredRender = null;
|
|
353
|
+
if (!this.__removed && this.el.offsetParent) {
|
|
354
|
+
this.render();
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (p.tableSkeletonNeedsRendering === true) {
|
|
363
|
+
p.tableSkeletonNeedsRendering = false;
|
|
364
|
+
|
|
365
|
+
if (o.width === Width.AUTO) {
|
|
366
|
+
clearSortArrows(this);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
let lastScrollTop = p.table && p.table.parentNode ? p.table.scrollTop : NaN,
|
|
370
|
+
lastScrollHorz = p.table && p.table.parentNode ? getScrollHorz(p.table) : NaN;
|
|
371
|
+
|
|
372
|
+
renderSkeletonBase(this);
|
|
373
|
+
renderSkeletonBody(this);
|
|
374
|
+
this.tableWidthChanged(true, false);
|
|
375
|
+
renderSkeletonHeaderCells(this);
|
|
376
|
+
|
|
377
|
+
p.virtualListHelper.setCount((p.filteredRows ?? p.rows).length);
|
|
378
|
+
|
|
379
|
+
updateVirtualHeight(this);
|
|
380
|
+
updateLastCellWidthFromScrollbar(this, true);
|
|
381
|
+
updateTableWidth(this, true);
|
|
382
|
+
|
|
383
|
+
// Show sort arrows
|
|
384
|
+
for (let i = 0; i < p.rows.sortColumn.length; i++) {
|
|
385
|
+
showSortArrow(this, p.rows.sortColumn[i].column, p.rows.sortColumn[i].descending);
|
|
386
|
+
}
|
|
387
|
+
if (o.adjustColumnWidthForSortArrow && p.rows.sortColumn.length) {
|
|
388
|
+
this.tableWidthChanged(true);
|
|
389
|
+
} else if (!o.virtualTable) {
|
|
390
|
+
this.tableWidthChanged();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (!isNaN(lastScrollTop))
|
|
394
|
+
p.table.scrollTop = lastScrollTop;
|
|
395
|
+
|
|
396
|
+
if (!isNaN(lastScrollHorz)) {
|
|
397
|
+
setScrollHorz(p.table, lastScrollHorz);
|
|
398
|
+
setScrollHorz(p.header, lastScrollHorz);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.emit('renderskeleton');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
p.virtualListHelper.render();
|
|
405
|
+
|
|
406
|
+
this.emit('render');
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** Forces a full render of the table */
|
|
411
|
+
clearAndRender(render?: boolean) {
|
|
412
|
+
let p = this._p;
|
|
413
|
+
|
|
414
|
+
p.tableSkeletonNeedsRendering = true;
|
|
415
|
+
p.notifyRendererOfColumnsConfig?.();
|
|
416
|
+
|
|
417
|
+
if (render === undefined || render) {
|
|
418
|
+
this.render();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// =========================================================================
|
|
425
|
+
// PUBLIC API - Columns
|
|
426
|
+
// =========================================================================
|
|
427
|
+
|
|
428
|
+
/** Sets the columns of the table */
|
|
429
|
+
setColumns(columns?: ColumnOptions[] | null, render?: boolean) {
|
|
430
|
+
const p = this._p;
|
|
431
|
+
|
|
432
|
+
columns = columns || [];
|
|
433
|
+
|
|
434
|
+
let normalizedCols = new ColumnCollection();
|
|
435
|
+
for (let i = 0, order = 0; i < columns.length; i++) {
|
|
436
|
+
|
|
437
|
+
let columnData = columns[i];
|
|
438
|
+
let normalizedColumn = initColumnFromData(this._o, columnData);
|
|
439
|
+
|
|
440
|
+
if (columnData.order !== undefined) {
|
|
441
|
+
if (columnData.order > order) {
|
|
442
|
+
order = columnData.order + 1;
|
|
443
|
+
}
|
|
444
|
+
normalizedColumn.order = columnData.order;
|
|
445
|
+
} else {
|
|
446
|
+
normalizedColumn.order = order++;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
normalizedCols.push(normalizedColumn);
|
|
450
|
+
}
|
|
451
|
+
normalizedCols.normalizeOrder();
|
|
452
|
+
|
|
453
|
+
p.columns = normalizedCols;
|
|
454
|
+
p.visibleColumns = normalizedCols.getVisibleColumns();
|
|
455
|
+
|
|
456
|
+
ensureVisibleColumns(this);
|
|
457
|
+
this.clearAndRender(render);
|
|
458
|
+
|
|
459
|
+
return this;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** Add a column to the table */
|
|
463
|
+
addColumn(columnData: ColumnOptions, before?: string | number, render?: boolean) {
|
|
464
|
+
const p = this._p;
|
|
465
|
+
let columns = p.columns;
|
|
466
|
+
|
|
467
|
+
if (columnData && !columns.get(columnData.name)) {
|
|
468
|
+
let beforeColumn = null;
|
|
469
|
+
if (before !== undefined) {
|
|
470
|
+
beforeColumn = typeof before === 'string'
|
|
471
|
+
? columns.get(before)
|
|
472
|
+
: columns.getByOrder(before);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
let column = initColumnFromData(this._o, columnData);
|
|
476
|
+
column.order = beforeColumn ? beforeColumn.order : (columns.getMaxOrder() + 1);
|
|
477
|
+
|
|
478
|
+
for (let i = columns.getMaxOrder(), to = column.order; i >= to; i--) {
|
|
479
|
+
let col = columns.getByOrder(i);
|
|
480
|
+
if (col) {
|
|
481
|
+
col.order++;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
columns.push(column);
|
|
486
|
+
columns.normalizeOrder();
|
|
487
|
+
|
|
488
|
+
p.visibleColumns = columns.getVisibleColumns();
|
|
489
|
+
ensureVisibleColumns(this);
|
|
490
|
+
this.clearAndRender(render);
|
|
491
|
+
|
|
492
|
+
this.emit('addcolumn', column.name);
|
|
493
|
+
}
|
|
494
|
+
return this;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Remove a column from the table */
|
|
498
|
+
removeColumn(column: string, render?: boolean) {
|
|
499
|
+
const p = this._p;
|
|
500
|
+
let columns = p.columns;
|
|
501
|
+
|
|
502
|
+
let colIdx = columns.indexOf(column);
|
|
503
|
+
if (colIdx > -1) {
|
|
504
|
+
columns.splice(colIdx, 1);
|
|
505
|
+
columns.normalizeOrder();
|
|
506
|
+
|
|
507
|
+
p.visibleColumns = columns.getVisibleColumns();
|
|
508
|
+
ensureVisibleColumns(this);
|
|
509
|
+
this.clearAndRender(render);
|
|
510
|
+
|
|
511
|
+
this.emit('removecolumn', column);
|
|
512
|
+
}
|
|
513
|
+
return this;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Set a new label to a column */
|
|
517
|
+
setColumnLabel(column: string, label: string) {
|
|
518
|
+
const p = this._p;
|
|
519
|
+
|
|
520
|
+
let col = p.columns.get(column);
|
|
521
|
+
if (col) {
|
|
522
|
+
col.label = label === undefined ? col.name : label;
|
|
523
|
+
|
|
524
|
+
if (col.element) {
|
|
525
|
+
for (let i = 0; i < col.element.firstChild.childNodes.length; i++) {
|
|
526
|
+
let node = col.element.firstChild.childNodes[i];
|
|
527
|
+
if (node.nodeType === 3) {
|
|
528
|
+
node.textContent = col.label;
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return this;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/** Move a column to a new position */
|
|
538
|
+
moveColumn(src: string | number, dest: string | number, visibleOnly = true) {
|
|
539
|
+
const o = this._o, p = this._p;
|
|
540
|
+
|
|
541
|
+
let columns = p.columns,
|
|
542
|
+
col, destCol;
|
|
543
|
+
|
|
544
|
+
let columnsArray = visibleOnly ? p.visibleColumns : columns.getColumns();
|
|
545
|
+
|
|
546
|
+
if (typeof src === 'string') {
|
|
547
|
+
col = columns.get(src);
|
|
548
|
+
} else if (typeof src === 'number') {
|
|
549
|
+
col = columnsArray[src];
|
|
550
|
+
}
|
|
551
|
+
if (typeof dest === 'string') {
|
|
552
|
+
destCol = columns.get(dest);
|
|
553
|
+
} else if (typeof dest === 'number') {
|
|
554
|
+
destCol = columnsArray[dest];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (col && destCol && src !== dest) {
|
|
558
|
+
let srcOrder = col.order, destOrder = destCol.order;
|
|
559
|
+
|
|
560
|
+
let visibleColumns = columns.moveColumn(col, destCol).getVisibleColumns();
|
|
561
|
+
|
|
562
|
+
if (p.visibleColumns.length !== visibleColumns.length ||
|
|
563
|
+
p.visibleColumns.some((x, i) => x !== visibleColumns[i])) {
|
|
564
|
+
|
|
565
|
+
p.visibleColumns = visibleColumns;
|
|
566
|
+
ensureVisibleColumns(this);
|
|
567
|
+
|
|
568
|
+
if (o.virtualTable) {
|
|
569
|
+
this.clearAndRender();
|
|
570
|
+
} else {
|
|
571
|
+
const headerCells = scopedSelectorAll(p.headerRow, `>div.${o.tableClassName}-header-cell`);
|
|
572
|
+
let beforePos = srcOrder < destOrder ? destOrder + 1 : destOrder,
|
|
573
|
+
fromPos = srcOrder;
|
|
574
|
+
headerCells[0].parentNode.insertBefore(headerCells[fromPos], headerCells[beforePos]);
|
|
575
|
+
|
|
576
|
+
let srcCol = p.visibleColumns[srcOrder];
|
|
577
|
+
let srcWidth = ((srcCol.actualWidthConsideringScrollbarWidth || srcCol.actualWidth) ?? 0) + 'px';
|
|
578
|
+
let destCol = p.visibleColumns[destOrder];
|
|
579
|
+
let destWidth = ((destCol.actualWidthConsideringScrollbarWidth || destCol.actualWidth) ?? 0) + 'px';
|
|
580
|
+
|
|
581
|
+
let tbodyChildren = p.tbody.childNodes;
|
|
582
|
+
for (let i = 0, count = tbodyChildren.length; i < count; i++) {
|
|
583
|
+
let row = tbodyChildren[i] as HTMLElement;
|
|
584
|
+
if (row.nodeType !== 1) continue;
|
|
585
|
+
row.insertBefore(row.childNodes[fromPos], row.childNodes[beforePos]);
|
|
586
|
+
((row.childNodes[destOrder] as HTMLElement).firstChild as HTMLElement).style.width = destWidth;
|
|
587
|
+
((row.childNodes[srcOrder] as HTMLElement).firstChild as HTMLElement).style.width = srcWidth;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
this.emit('movecolumn', { name: col.name, src: srcOrder, dest: destOrder });
|
|
593
|
+
}
|
|
594
|
+
return this;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** Show or hide a column */
|
|
598
|
+
setColumnVisible(column: string, visible: boolean) {
|
|
599
|
+
const p = this._p;
|
|
600
|
+
|
|
601
|
+
let col = p.columns.get(column);
|
|
602
|
+
|
|
603
|
+
visible = !!visible;
|
|
604
|
+
|
|
605
|
+
if (col && !!col.visible !== visible) {
|
|
606
|
+
col.visible = visible;
|
|
607
|
+
p.visibleColumns = p.columns.getVisibleColumns();
|
|
608
|
+
this.emit(visible ? 'showcolumn' : 'hidecolumn', column);
|
|
609
|
+
ensureVisibleColumns(this);
|
|
610
|
+
this.clearAndRender();
|
|
611
|
+
}
|
|
612
|
+
return this;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** Get the visibility mode of a column */
|
|
616
|
+
isColumnVisible(column: string): boolean {
|
|
617
|
+
const p = this._p;
|
|
618
|
+
let col = p.columns.get(column);
|
|
619
|
+
if (col) {
|
|
620
|
+
return col.visible;
|
|
621
|
+
}
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** Globally set the minimum column width */
|
|
626
|
+
setMinColumnWidth(minColumnWidth: number) {
|
|
627
|
+
let o = this._o;
|
|
628
|
+
minColumnWidth = Math.max(minColumnWidth, 0);
|
|
629
|
+
if (o.minColumnWidth !== minColumnWidth) {
|
|
630
|
+
o.minColumnWidth = minColumnWidth;
|
|
631
|
+
this.tableWidthChanged(true);
|
|
632
|
+
}
|
|
633
|
+
return this;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Get the current minimum column width */
|
|
637
|
+
getMinColumnWidth(): number {
|
|
638
|
+
return this._o.minColumnWidth;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** Set a new width to a column */
|
|
642
|
+
setColumnWidth(column: string, width: number | string) {
|
|
643
|
+
const p = this._p;
|
|
644
|
+
|
|
645
|
+
let col = p.columns.get(column);
|
|
646
|
+
|
|
647
|
+
let parsedWidth = parseColumnWidth(width, col.ignoreMin ? 0 : this._o.minColumnWidth);
|
|
648
|
+
|
|
649
|
+
if (col) {
|
|
650
|
+
let oldWidth = serializeColumnWidth(col);
|
|
651
|
+
|
|
652
|
+
col.width = parsedWidth.width;
|
|
653
|
+
col.widthMode = parsedWidth.mode;
|
|
654
|
+
|
|
655
|
+
let newWidth = serializeColumnWidth(col);
|
|
656
|
+
|
|
657
|
+
if (oldWidth !== newWidth) {
|
|
658
|
+
this.tableWidthChanged(true);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
this.emit('columnwidth', { name: col.name, width: newWidth, oldWidth: oldWidth });
|
|
662
|
+
}
|
|
663
|
+
return this;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/** Get the serialized width of the specified column */
|
|
667
|
+
getColumnWidth(column: string): string | number | null {
|
|
668
|
+
const p = this._p;
|
|
669
|
+
|
|
670
|
+
let col = p.columns.get(column);
|
|
671
|
+
if (col) {
|
|
672
|
+
return serializeColumnWidth(col);
|
|
673
|
+
}
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/** Get configuration for a specific column */
|
|
678
|
+
getColumnConfig(column: string): SerializedColumn | null {
|
|
679
|
+
const p = this._p;
|
|
680
|
+
let col = p.columns.get(column);
|
|
681
|
+
if (col) {
|
|
682
|
+
return {
|
|
683
|
+
'order': col.order,
|
|
684
|
+
'width': serializeColumnWidth(col),
|
|
685
|
+
'visible': col.visible,
|
|
686
|
+
'label': col.label,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/** Returns a config object for all columns */
|
|
693
|
+
getColumnsConfig() {
|
|
694
|
+
const p = this._p;
|
|
695
|
+
|
|
696
|
+
let config: Record<string, ReturnType<typeof this.getColumnConfig>> = {};
|
|
697
|
+
for (let i = 0; i < p.columns.length; i++) {
|
|
698
|
+
config[p.columns[i].name] = this.getColumnConfig(p.columns[i].name);
|
|
699
|
+
}
|
|
700
|
+
return config;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// =========================================================================
|
|
704
|
+
// PUBLIC API - Sorting
|
|
705
|
+
// =========================================================================
|
|
706
|
+
|
|
707
|
+
/** Set the limit on concurrent columns sorted */
|
|
708
|
+
setSortableColumns(sortableColumns: number) {
|
|
709
|
+
const p = this._p, o = this._o;
|
|
710
|
+
if (o.sortableColumns !== sortableColumns) {
|
|
711
|
+
o.sortableColumns = sortableColumns;
|
|
712
|
+
if (p.table) {
|
|
713
|
+
const headerCells = scopedSelectorAll(p.headerRow, `>div.${o.tableClassName}-header-cell`);
|
|
714
|
+
for (let i = 0, len = headerCells.length; i < len; i++) {
|
|
715
|
+
const cell = headerCells[i];
|
|
716
|
+
cell.classList[(o.sortableColumns > 0 && p.visibleColumns[i].sortable) ? 'add' : 'remove']('sortable');
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return this;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/** Get the limit on concurrent columns sorted */
|
|
724
|
+
getSortableColumns(): number {
|
|
725
|
+
return this._o.sortableColumns;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** Set whether columns are movable */
|
|
729
|
+
setMovableColumns(movableColumns?: boolean) {
|
|
730
|
+
let o = this._o;
|
|
731
|
+
movableColumns = movableColumns === undefined ? true : !!movableColumns;
|
|
732
|
+
if (o.movableColumns !== movableColumns) {
|
|
733
|
+
o.movableColumns = movableColumns;
|
|
734
|
+
}
|
|
735
|
+
return this;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** Get whether columns are movable */
|
|
739
|
+
getMovableColumns(): boolean {
|
|
740
|
+
return this._o.movableColumns;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/** Set whether columns are resizable */
|
|
744
|
+
setResizableColumns(resizableColumns?: boolean) {
|
|
745
|
+
let o = this._o;
|
|
746
|
+
resizableColumns = resizableColumns === undefined ? true : !!resizableColumns;
|
|
747
|
+
if (o.resizableColumns !== resizableColumns) {
|
|
748
|
+
o.resizableColumns = resizableColumns;
|
|
749
|
+
}
|
|
750
|
+
return this;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** Get whether columns are resizable */
|
|
754
|
+
getResizableColumns(): boolean {
|
|
755
|
+
return this._o.resizableColumns;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/** Sets a function that supplies comparators dynamically */
|
|
759
|
+
setOnComparatorRequired(comparatorProvider: OnComparatorRequired | null) {
|
|
760
|
+
let o = this._o;
|
|
761
|
+
if (o.onComparatorRequired !== comparatorProvider) {
|
|
762
|
+
o.onComparatorRequired = comparatorProvider;
|
|
763
|
+
}
|
|
764
|
+
return this;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** Sets custom sorting function for a data set */
|
|
768
|
+
setCustomSortingProvider(customSortingProvider: CustomSortingProvider | null) {
|
|
769
|
+
let o = this._o;
|
|
770
|
+
if (o.customSortingProvider !== customSortingProvider) {
|
|
771
|
+
o.customSortingProvider = customSortingProvider;
|
|
772
|
+
}
|
|
773
|
+
return this;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/** Sort the table by column */
|
|
777
|
+
sort(column?: string, descending?: boolean, add?: boolean) {
|
|
778
|
+
const o = this._o, p = this._p;
|
|
779
|
+
|
|
780
|
+
let columns = p.columns,
|
|
781
|
+
col = columns.get(column);
|
|
782
|
+
|
|
783
|
+
let currentSort = p.rows.sortColumn;
|
|
784
|
+
|
|
785
|
+
if (col) {
|
|
786
|
+
if (add) {
|
|
787
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
788
|
+
if (currentSort[i].column === col.name) {
|
|
789
|
+
if (i < currentSort.length - 1) {
|
|
790
|
+
currentSort.length = 0;
|
|
791
|
+
} else {
|
|
792
|
+
descending = currentSort[currentSort.length - 1].descending;
|
|
793
|
+
currentSort.splice(currentSort.length - 1, 1);
|
|
794
|
+
}
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if ((o.sortableColumns > 0 && currentSort.length >= o.sortableColumns) || currentSort.length >= p.visibleColumns.length) {
|
|
799
|
+
currentSort.length = 0;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
} else {
|
|
803
|
+
currentSort.length = 0;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
descending = descending === undefined ? false : descending;
|
|
807
|
+
|
|
808
|
+
currentSort.push({
|
|
809
|
+
column: col.name,
|
|
810
|
+
comparePath: col.comparePath || col.dataPath,
|
|
811
|
+
descending: !!descending,
|
|
812
|
+
});
|
|
813
|
+
} else {
|
|
814
|
+
currentSort.length = 0;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
clearSortArrows(this);
|
|
818
|
+
|
|
819
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
820
|
+
showSortArrow(this, currentSort[i].column, currentSort[i].descending);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (o.adjustColumnWidthForSortArrow && !p.tableSkeletonNeedsRendering) {
|
|
824
|
+
this.tableWidthChanged(true);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
p.rows.sortColumn = currentSort;
|
|
828
|
+
|
|
829
|
+
if (currentSort.length) {
|
|
830
|
+
p.rows.sort();
|
|
831
|
+
if (p.filteredRows) {
|
|
832
|
+
p.filteredRows.sort();
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (p.virtualListHelper)
|
|
837
|
+
p.virtualListHelper.invalidate().render();
|
|
838
|
+
|
|
839
|
+
let sorts = [];
|
|
840
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
841
|
+
sorts.push({ 'column': currentSort[i].column, 'descending': currentSort[i].descending });
|
|
842
|
+
}
|
|
843
|
+
this.emit('sort', { sorts: sorts });
|
|
844
|
+
|
|
845
|
+
return this;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/** Re-sort the table using current sort specifiers */
|
|
849
|
+
resort() {
|
|
850
|
+
const p = this._p;
|
|
851
|
+
let columns = p.columns;
|
|
852
|
+
|
|
853
|
+
let currentSort = p.rows.sortColumn;
|
|
854
|
+
if (currentSort.length) {
|
|
855
|
+
|
|
856
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
857
|
+
if (!columns.get(currentSort[i].column)) {
|
|
858
|
+
currentSort.splice(i--, 1);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
p.rows.sortColumn = currentSort;
|
|
863
|
+
if (currentSort.length) {
|
|
864
|
+
p.rows.sort();
|
|
865
|
+
if (p.filteredRows) {
|
|
866
|
+
p.filteredRows.sort();
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
let sorts = [];
|
|
871
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
872
|
+
sorts.push({ 'column': currentSort[i].column, 'descending': currentSort[i].descending });
|
|
873
|
+
}
|
|
874
|
+
this.emit('sort', { sorts: sorts, resort: true });
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
return this;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/** Returns an array of the currently sorted columns */
|
|
881
|
+
getSortedColumns(): SerializedColumnSort[] {
|
|
882
|
+
const p = this._p;
|
|
883
|
+
|
|
884
|
+
let sorted = [];
|
|
885
|
+
for (let i = 0; i < p.rows.sortColumn.length; i++) {
|
|
886
|
+
let sort = p.rows.sortColumn[i];
|
|
887
|
+
sorted.push({ column: sort.column, descending: sort.descending });
|
|
888
|
+
}
|
|
889
|
+
return sorted;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// =========================================================================
|
|
893
|
+
// PUBLIC API - Formatters & Filters
|
|
894
|
+
// =========================================================================
|
|
895
|
+
|
|
896
|
+
/** Sets a new cell formatter */
|
|
897
|
+
setCellFormatter(formatter?: CellFormatter | null) {
|
|
898
|
+
if (!formatter) {
|
|
899
|
+
const defaultFormatter = (val: unknown) => (typeof val === 'string') ? htmlEncode(val) : val;
|
|
900
|
+
(defaultFormatter as unknown as Record<symbol, boolean>)[IsSafeSymbol] = true;
|
|
901
|
+
formatter = defaultFormatter as unknown as CellFormatter;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
this._o.cellFormatter = formatter;
|
|
905
|
+
|
|
906
|
+
return this;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/** Sets a new header cell formatter */
|
|
910
|
+
setHeaderCellFormatter(formatter?: HeaderCellFormatter | null) {
|
|
911
|
+
this._o.headerCellFormatter = formatter || function (val: string) {
|
|
912
|
+
return (typeof val === 'string') ? htmlEncode(val) : val;
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
return this;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** Set the filter function */
|
|
919
|
+
setFilter(filterFunc?: FilterFunction | null) {
|
|
920
|
+
this._o.filter = filterFunc;
|
|
921
|
+
return this;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/** Filter the table rows */
|
|
925
|
+
filter(args?: unknown) {
|
|
926
|
+
const p = this._p;
|
|
927
|
+
|
|
928
|
+
let filterFunc = (this._o.filter || ByColumnFilter) as FilterFunction;
|
|
929
|
+
|
|
930
|
+
// Deprecated use of older by-column filter
|
|
931
|
+
if (typeof arguments[0] === 'string' && typeof arguments[1] === 'string') {
|
|
932
|
+
args = {
|
|
933
|
+
column: arguments[0],
|
|
934
|
+
keyword: arguments[1],
|
|
935
|
+
caseSensitive: arguments[2],
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
let hadFilter = !!p.filteredRows;
|
|
940
|
+
if (p.filteredRows) {
|
|
941
|
+
p.filteredRows = null;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
p.filterArgs = args == null ? null : ((typeof args === 'object' && !Array.isArray(args)) ? Object.assign({}, args) : args);
|
|
945
|
+
|
|
946
|
+
if (p.filterArgs !== null) {
|
|
947
|
+
p.filteredRows = p.rows.filteredCollection(filterFunc, p.filterArgs);
|
|
948
|
+
|
|
949
|
+
if (hadFilter || p.filteredRows) {
|
|
950
|
+
this.clearAndRender();
|
|
951
|
+
this.emit('filter', args);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
p.filterArgs = null;
|
|
956
|
+
p.filteredRows = null;
|
|
957
|
+
this.clearAndRender();
|
|
958
|
+
this.emit('filterclear', {});
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return this;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/** Clear the current filter */
|
|
965
|
+
clearFilter() {
|
|
966
|
+
const p = this._p;
|
|
967
|
+
|
|
968
|
+
if (p.filteredRows) {
|
|
969
|
+
p.filterArgs = null;
|
|
970
|
+
p.filteredRows = null;
|
|
971
|
+
this.clearAndRender();
|
|
972
|
+
this.emit('filterclear', {});
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return this;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// =========================================================================
|
|
979
|
+
// PUBLIC API - Row Operations
|
|
980
|
+
// =========================================================================
|
|
981
|
+
|
|
982
|
+
/** Returns the HTML string for a specific cell by row index */
|
|
983
|
+
getHtmlForRowCell(rowIndex: number, columnName: string): string | null {
|
|
984
|
+
const p = this._p;
|
|
985
|
+
|
|
986
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1) return null;
|
|
987
|
+
let column = p.columns.get(columnName);
|
|
988
|
+
if (!column) return null;
|
|
989
|
+
let rowData = p.rows[rowIndex];
|
|
990
|
+
|
|
991
|
+
return getHtmlForCell(this._o, rowData, column);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/** Returns the HTML string for a specific cell by row data */
|
|
995
|
+
getHtmlForRowDataCell(rowData: RowData, columnName: string): string | null {
|
|
996
|
+
const p = this._p;
|
|
997
|
+
|
|
998
|
+
let column = p.columns.get(columnName);
|
|
999
|
+
if (!column) return null;
|
|
1000
|
+
|
|
1001
|
+
return getHtmlForCell(this._o, rowData, column);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/** Returns the y position of a row by index */
|
|
1005
|
+
getRowYPos(rowIndex: number): number | null {
|
|
1006
|
+
const p = this._p;
|
|
1007
|
+
|
|
1008
|
+
return p.virtualListHelper.getItemPosition(rowIndex) || null;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/** Returns the row data for a specific row */
|
|
1012
|
+
getDataForRow(row: number): RowData | null {
|
|
1013
|
+
const p = this._p;
|
|
1014
|
+
|
|
1015
|
+
if (row < 0 || row > p.rows.length - 1) return null;
|
|
1016
|
+
return p.rows[row];
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/** Gets the number of rows */
|
|
1020
|
+
getRowCount(): number {
|
|
1021
|
+
const p = this._p;
|
|
1022
|
+
return p.rows ? p.rows.length : 0;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/** Returns the actual row index for specific row data */
|
|
1026
|
+
getIndexForRow(rowData: RowData): number {
|
|
1027
|
+
const p = this._p;
|
|
1028
|
+
return p.rows.indexOf(rowData);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/** Gets the number of filtered rows */
|
|
1032
|
+
getFilteredRowCount(): number {
|
|
1033
|
+
const p = this._p;
|
|
1034
|
+
return (p.filteredRows || p.rows).length;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/** Returns the filtered row index for specific row data */
|
|
1038
|
+
getIndexForFilteredRow(rowData: RowData): number {
|
|
1039
|
+
const p = this._p;
|
|
1040
|
+
return (p.filteredRows || p.rows).indexOf(rowData);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/** Returns the row data for a specific filtered row */
|
|
1044
|
+
getDataForFilteredRow(row: number): RowData | null {
|
|
1045
|
+
const p = this._p;
|
|
1046
|
+
if (row < 0 || row > (p.filteredRows || p.rows).length - 1) return null;
|
|
1047
|
+
return (p.filteredRows || p.rows)[row];
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/** Returns DOM element of the header row */
|
|
1051
|
+
getHeaderRowElement(): HTMLElement | undefined {
|
|
1052
|
+
return this._p.headerRow;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/** Add rows to the table */
|
|
1056
|
+
addRows(data: RowData | RowData[], at?: number | boolean, resort?: boolean, render?: boolean) {
|
|
1057
|
+
let p = this._p;
|
|
1058
|
+
|
|
1059
|
+
if (typeof at === 'boolean') {
|
|
1060
|
+
render = resort;
|
|
1061
|
+
resort = at;
|
|
1062
|
+
at = -1;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (typeof at !== 'number')
|
|
1066
|
+
at = -1;
|
|
1067
|
+
|
|
1068
|
+
if (at < 0 || at > p.rows.length)
|
|
1069
|
+
at = p.rows.length;
|
|
1070
|
+
|
|
1071
|
+
render = (render === undefined) ? true : !!render;
|
|
1072
|
+
|
|
1073
|
+
const dataArray = Array.isArray(data) ? data : [data];
|
|
1074
|
+
const dataCount = dataArray.length;
|
|
1075
|
+
|
|
1076
|
+
if (data) {
|
|
1077
|
+
p.rows.add(data, at);
|
|
1078
|
+
|
|
1079
|
+
if (p.filteredRows || (resort && p.rows.sortColumn.length)) {
|
|
1080
|
+
|
|
1081
|
+
if (resort && p.rows.sortColumn.length) {
|
|
1082
|
+
this.resort();
|
|
1083
|
+
} else {
|
|
1084
|
+
refilter(this);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
p.tableSkeletonNeedsRendering = true;
|
|
1088
|
+
|
|
1089
|
+
if (render) {
|
|
1090
|
+
// Render the skeleton with all rows from scratch
|
|
1091
|
+
this.render();
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
} else if (render) {
|
|
1095
|
+
p.virtualListHelper.addItemsAt(dataCount, at);
|
|
1096
|
+
|
|
1097
|
+
if (this._o.virtualTable) {
|
|
1098
|
+
updateVirtualHeight(this);
|
|
1099
|
+
updateLastCellWidthFromScrollbar(this);
|
|
1100
|
+
this.render();
|
|
1101
|
+
updateTableWidth(this, false);
|
|
1102
|
+
|
|
1103
|
+
} else if (p.tbody) {
|
|
1104
|
+
this.render();
|
|
1105
|
+
updateLastCellWidthFromScrollbar(this);
|
|
1106
|
+
updateTableWidth(this, true);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
this.emit('addrows', { count: dataCount, clear: false });
|
|
1111
|
+
}
|
|
1112
|
+
return this;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/** Removes rows from the table */
|
|
1116
|
+
removeRows(rowIndex: number, count: number, render?: boolean) {
|
|
1117
|
+
let p = this._p;
|
|
1118
|
+
|
|
1119
|
+
if (typeof count !== 'number' || count <= 0) return this;
|
|
1120
|
+
|
|
1121
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1) return this;
|
|
1122
|
+
|
|
1123
|
+
p.rows.splice(rowIndex, count);
|
|
1124
|
+
render = (render === undefined) ? true : !!render;
|
|
1125
|
+
|
|
1126
|
+
if (p.filteredRows) {
|
|
1127
|
+
refilter(this);
|
|
1128
|
+
|
|
1129
|
+
p.tableSkeletonNeedsRendering = true;
|
|
1130
|
+
|
|
1131
|
+
if (render) {
|
|
1132
|
+
// Render the skeleton with all rows from scratch
|
|
1133
|
+
this.render();
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
} else if (render) {
|
|
1137
|
+
p.virtualListHelper.removeItemsAt(count, rowIndex);
|
|
1138
|
+
|
|
1139
|
+
if (this._o.virtualTable) {
|
|
1140
|
+
updateVirtualHeight(this);
|
|
1141
|
+
updateLastCellWidthFromScrollbar(this);
|
|
1142
|
+
this.render();
|
|
1143
|
+
updateTableWidth(this, false);
|
|
1144
|
+
} else {
|
|
1145
|
+
this.render();
|
|
1146
|
+
updateLastCellWidthFromScrollbar(this);
|
|
1147
|
+
updateTableWidth(this, true);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return this;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/** Removes a single row from the table */
|
|
1155
|
+
removeRow(rowIndex: number, render?: boolean) {
|
|
1156
|
+
return this.removeRows(rowIndex, 1, render);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/** Refreshes the row specified */
|
|
1160
|
+
refreshRow(rowIndex: number, render = true) {
|
|
1161
|
+
let p = this._p;
|
|
1162
|
+
|
|
1163
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1)
|
|
1164
|
+
return this;
|
|
1165
|
+
|
|
1166
|
+
// Find out if the row is in the rendered dataset
|
|
1167
|
+
let filteredRowIndex = -1;
|
|
1168
|
+
if (p.filteredRows && (filteredRowIndex = p.filteredRows.indexOf(p.rows[rowIndex])) === -1)
|
|
1169
|
+
return this;
|
|
1170
|
+
|
|
1171
|
+
if (filteredRowIndex === -1) {
|
|
1172
|
+
filteredRowIndex = rowIndex;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
p.virtualListHelper.refreshItemAt(filteredRowIndex);
|
|
1176
|
+
|
|
1177
|
+
if (render)
|
|
1178
|
+
p.virtualListHelper.render();
|
|
1179
|
+
|
|
1180
|
+
return this;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/** Get the DOM element for the specified row, if it exists */
|
|
1184
|
+
getRowElement(rowIndex: number): HTMLElement | null {
|
|
1185
|
+
let p = this._p;
|
|
1186
|
+
|
|
1187
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1)
|
|
1188
|
+
return null;
|
|
1189
|
+
|
|
1190
|
+
// Find out if the row is in the rendered dataset
|
|
1191
|
+
let filteredRowIndex = -1;
|
|
1192
|
+
if (p.filteredRows && (filteredRowIndex = p.filteredRows.indexOf(p.rows[rowIndex])) === -1)
|
|
1193
|
+
return null;
|
|
1194
|
+
|
|
1195
|
+
if (filteredRowIndex === -1) {
|
|
1196
|
+
filteredRowIndex = rowIndex;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
return p.virtualListHelper.getItemElementAt(filteredRowIndex) || null;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/** Refreshes all virtual rows */
|
|
1203
|
+
refreshAllVirtualRows() {
|
|
1204
|
+
const p = this._p;
|
|
1205
|
+
p.virtualListHelper.invalidate().render();
|
|
1206
|
+
return this;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/** Replace the whole dataset */
|
|
1210
|
+
setRows(data: RowData[], resort?: boolean) {
|
|
1211
|
+
let p = this._p;
|
|
1212
|
+
|
|
1213
|
+
p.rows.reset(data);
|
|
1214
|
+
|
|
1215
|
+
if (resort && p.rows.sortColumn.length) {
|
|
1216
|
+
this.resort();
|
|
1217
|
+
} else {
|
|
1218
|
+
refilter(this);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
this.clearAndRender().emit('addrows', { count: data.length, clear: true });
|
|
1222
|
+
|
|
1223
|
+
return this;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// =========================================================================
|
|
1227
|
+
// PUBLIC API - Size Changes
|
|
1228
|
+
// =========================================================================
|
|
1229
|
+
|
|
1230
|
+
/** Notify the table that its width has changed */
|
|
1231
|
+
tableWidthChanged(forceUpdate?: boolean, renderColumns?: boolean) {
|
|
1232
|
+
let o = this._o,
|
|
1233
|
+
p = this._p,
|
|
1234
|
+
detectedWidth = calculateWidthAvailableForColumns(this),
|
|
1235
|
+
sizeLeft = detectedWidth,
|
|
1236
|
+
relatives = 0;
|
|
1237
|
+
|
|
1238
|
+
if (!p.table) return this;
|
|
1239
|
+
|
|
1240
|
+
renderColumns = renderColumns === undefined || renderColumns;
|
|
1241
|
+
|
|
1242
|
+
let tableWidthBeforeCalculations = 0;
|
|
1243
|
+
|
|
1244
|
+
if (!p.tbody) {
|
|
1245
|
+
renderColumns = false;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
if (renderColumns) {
|
|
1249
|
+
tableWidthBeforeCalculations = parseFloat(p.tbody.style.minWidth) || 0;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
if (sizeLeft !== p.lastDetectedWidth || forceUpdate) {
|
|
1253
|
+
p.lastDetectedWidth = detectedWidth;
|
|
1254
|
+
|
|
1255
|
+
let absWidthTotal = 0, changedColumnIndexes = [], totalRelativePercentage = 0;
|
|
1256
|
+
|
|
1257
|
+
for (let i = 0; i < p.columns.length; i++) {
|
|
1258
|
+
p.columns[i].actualWidthConsideringScrollbarWidth = null;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1262
|
+
let col = p.visibleColumns[i];
|
|
1263
|
+
if (col.widthMode === ColumnWidthMode.ABSOLUTE) {
|
|
1264
|
+
let width = col.width;
|
|
1265
|
+
width += col.arrowProposedWidth || 0;
|
|
1266
|
+
if (!col.ignoreMin && width < o.minColumnWidth) {
|
|
1267
|
+
width = o.minColumnWidth;
|
|
1268
|
+
}
|
|
1269
|
+
sizeLeft -= width;
|
|
1270
|
+
absWidthTotal += width;
|
|
1271
|
+
|
|
1272
|
+
// Update actualWidth
|
|
1273
|
+
if (width !== col.actualWidth) {
|
|
1274
|
+
col.actualWidth = width;
|
|
1275
|
+
changedColumnIndexes.push(i);
|
|
1276
|
+
}
|
|
1277
|
+
} else if (col.widthMode === ColumnWidthMode.AUTO) {
|
|
1278
|
+
let width = getTextWidth(this, col.label) + 20;
|
|
1279
|
+
width += col.arrowProposedWidth || 0;
|
|
1280
|
+
if (!col.ignoreMin && width < o.minColumnWidth) {
|
|
1281
|
+
width = o.minColumnWidth;
|
|
1282
|
+
}
|
|
1283
|
+
sizeLeft -= width;
|
|
1284
|
+
absWidthTotal += width;
|
|
1285
|
+
|
|
1286
|
+
// Update actualWidth
|
|
1287
|
+
if (width !== col.actualWidth) {
|
|
1288
|
+
col.actualWidth = width;
|
|
1289
|
+
if (!o.convertColumnWidthsToRelative) {
|
|
1290
|
+
changedColumnIndexes.push(i);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
} else if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
1294
|
+
totalRelativePercentage += col.width;
|
|
1295
|
+
relatives++;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// Normalize relative sizes if needed
|
|
1300
|
+
if (o.convertColumnWidthsToRelative) {
|
|
1301
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1302
|
+
let col = p.visibleColumns[i];
|
|
1303
|
+
if (col.widthMode === ColumnWidthMode.AUTO) {
|
|
1304
|
+
col.widthMode = ColumnWidthMode.RELATIVE;
|
|
1305
|
+
sizeLeft += col.actualWidth;
|
|
1306
|
+
col.width = col.actualWidth / absWidthTotal;
|
|
1307
|
+
totalRelativePercentage += col.width;
|
|
1308
|
+
relatives++;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Normalize relative sizes if needed
|
|
1314
|
+
if (relatives && ((totalRelativePercentage < 1 && o.relativeWidthGrowsToFillWidth) ||
|
|
1315
|
+
(totalRelativePercentage > 1 && o.relativeWidthShrinksToFillWidth))) {
|
|
1316
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1317
|
+
let col = p.visibleColumns[i];
|
|
1318
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
1319
|
+
col.width /= totalRelativePercentage;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
let sizeLeftForRelative = Math.max(0, sizeLeft); // Use this as the space to take the relative widths out of
|
|
1325
|
+
if (sizeLeftForRelative === 0) {
|
|
1326
|
+
sizeLeftForRelative = p.table.clientWidth;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
let minColumnWidthRelative = (o.minColumnWidth / sizeLeftForRelative);
|
|
1330
|
+
if (isNaN(minColumnWidthRelative)) {
|
|
1331
|
+
minColumnWidthRelative = 0;
|
|
1332
|
+
}
|
|
1333
|
+
if (minColumnWidthRelative > 0) {
|
|
1334
|
+
let extraRelative = 0, delta;
|
|
1335
|
+
|
|
1336
|
+
// First pass - make sure they are all constrained to the minimum width
|
|
1337
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1338
|
+
let col = p.visibleColumns[i];
|
|
1339
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
1340
|
+
if (!col.ignoreMin && col.width < minColumnWidthRelative) {
|
|
1341
|
+
extraRelative += minColumnWidthRelative - col.width;
|
|
1342
|
+
col.width = minColumnWidthRelative;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Second pass - try to take the extra width out of the other columns to compensate
|
|
1348
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1349
|
+
let col = p.visibleColumns[i];
|
|
1350
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
1351
|
+
if (!col.ignoreMin && col.width > minColumnWidthRelative) {
|
|
1352
|
+
if (extraRelative > 0) {
|
|
1353
|
+
delta = Math.min(extraRelative, col.width - minColumnWidthRelative);
|
|
1354
|
+
col.width -= delta;
|
|
1355
|
+
extraRelative -= delta;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// Try to fill width
|
|
1363
|
+
if (o.autoFillTableWidth && sizeLeft > 0) {
|
|
1364
|
+
let nonResizableTotal = 0;
|
|
1365
|
+
let sizeLeftToFill = sizeLeft;
|
|
1366
|
+
|
|
1367
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1368
|
+
let col = p.visibleColumns[i];
|
|
1369
|
+
if (!col.resizable && col.widthMode === ColumnWidthMode.ABSOLUTE)
|
|
1370
|
+
nonResizableTotal += col.width;
|
|
1371
|
+
|
|
1372
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE)
|
|
1373
|
+
sizeLeftToFill -= Math.round(sizeLeftForRelative * col.width);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
let conv = ((detectedWidth - nonResizableTotal) / (detectedWidth - sizeLeftToFill - nonResizableTotal)) || NaN;
|
|
1377
|
+
for (let i = 0; i < p.visibleColumns.length && sizeLeftToFill > 0; i++) {
|
|
1378
|
+
let col = p.visibleColumns[i];
|
|
1379
|
+
if (!col.resizable && col.widthMode === ColumnWidthMode.ABSOLUTE)
|
|
1380
|
+
continue;
|
|
1381
|
+
|
|
1382
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
1383
|
+
col.width *= conv;
|
|
1384
|
+
} else {
|
|
1385
|
+
let width = col.actualWidth * conv;
|
|
1386
|
+
if (col.actualWidth !== width) {
|
|
1387
|
+
col.actualWidth = width;
|
|
1388
|
+
if (changedColumnIndexes.indexOf(i) === -1)
|
|
1389
|
+
changedColumnIndexes.push(i);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// Materialize relative sizes
|
|
1396
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
1397
|
+
let col = p.visibleColumns[i];
|
|
1398
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
1399
|
+
let width = Math.round(sizeLeftForRelative * col.width);
|
|
1400
|
+
sizeLeft -= width;
|
|
1401
|
+
relatives--;
|
|
1402
|
+
|
|
1403
|
+
// Take care of rounding errors
|
|
1404
|
+
if (relatives === 0 && sizeLeft === 1) {
|
|
1405
|
+
width++;
|
|
1406
|
+
sizeLeft--;
|
|
1407
|
+
}
|
|
1408
|
+
if (sizeLeft === -1) {
|
|
1409
|
+
width--;
|
|
1410
|
+
sizeLeft++;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// Update actualWidth
|
|
1414
|
+
if (width !== col.actualWidth) {
|
|
1415
|
+
col.actualWidth = width;
|
|
1416
|
+
changedColumnIndexes.push(i);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
if (p.visibleColumns.length) {
|
|
1422
|
+
// (There should always be at least 1 column visible, but just in case)
|
|
1423
|
+
p.visibleColumns[p.visibleColumns.length - 1].actualWidthConsideringScrollbarWidth =
|
|
1424
|
+
p.visibleColumns[p.visibleColumns.length - 1].actualWidth - (p.scrollbarWidth || 0);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
p.notifyRendererOfColumnsConfig?.();
|
|
1428
|
+
|
|
1429
|
+
if (renderColumns) {
|
|
1430
|
+
let tableWidth = calculateTbodyWidth(this);
|
|
1431
|
+
|
|
1432
|
+
if (tableWidthBeforeCalculations < tableWidth) {
|
|
1433
|
+
updateTableWidth(this, false);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
for (let i = 0; i < changedColumnIndexes.length; i++) {
|
|
1437
|
+
resizeColumnElements(this, changedColumnIndexes[i]);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
if (tableWidthBeforeCalculations > tableWidth) {
|
|
1441
|
+
updateTableWidth(this, false);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
return this;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/** Notify the table that its height has changed */
|
|
1450
|
+
tableHeightChanged() {
|
|
1451
|
+
let o = this._o,
|
|
1452
|
+
p = this._p;
|
|
1453
|
+
|
|
1454
|
+
if (!p.table) {
|
|
1455
|
+
return this;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
const tableStyle = getComputedStyle(p.table);
|
|
1459
|
+
|
|
1460
|
+
let height = getElementHeight(this.el, true)
|
|
1461
|
+
- (parseFloat(tableStyle.borderTopWidth) || 0)
|
|
1462
|
+
- (parseFloat(tableStyle.borderBottomWidth) || 0);
|
|
1463
|
+
|
|
1464
|
+
if (height !== o.height) {
|
|
1465
|
+
|
|
1466
|
+
o.height = height;
|
|
1467
|
+
|
|
1468
|
+
if (p.tbody) {
|
|
1469
|
+
p.tbody.style.height = Math.max(o.height - getElementHeight(p.header, true, true, true), 1) + 'px';
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
if (o.virtualTable) {
|
|
1473
|
+
this.clearAndRender();
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
return this;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// =========================================================================
|
|
1481
|
+
// PUBLIC API - Cell Preview
|
|
1482
|
+
// =========================================================================
|
|
1483
|
+
|
|
1484
|
+
/** Hides the current cell preview */
|
|
1485
|
+
hideCellPreview() {
|
|
1486
|
+
hideCellPreview(this);
|
|
1487
|
+
return this;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
/** A synonym for hideCellPreview() */
|
|
1491
|
+
abortCellPreview() {
|
|
1492
|
+
this.hideCellPreview();
|
|
1493
|
+
return this;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
/** Cancel a resize in progress */
|
|
1497
|
+
cancelColumnResize() {
|
|
1498
|
+
cancelColumnResize(this);
|
|
1499
|
+
return this;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// =========================================================================
|
|
1503
|
+
// PUBLIC API - Web Workers
|
|
1504
|
+
// =========================================================================
|
|
1505
|
+
|
|
1506
|
+
/** Creates a URL representing the data in the specified element (for Web Workers) */
|
|
1507
|
+
getUrlForElementContent(id: string): string | null {
|
|
1508
|
+
let blob,
|
|
1509
|
+
el = document.getElementById(id);
|
|
1510
|
+
if (el) {
|
|
1511
|
+
let data = el.textContent;
|
|
1512
|
+
if (typeof Blob === 'function') {
|
|
1513
|
+
blob = new Blob([data || '']);
|
|
1514
|
+
} else {
|
|
1515
|
+
// Legacy BlobBuilder support (deprecated)
|
|
1516
|
+
const win = window as unknown as Record<string, unknown>;
|
|
1517
|
+
const BlobBuilder = win.BlobBuilder || win.WebKitBlobBuilder || win.MozBlobBuilder || win.MSBlobBuilder;
|
|
1518
|
+
if (!BlobBuilder) {
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
const builder = new (BlobBuilder as new () => { append(data: string): void; getBlob(): Blob })();
|
|
1522
|
+
builder.append(data || '');
|
|
1523
|
+
blob = builder.getBlob();
|
|
1524
|
+
}
|
|
1525
|
+
return (window.URL || (window as unknown as { webkitURL: typeof URL }).webkitURL).createObjectURL(blob);
|
|
1526
|
+
}
|
|
1527
|
+
return null;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/** Check if Web Workers are supported */
|
|
1531
|
+
isWorkerSupported(): boolean {
|
|
1532
|
+
return window['Worker'] instanceof Function;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
/** Creates a Web Worker for updating the table */
|
|
1536
|
+
createWebWorker(url: string, start?: boolean, resort?: boolean): Worker | null {
|
|
1537
|
+
if (this.isWorkerSupported()) {
|
|
1538
|
+
let p = this._p;
|
|
1539
|
+
|
|
1540
|
+
let worker = new Worker(url);
|
|
1541
|
+
let listener = (evt: MessageEvent) => {
|
|
1542
|
+
if (evt.data.append) {
|
|
1543
|
+
this.addRows(evt.data.rows, resort);
|
|
1544
|
+
} else {
|
|
1545
|
+
this.setRows(evt.data.rows, resort);
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
worker.addEventListener('message', listener, false);
|
|
1549
|
+
if (!p.workerListeners) {
|
|
1550
|
+
p.workerListeners = [];
|
|
1551
|
+
}
|
|
1552
|
+
p.workerListeners.push({ worker: worker, listener: listener });
|
|
1553
|
+
if (start || start === undefined) {
|
|
1554
|
+
worker.postMessage(null);
|
|
1555
|
+
}
|
|
1556
|
+
return worker;
|
|
1557
|
+
}
|
|
1558
|
+
return null;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/** Unbinds a Web Worker from the table, stopping updates */
|
|
1562
|
+
unbindWebWorker(worker: Worker) {
|
|
1563
|
+
let p = this._p;
|
|
1564
|
+
|
|
1565
|
+
if (p.workerListeners) {
|
|
1566
|
+
for (let j = 0; j < p.workerListeners.length; j++) {
|
|
1567
|
+
if (p.workerListeners[j].worker === worker) {
|
|
1568
|
+
worker.removeEventListener('message', p.workerListeners[j].listener, false);
|
|
1569
|
+
p.workerListeners.splice(j, 1);
|
|
1570
|
+
j--;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
return this;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
export default DGTable;
|
|
1580
|
+
|
|
1581
|
+
// Re-export types for TypeScript users
|
|
1582
|
+
export type {
|
|
1583
|
+
// Configuration types
|
|
1584
|
+
DGTableOptions,
|
|
1585
|
+
ColumnOptions,
|
|
1586
|
+
ColumnSortOptions,
|
|
1587
|
+
SerializedColumn,
|
|
1588
|
+
SerializedColumnSort,
|
|
1589
|
+
RowData,
|
|
1590
|
+
// Function types
|
|
1591
|
+
CellFormatter,
|
|
1592
|
+
HeaderCellFormatter,
|
|
1593
|
+
FilterFunction,
|
|
1594
|
+
ComparatorFunction,
|
|
1595
|
+
OnComparatorRequired,
|
|
1596
|
+
CustomSortingProvider,
|
|
1597
|
+
// Event types
|
|
1598
|
+
RowCreateEvent,
|
|
1599
|
+
RowClickEvent,
|
|
1600
|
+
CellPreviewEvent,
|
|
1601
|
+
CellPreviewDestroyEvent,
|
|
1602
|
+
HeaderContextMenuEvent,
|
|
1603
|
+
MoveColumnEvent,
|
|
1604
|
+
ColumnWidthEvent,
|
|
1605
|
+
AddRowsEvent,
|
|
1606
|
+
SortEvent,
|
|
1607
|
+
// Event map for typed handlers
|
|
1608
|
+
DGTableEventMap,
|
|
1609
|
+
} from './types';
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
|
|
1615
|
+
|
|
1616
|
+
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
|
|
1620
|
+
|
|
1621
|
+
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
|
|
1627
|
+
|
|
1628
|
+
|