@danielgindi/dgtable.js 2.0.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/LICENSE +21 -0
- package/README.md +288 -0
- package/connectors/jquery/index.js +27 -0
- package/connectors/jquery/package.json +46 -0
- package/dist/lib.cjs.js +6290 -0
- package/dist/lib.cjs.js.map +1 -0
- package/dist/lib.cjs.min.js +7 -0
- package/dist/lib.cjs.min.js.map +1 -0
- package/dist/lib.es6.js +6288 -0
- package/dist/lib.es6.js.map +1 -0
- package/dist/lib.es6.min.js +7 -0
- package/dist/lib.es6.min.js.map +1 -0
- package/dist/lib.umd.js +4365 -0
- package/dist/lib.umd.js.map +1 -0
- package/dist/lib.umd.min.js +7 -0
- package/dist/lib.umd.min.js.map +1 -0
- package/eslint.config.mjs +133 -0
- package/package.json +55 -0
- package/src/SelectionHelper.js +65 -0
- package/src/by_column_filter.js +25 -0
- package/src/column_collection.js +153 -0
- package/src/index.js +4003 -0
- package/src/row_collection.js +183 -0
- package/src/util.js +17 -0
package/dist/lib.umd.js
ADDED
|
@@ -0,0 +1,4365 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @danielgindi/dgtable.js 2.0.0
|
|
3
|
+
* git://github.com/danielgindi/dgtable.js.git
|
|
4
|
+
*/
|
|
5
|
+
(function (global, factory) {
|
|
6
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@danielgindi/dom-utils/lib/ScrollHelper.js'), require('@danielgindi/dom-utils/lib/Css.js'), require('@danielgindi/dom-utils/lib/DomCompat.js'), require('@danielgindi/virtual-list-helper'), require('@danielgindi/dom-utils/lib/DomEventsSink.js')) :
|
|
7
|
+
typeof define === 'function' && define.amd ? define(['@danielgindi/dom-utils/lib/ScrollHelper.js', '@danielgindi/dom-utils/lib/Css.js', '@danielgindi/dom-utils/lib/DomCompat.js', '@danielgindi/virtual-list-helper', '@danielgindi/dom-utils/lib/DomEventsSink.js'], factory) :
|
|
8
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.DGTable = factory(global.domUtilsScrollHelper, global.domUtilsCss, global.domUtilsDomCompat, global.VirtualListHelper, global.domUtilsDomEventsSink));
|
|
9
|
+
})(this, (function (ScrollHelper_js, Css_js, DomCompat_js, VirtualListHelper, DomEventsSink) { 'use strict';
|
|
10
|
+
|
|
11
|
+
const find = function find(array, predicate) {
|
|
12
|
+
for (let i = 0, len = array.length; i >= 0 && i < len; i += 1) {
|
|
13
|
+
if (predicate(array[i], i, array))
|
|
14
|
+
return array[i];
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const htmlEncode = function htmlEncode(text) {
|
|
19
|
+
return text.replace(/&/g, "&").
|
|
20
|
+
replace(/</g, "<").
|
|
21
|
+
replace(/>/g, ">").
|
|
22
|
+
replace(/'/g, "'").
|
|
23
|
+
replace(/"/g, """).
|
|
24
|
+
replace(/\n/g, '<br />');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Define class RowCollection
|
|
28
|
+
function RowCollection() {
|
|
29
|
+
|
|
30
|
+
// Instantiate an Array. Seems like the `.length = ` of an inherited Array does not work well.
|
|
31
|
+
// I will not use the IFRAME solution either in fear of memory leaks, and we're supporting large datasets...
|
|
32
|
+
let collection = [];
|
|
33
|
+
|
|
34
|
+
// Synthetically set the 'prototype'
|
|
35
|
+
Object.assign(collection, RowCollection.prototype);
|
|
36
|
+
|
|
37
|
+
// Call initializer
|
|
38
|
+
collection.initialize.apply(collection, arguments);
|
|
39
|
+
|
|
40
|
+
return collection;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Inherit Array
|
|
44
|
+
RowCollection.prototype = [];
|
|
45
|
+
|
|
46
|
+
RowCollection.prototype.initialize = function (options) {
|
|
47
|
+
|
|
48
|
+
options = options || {};
|
|
49
|
+
|
|
50
|
+
/** @field {string} sortColumn */
|
|
51
|
+
this.sortColumn = options.sortColumn == null ? [] : options.sortColumn;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Object|Object[]} rows - row or array of rows to add to this collection
|
|
56
|
+
* @param {number?} at - position to insert rows at
|
|
57
|
+
*/
|
|
58
|
+
RowCollection.prototype.add = function (rows, at) {
|
|
59
|
+
let isArray = 'splice' in rows && 'length' in rows,i,len;
|
|
60
|
+
if (isArray) {
|
|
61
|
+
if (typeof at === 'number') {
|
|
62
|
+
for (i = 0, len = rows.length; i < len; i++) {
|
|
63
|
+
this.splice(at++, 0, rows[i]);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
for (i = 0, len = rows.length; i < len; i++) {
|
|
67
|
+
this.push(rows[i]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
if (typeof at === 'number') {
|
|
72
|
+
this.splice(at, 0, rows);
|
|
73
|
+
} else {
|
|
74
|
+
this.push(rows);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {Object|Object[]=} rows Row or array of rows to add to this collection
|
|
81
|
+
*/
|
|
82
|
+
RowCollection.prototype.reset = function (rows) {
|
|
83
|
+
this.length = 0;
|
|
84
|
+
if (rows) {
|
|
85
|
+
this.add(rows);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {Function} filterFunc - Filtering function
|
|
91
|
+
* @param {Object|null?} args - Options to pass to the function
|
|
92
|
+
* @returns {RowCollection} success result
|
|
93
|
+
*/
|
|
94
|
+
RowCollection.prototype.filteredCollection = function (filterFunc, args) {
|
|
95
|
+
if (filterFunc && args) {
|
|
96
|
+
let rows = new RowCollection({
|
|
97
|
+
sortColumn: this.sortColumn,
|
|
98
|
+
onComparatorRequired: this.onComparatorRequired,
|
|
99
|
+
customSortingProvider: this.customSortingProvider
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
for (let i = 0, len = this.length, row; i < len; i++) {
|
|
103
|
+
row = this[i];
|
|
104
|
+
if (filterFunc(row, args)) {
|
|
105
|
+
row['__i'] = i;
|
|
106
|
+
rows.push(row);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return rows;
|
|
110
|
+
} else {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @type {function(columnName: string, descending: boolean, defaultComparator: function(a,b):number)|null|undefined}
|
|
117
|
+
*/
|
|
118
|
+
RowCollection.prototype.onComparatorRequired = null;
|
|
119
|
+
/**
|
|
120
|
+
* @type {function(data: any[], sort: function(any[]):any[]):any[]|null|undefined}
|
|
121
|
+
*/
|
|
122
|
+
RowCollection.prototype.customSortingProvider = null;
|
|
123
|
+
|
|
124
|
+
let nativeSort = RowCollection.prototype.sort;
|
|
125
|
+
|
|
126
|
+
function getDefaultComparator(column, descending) {
|
|
127
|
+
let columnName = column.column;
|
|
128
|
+
let comparePath = column.comparePath || columnName;
|
|
129
|
+
if (typeof comparePath === 'string') {
|
|
130
|
+
comparePath = comparePath.split('.');
|
|
131
|
+
}
|
|
132
|
+
let pathLength = comparePath.length,
|
|
133
|
+
hasPath = pathLength > 1,
|
|
134
|
+
i;
|
|
135
|
+
|
|
136
|
+
let lessVal = descending ? 1 : -1,moreVal = descending ? -1 : 1;
|
|
137
|
+
return function (leftRow, rightRow) {
|
|
138
|
+
let leftVal = leftRow[comparePath[0]],
|
|
139
|
+
rightVal = rightRow[comparePath[0]];
|
|
140
|
+
if (hasPath) {
|
|
141
|
+
for (i = 1; i < pathLength; i++) {
|
|
142
|
+
leftVal = leftVal && leftVal[comparePath[i]];
|
|
143
|
+
rightVal = rightVal && rightVal[comparePath[i]];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (leftVal === rightVal) return 0;
|
|
147
|
+
if (leftVal == null) return lessVal;
|
|
148
|
+
if (rightVal == null) return moreVal;
|
|
149
|
+
if (leftVal < rightVal) return lessVal;
|
|
150
|
+
return moreVal;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @returns {Function|undefined} the comparator that was used
|
|
156
|
+
*/
|
|
157
|
+
RowCollection.prototype.sort = function () {
|
|
158
|
+
let comparator;
|
|
159
|
+
|
|
160
|
+
if (this.sortColumn.length) {
|
|
161
|
+
let comparators = [];
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < this.sortColumn.length; i++) {
|
|
164
|
+
comparator = null;
|
|
165
|
+
const defaultComparator = getDefaultComparator(this.sortColumn[i], this.sortColumn[i].descending);
|
|
166
|
+
if (this.onComparatorRequired) {
|
|
167
|
+
comparator = this.onComparatorRequired(this.sortColumn[i].column, this.sortColumn[i].descending, defaultComparator);
|
|
168
|
+
}
|
|
169
|
+
if (!comparator) {
|
|
170
|
+
comparator = defaultComparator;
|
|
171
|
+
}
|
|
172
|
+
comparators.push(comparator.bind(this));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (comparators.length === 1) {
|
|
176
|
+
comparator = comparators[0];
|
|
177
|
+
} else {
|
|
178
|
+
let len = comparators.length,
|
|
179
|
+
value;
|
|
180
|
+
|
|
181
|
+
comparator = function (leftRow, rightRow) {
|
|
182
|
+
for (let i = 0; i < len; i++) {
|
|
183
|
+
value = comparators[i](leftRow, rightRow);
|
|
184
|
+
if (value !== 0) {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return value;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const sorter = (data) => nativeSort.call(data, comparator);
|
|
193
|
+
|
|
194
|
+
if (this.customSortingProvider) {
|
|
195
|
+
let results = this.customSortingProvider(this, sorter);
|
|
196
|
+
if (results !== this) {
|
|
197
|
+
this.splice(0, this.length, ...results);
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
sorter(this);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return comparator;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Define class RowCollection
|
|
208
|
+
function ColumnCollection() {
|
|
209
|
+
|
|
210
|
+
// Instantiate an Array. Seems like the `.length = ` of an inherited Array does not work well.
|
|
211
|
+
// I will not use the IFRAME solution either in fear of memory leaks, and we're supporting large datasets...
|
|
212
|
+
let collection = [];
|
|
213
|
+
|
|
214
|
+
// Synthetically set the 'prototype'
|
|
215
|
+
Object.assign(collection, ColumnCollection.prototype);
|
|
216
|
+
|
|
217
|
+
// Call initializer
|
|
218
|
+
collection.initialize.apply(collection, arguments);
|
|
219
|
+
|
|
220
|
+
return collection;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Inherit Array
|
|
224
|
+
ColumnCollection.prototype = [];
|
|
225
|
+
|
|
226
|
+
ColumnCollection.prototype.initialize = function () {
|
|
227
|
+
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get the column by this name
|
|
232
|
+
* @param {string} column column name
|
|
233
|
+
* @returns {Object} the column object
|
|
234
|
+
*/
|
|
235
|
+
ColumnCollection.prototype.get = function (column) {
|
|
236
|
+
for (let i = 0, len = this.length; i < len; i++) {
|
|
237
|
+
if (this[i].name === column) {
|
|
238
|
+
return this[i];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get the index of the column by this name
|
|
246
|
+
* @param {string} column column name
|
|
247
|
+
* @returns {int} the index of this column
|
|
248
|
+
*/
|
|
249
|
+
ColumnCollection.prototype.indexOf = function (column) {
|
|
250
|
+
for (let i = 0, len = this.length; i < len; i++) {
|
|
251
|
+
if (this[i].name === column) {
|
|
252
|
+
return i;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return -1;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the column by the specified order
|
|
260
|
+
* @param {number} order the column's order
|
|
261
|
+
* @returns {Object} the column object
|
|
262
|
+
*/
|
|
263
|
+
ColumnCollection.prototype.getByOrder = function (order) {
|
|
264
|
+
for (let i = 0, len = this.length; i < len; i++) {
|
|
265
|
+
if (this[i].order === order) {
|
|
266
|
+
return this[i];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Normalize order
|
|
274
|
+
* @returns {ColumnCollection} self
|
|
275
|
+
*/
|
|
276
|
+
ColumnCollection.prototype.normalizeOrder = function () {
|
|
277
|
+
let ordered = [],i;
|
|
278
|
+
for (i = 0; i < this.length; i++) {
|
|
279
|
+
ordered.push(this[i]);
|
|
280
|
+
}
|
|
281
|
+
ordered.sort(function (col1, col2) {return col1.order < col2.order ? -1 : col1.order > col2.order ? 1 : 0;});
|
|
282
|
+
for (i = 0; i < ordered.length; i++) {
|
|
283
|
+
ordered[i].order = i;
|
|
284
|
+
}
|
|
285
|
+
return this;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get the array of columns, order by the order property
|
|
290
|
+
* @returns {Array<Object>} ordered array of columns
|
|
291
|
+
*/
|
|
292
|
+
ColumnCollection.prototype.getColumns = function () {
|
|
293
|
+
let cols = [];
|
|
294
|
+
for (let i = 0, column; i < this.length; i++) {
|
|
295
|
+
column = this[i];
|
|
296
|
+
cols.push(column);
|
|
297
|
+
}
|
|
298
|
+
cols.sort((col1, col2) => col1.order < col2.order ? -1 : col1.order > col2.order ? 1 : 0);
|
|
299
|
+
return cols;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get the array of visible columns, order by the order property
|
|
304
|
+
* @returns {Array<Object>} ordered array of visible columns
|
|
305
|
+
*/
|
|
306
|
+
ColumnCollection.prototype.getVisibleColumns = function () {
|
|
307
|
+
let cols = [];
|
|
308
|
+
for (let i = 0, column; i < this.length; i++) {
|
|
309
|
+
column = this[i];
|
|
310
|
+
if (column.visible) {
|
|
311
|
+
cols.push(column);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
cols.sort((col1, col2) => col1.order < col2.order ? -1 : col1.order > col2.order ? 1 : 0);
|
|
315
|
+
return cols;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @returns {int} maximum order currently in the array
|
|
320
|
+
*/
|
|
321
|
+
ColumnCollection.prototype.getMaxOrder = function () {
|
|
322
|
+
let order = 0;
|
|
323
|
+
for (let i = 0, column; i < this.length; i++) {
|
|
324
|
+
column = this[i];
|
|
325
|
+
if (column.order > order) {
|
|
326
|
+
order = column.order;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return order;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Move a column to a new spot in the collection
|
|
334
|
+
* @param {Object} src the column to move
|
|
335
|
+
* @param {Object} dest the destination column
|
|
336
|
+
* @returns {ColumnCollection} self
|
|
337
|
+
*/
|
|
338
|
+
ColumnCollection.prototype.moveColumn = function (src, dest) {
|
|
339
|
+
if (src && dest) {
|
|
340
|
+
let srcOrder = src.order,destOrder = dest.order,i,col;
|
|
341
|
+
if (srcOrder < destOrder) {
|
|
342
|
+
for (i = srcOrder + 1; i <= destOrder; i++) {
|
|
343
|
+
col = this.getByOrder(i);
|
|
344
|
+
col.order--;
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
for (i = srcOrder - 1; i >= destOrder; i--) {
|
|
348
|
+
col = this.getByOrder(i);
|
|
349
|
+
col.order++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
src.order = destOrder;
|
|
353
|
+
}
|
|
354
|
+
return this;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
/* eslint-env browser */
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
// saveSelection/restoreSelection courtesy of Tim Down, with my improvements
|
|
361
|
+
// https://stackoverflow.com/questions/13949059/persisting-the-changes-of-range-objects-after-selection-in-html/13950376#13950376
|
|
362
|
+
|
|
363
|
+
function isChildOf(child, parent) {
|
|
364
|
+
while ((child = child.parentNode) && child !== parent);
|
|
365
|
+
return !!child;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
class SelectionHelper {
|
|
369
|
+
|
|
370
|
+
static saveSelection(el) {
|
|
371
|
+
let range = window.getSelection().getRangeAt(0);
|
|
372
|
+
|
|
373
|
+
if (el !== range.commonAncestorContainer && !isChildOf(range.commonAncestorContainer, el))
|
|
374
|
+
return null;
|
|
375
|
+
|
|
376
|
+
let preSelectionRange = range.cloneRange();
|
|
377
|
+
preSelectionRange.selectNodeContents(el);
|
|
378
|
+
preSelectionRange.setEnd(range.startContainer, range.startOffset);
|
|
379
|
+
let start = preSelectionRange.toString().length;
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
start: start,
|
|
383
|
+
end: start + range.toString().length
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
static restoreSelection(el, savedSel) {
|
|
388
|
+
let charIndex = 0;
|
|
389
|
+
let nodeStack = [el],node,foundStart = false,stop = false;
|
|
390
|
+
let range = document.createRange();
|
|
391
|
+
range.setStart(el, 0);
|
|
392
|
+
range.collapse(true);
|
|
393
|
+
|
|
394
|
+
while (!stop && (node = nodeStack.pop())) {
|
|
395
|
+
if (node.nodeType === 3) {
|
|
396
|
+
let nextCharIndex = charIndex + node.length;
|
|
397
|
+
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
|
|
398
|
+
range.setStart(node, savedSel.start - charIndex);
|
|
399
|
+
foundStart = true;
|
|
400
|
+
}
|
|
401
|
+
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
|
|
402
|
+
range.setEnd(node, savedSel.end - charIndex);
|
|
403
|
+
stop = true;
|
|
404
|
+
}
|
|
405
|
+
charIndex = nextCharIndex;
|
|
406
|
+
} else {
|
|
407
|
+
let i = node.childNodes.length;
|
|
408
|
+
while (i--) {
|
|
409
|
+
nodeStack.push(node.childNodes[i]);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
let sel = window.getSelection();
|
|
415
|
+
sel.removeAllRanges();
|
|
416
|
+
sel.addRange(range);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function ByColumnFilter(row, args) {
|
|
421
|
+
|
|
422
|
+
let column = args.column;
|
|
423
|
+
let keyword = args.keyword == null ? '' : args.keyword.toString();
|
|
424
|
+
|
|
425
|
+
if (!keyword || !column) return true;
|
|
426
|
+
|
|
427
|
+
let actualVal = row[column];
|
|
428
|
+
if (actualVal == null) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
actualVal = actualVal.toString();
|
|
433
|
+
|
|
434
|
+
if (!args.caseSensitive) {
|
|
435
|
+
actualVal = actualVal.toLowerCase();
|
|
436
|
+
keyword = keyword.toLowerCase();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return actualVal.indexOf(keyword) !== -1;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function mitt (n) {return { all: n = n || new Map(), on: function (t, e) {var i = n.get(t);i ? i.push(e) : n.set(t, [e]);}, off: function (t, e) {var i = n.get(t);i && (e ? i.splice(i.indexOf(e) >>> 0, 1) : n.set(t, []));}, emit: function (t, e) {var i = n.get(t);i && i.slice().map(function (n) {n(e);}), (i = n.get("*")) && i.slice().map(function (n) {n(t, e);});} };}
|
|
443
|
+
|
|
444
|
+
/* eslint-env browser */
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
const nativeIndexOf = Array.prototype.indexOf;
|
|
448
|
+
|
|
449
|
+
let createElement = document.createElement.bind(document);
|
|
450
|
+
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
451
|
+
|
|
452
|
+
const IsSafeSymbol = Symbol('safe');
|
|
453
|
+
const HoverInEventSymbol = Symbol('hover_in');
|
|
454
|
+
const HoverOutEventSymbol = Symbol('hover_out');
|
|
455
|
+
const RowClickEventSymbol = Symbol('row_click');
|
|
456
|
+
const PreviewCellSymbol = Symbol('preview_cell');
|
|
457
|
+
const OriginalCellSymbol = Symbol('cell');
|
|
458
|
+
|
|
459
|
+
function webkitRenderBugfix(el) {
|
|
460
|
+
// BUGFIX: WebKit has a bug where it does not relayout, and this affects us because scrollbars
|
|
461
|
+
// are still calculated even though they are not there yet. This is the last resort.
|
|
462
|
+
let oldDisplay = el.style.display;
|
|
463
|
+
el.style.display = 'none';
|
|
464
|
+
//noinspection BadExpressionStatementJS
|
|
465
|
+
el.offsetHeight; // No need to store this anywhere, the reference is enough
|
|
466
|
+
el.style.display = oldDisplay;
|
|
467
|
+
return el;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function relativizeElement(el) {
|
|
471
|
+
if (!['relative', 'absolute', 'fixed'].includes(getComputedStyle(el).position)) {
|
|
472
|
+
el.style.position = 'relative';
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const isInputElementEvent = (event) => /^(?:INPUT|TEXTAREA|BUTTON|SELECT)$/.test(event.target.tagName);
|
|
477
|
+
|
|
478
|
+
// noinspection JSUnusedGlobalSymbols
|
|
479
|
+
class DGTable {
|
|
480
|
+
/**
|
|
481
|
+
* @param {DGTable.Options?} options - initialization options
|
|
482
|
+
*/
|
|
483
|
+
constructor(options) {
|
|
484
|
+
this._init(options);
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @public
|
|
488
|
+
* @expose
|
|
489
|
+
* @type {string}
|
|
490
|
+
*/
|
|
491
|
+
this.VERSION = DGTable.VERSION;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @param {DGTable.Options?} options - initialization options
|
|
496
|
+
*/
|
|
497
|
+
_init(options) {
|
|
498
|
+
options = options || {};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* @private
|
|
502
|
+
* @type {DGTable.Options}
|
|
503
|
+
* */
|
|
504
|
+
let o = this._o = {};
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @private
|
|
508
|
+
* This is for encapsulating private data */
|
|
509
|
+
let p = this._p = {
|
|
510
|
+
eventsSink: new DomEventsSink(),
|
|
511
|
+
mitt: mitt(),
|
|
512
|
+
/** @type {boolean} */
|
|
513
|
+
tableSkeletonNeedsRendering: true
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @public
|
|
518
|
+
* @expose
|
|
519
|
+
* */
|
|
520
|
+
this.el = options.el && options.el instanceof Element ? options.el : document.createElement('div');
|
|
521
|
+
|
|
522
|
+
if (this.el !== options.el) {
|
|
523
|
+
this.el.classList.add(options.className || 'dgtable-wrapper');
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
p.eventsSink.add(this.el, 'dragend.colresize', this._onEndDragColumnHeader.bind(this));
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* @private
|
|
530
|
+
* @field {boolean} virtualTable */
|
|
531
|
+
o.virtualTable = options.virtualTable === undefined ? true : !!options.virtualTable;
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* @private
|
|
535
|
+
* @field {number} estimatedRowHeight */
|
|
536
|
+
o.estimatedRowHeight = options.estimatedRowHeight || undefined;
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* @private
|
|
540
|
+
* @field {number} rowsBufferSize */
|
|
541
|
+
o.rowsBufferSize = options.rowsBufferSize || 3;
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* @private
|
|
545
|
+
* @field {number} minColumnWidth */
|
|
546
|
+
o.minColumnWidth = Math.max(options.minColumnWidth || 35, 0);
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @private
|
|
550
|
+
* @field {number} resizeAreaWidth */
|
|
551
|
+
o.resizeAreaWidth = options.resizeAreaWidth || 8;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* @private
|
|
555
|
+
* @field {boolean} resizableColumns */
|
|
556
|
+
o.resizableColumns = options.resizableColumns === undefined ? true : !!options.resizableColumns;
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* @private
|
|
560
|
+
* @field {boolean} movableColumns */
|
|
561
|
+
o.movableColumns = options.movableColumns === undefined ? true : !!options.movableColumns;
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* @private
|
|
565
|
+
* @field {number} sortableColumns */
|
|
566
|
+
o.sortableColumns = options.sortableColumns === undefined ? 1 : parseInt(options.sortableColumns, 10) || 1;
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* @private
|
|
570
|
+
* @field {boolean} adjustColumnWidthForSortArrow */
|
|
571
|
+
o.adjustColumnWidthForSortArrow = options.adjustColumnWidthForSortArrow === undefined ? true : !!options.adjustColumnWidthForSortArrow;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* @private
|
|
575
|
+
* @field {boolean} convertColumnWidthsToRelative */
|
|
576
|
+
o.convertColumnWidthsToRelative = options.convertColumnWidthsToRelative === undefined ? false : !!options.convertColumnWidthsToRelative;
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* @private
|
|
580
|
+
* @field {boolean} autoFillTableWidth */
|
|
581
|
+
o.autoFillTableWidth = options.autoFillTableWidth === undefined ? false : !!options.autoFillTableWidth;
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* @private
|
|
585
|
+
* @field {boolean} allowCancelSort */
|
|
586
|
+
o.allowCancelSort = options.allowCancelSort === undefined ? true : !!options.allowCancelSort;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* @private
|
|
590
|
+
* @field {string} cellClasses */
|
|
591
|
+
o.cellClasses = options.cellClasses === undefined ? '' : options.cellClasses;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* @private
|
|
595
|
+
* @field {string} resizerClassName */
|
|
596
|
+
o.resizerClassName = options.resizerClassName === undefined ? 'dgtable-resize' : options.resizerClassName;
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* @private
|
|
600
|
+
* @field {string} tableClassName */
|
|
601
|
+
o.tableClassName = options.tableClassName === undefined ? 'dgtable' : options.tableClassName;
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* @private
|
|
605
|
+
* @field {boolean} allowCellPreview */
|
|
606
|
+
o.allowCellPreview = options.allowCellPreview === undefined ? true : options.allowCellPreview;
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* @private
|
|
610
|
+
* @field {boolean} allowHeaderCellPreview */
|
|
611
|
+
o.allowHeaderCellPreview = options.allowHeaderCellPreview === undefined ? true : options.allowHeaderCellPreview;
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* @private
|
|
615
|
+
* @field {string} cellPreviewClassName */
|
|
616
|
+
o.cellPreviewClassName = options.cellPreviewClassName === undefined ? 'dgtable-cell-preview' : options.cellPreviewClassName;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* @private
|
|
620
|
+
* @field {boolean} cellPreviewAutoBackground */
|
|
621
|
+
o.cellPreviewAutoBackground = options.cellPreviewAutoBackground === undefined ? true : options.cellPreviewAutoBackground;
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* @private
|
|
625
|
+
* @field {function(columnName: string, descending: boolean, defaultComparator: function(a,b):number):(function(a,b):number)} onComparatorRequired */
|
|
626
|
+
o.onComparatorRequired = options.onComparatorRequired === undefined ? null : options.onComparatorRequired;
|
|
627
|
+
if (!o.onComparatorRequired && typeof options['comparatorCallback'] === 'function') {
|
|
628
|
+
o.onComparatorRequired = options['comparatorCallback'];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
o.customSortingProvider = options.customSortingProvider === undefined ? null : options.customSortingProvider;
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* @private
|
|
635
|
+
* @field {boolean} width */
|
|
636
|
+
o.width = options.width === undefined ? DGTable.Width.NONE : options.width;
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @private
|
|
640
|
+
* @field {boolean} relativeWidthGrowsToFillWidth */
|
|
641
|
+
o.relativeWidthGrowsToFillWidth = options.relativeWidthGrowsToFillWidth === undefined ? true : !!options.relativeWidthGrowsToFillWidth;
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* @private
|
|
645
|
+
* @field {boolean} relativeWidthShrinksToFillWidth */
|
|
646
|
+
o.relativeWidthShrinksToFillWidth = options.relativeWidthShrinksToFillWidth === undefined ? false : !!options.relativeWidthShrinksToFillWidth;
|
|
647
|
+
|
|
648
|
+
this.setCellFormatter(options.cellFormatter);
|
|
649
|
+
this.setHeaderCellFormatter(options.headerCellFormatter);
|
|
650
|
+
this.setFilter(options.filter);
|
|
651
|
+
|
|
652
|
+
/** @private
|
|
653
|
+
* @field {number} height */
|
|
654
|
+
o.height = options.height;
|
|
655
|
+
|
|
656
|
+
// Prepare columns
|
|
657
|
+
this.setColumns(options.columns || [], false);
|
|
658
|
+
|
|
659
|
+
// Set sorting columns
|
|
660
|
+
let sortColumns = [];
|
|
661
|
+
|
|
662
|
+
if (options.sortColumn) {
|
|
663
|
+
|
|
664
|
+
let tmpSortColumns = options.sortColumn;
|
|
665
|
+
|
|
666
|
+
if (tmpSortColumns && !Array.isArray(tmpSortColumns)) {
|
|
667
|
+
tmpSortColumns = [tmpSortColumns];
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (tmpSortColumns) {
|
|
671
|
+
for (let i = 0, len = tmpSortColumns.length; i < len; i++) {
|
|
672
|
+
let sortColumn = tmpSortColumns[i];
|
|
673
|
+
if (typeof sortColumn === 'string') {
|
|
674
|
+
sortColumn = { column: sortColumn, descending: false };
|
|
675
|
+
}
|
|
676
|
+
let col = p.columns.get(sortColumn.column);
|
|
677
|
+
if (!col) continue;
|
|
678
|
+
|
|
679
|
+
sortColumns.push({
|
|
680
|
+
column: sortColumn.column,
|
|
681
|
+
comparePath: col.comparePath || col.dataPath,
|
|
682
|
+
descending: sortColumn.descending
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/** @field {RowCollection} _rows */
|
|
689
|
+
p.rows = new RowCollection({ sortColumn: sortColumns });
|
|
690
|
+
p.rows.onComparatorRequired = (column, descending, defaultComparator) => {
|
|
691
|
+
if (o.onComparatorRequired) {
|
|
692
|
+
return o.onComparatorRequired(column, descending, defaultComparator);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
p.rows.customSortingProvider = (data, sort) => {
|
|
696
|
+
if (o.customSortingProvider) {
|
|
697
|
+
return o.customSortingProvider(data, sort);
|
|
698
|
+
} else {
|
|
699
|
+
return sort(data);
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
/** @private
|
|
704
|
+
* @field {RowCollection} _filteredRows */
|
|
705
|
+
p.filteredRows = null;
|
|
706
|
+
|
|
707
|
+
p.scrollbarWidth = 0;
|
|
708
|
+
p.lastVirtualScrollHeight = 0;
|
|
709
|
+
|
|
710
|
+
this._setupHovers();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
_setupHovers() {
|
|
714
|
+
const p = this._p;
|
|
715
|
+
|
|
716
|
+
/*
|
|
717
|
+
Setup hover mechanism.
|
|
718
|
+
We need this to be high performance, as there may be MANY cells to call this on, on creation and destruction.
|
|
719
|
+
*/
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* @param {MouseEvent} event
|
|
723
|
+
* @this {HTMLElement}
|
|
724
|
+
* */
|
|
725
|
+
let hoverMouseOverHandler = (event) => {
|
|
726
|
+
let cell = event.currentTarget;
|
|
727
|
+
let target = event.relatedTarget;
|
|
728
|
+
if (target === cell || cell.contains(target))
|
|
729
|
+
return;
|
|
730
|
+
if (cell[PreviewCellSymbol] && (
|
|
731
|
+
target === cell[PreviewCellSymbol] || cell[PreviewCellSymbol].contains(target)))
|
|
732
|
+
return;
|
|
733
|
+
this._cellMouseOverEvent(cell);
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* @param {MouseEvent} event
|
|
738
|
+
* @this {HTMLElement}
|
|
739
|
+
* */
|
|
740
|
+
let hoverMouseOutHandler = (event) => {
|
|
741
|
+
let cell = event.currentTarget[OriginalCellSymbol] || event.currentTarget;
|
|
742
|
+
let target = event.relatedTarget;
|
|
743
|
+
if (target === this || cell.contains(target))
|
|
744
|
+
return;
|
|
745
|
+
if (cell[PreviewCellSymbol] && (
|
|
746
|
+
target === cell[PreviewCellSymbol] || cell[PreviewCellSymbol].contains(target)))
|
|
747
|
+
return;
|
|
748
|
+
this._cellMouseOutEvent(cell);
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* @param {HTMLElement} el cell or header-cell
|
|
753
|
+
* */
|
|
754
|
+
p._bindCellHoverIn = (el) => {
|
|
755
|
+
if (!el[HoverInEventSymbol]) {
|
|
756
|
+
el.addEventListener('mouseover', el[HoverInEventSymbol] = hoverMouseOverHandler);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* @param {HTMLElement} el cell or header-cell
|
|
762
|
+
* */
|
|
763
|
+
p._unbindCellHoverIn = (el) => {
|
|
764
|
+
if (el[HoverInEventSymbol]) {
|
|
765
|
+
el.removeEventListener('mouseover', el[HoverInEventSymbol]);
|
|
766
|
+
el[HoverInEventSymbol] = null;
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* @param {HTMLElement} el cell or header-cell
|
|
772
|
+
* @returns {DGTable} self
|
|
773
|
+
* */
|
|
774
|
+
p._bindCellHoverOut = (el) => {
|
|
775
|
+
if (!el[HoverOutEventSymbol]) {
|
|
776
|
+
el.addEventListener('mouseout', el[HoverOutEventSymbol] = hoverMouseOutHandler);
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* @param {HTMLElement} el cell or header-cell
|
|
782
|
+
* @returns {DGTable} self
|
|
783
|
+
* */
|
|
784
|
+
p._unbindCellHoverOut = (el) => {
|
|
785
|
+
if (el[HoverOutEventSymbol]) {
|
|
786
|
+
el.removeEventListener('mouseout', el[HoverOutEventSymbol]);
|
|
787
|
+
el[HoverOutEventSymbol] = null;
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
_setupVirtualTable() {
|
|
793
|
+
const p = this._p,o = this._o;
|
|
794
|
+
|
|
795
|
+
const tableClassName = o.tableClassName,
|
|
796
|
+
rowClassName = tableClassName + '-row',
|
|
797
|
+
altRowClassName = tableClassName + '-row-alt',
|
|
798
|
+
cellClassName = tableClassName + '-cell';
|
|
799
|
+
|
|
800
|
+
let visibleColumns = p.visibleColumns,
|
|
801
|
+
colCount = visibleColumns.length;
|
|
802
|
+
|
|
803
|
+
p.notifyRendererOfColumnsConfig = () => {
|
|
804
|
+
visibleColumns = p.visibleColumns;
|
|
805
|
+
colCount = visibleColumns.length;
|
|
806
|
+
|
|
807
|
+
for (let colIndex = 0, column; colIndex < colCount; colIndex++) {
|
|
808
|
+
column = visibleColumns[colIndex];
|
|
809
|
+
column._finalWidth = column.actualWidthConsideringScrollbarWidth || column.actualWidth;
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
p.virtualListHelper = new VirtualListHelper({
|
|
814
|
+
list: p.table,
|
|
815
|
+
itemsParent: p.tbody,
|
|
816
|
+
autoVirtualWrapperWidth: false,
|
|
817
|
+
virtual: o.virtualTable,
|
|
818
|
+
buffer: o.rowsBufferSize,
|
|
819
|
+
estimatedItemHeight: o.estimatedRowHeight ? o.estimatedRowHeight : p.virtualRowHeight || 40,
|
|
820
|
+
itemElementCreatorFn: () => {
|
|
821
|
+
return createElement('div');
|
|
822
|
+
},
|
|
823
|
+
onItemRender: (row, virtualIndex) => {
|
|
824
|
+
const rows = p.filteredRows || p.rows,
|
|
825
|
+
isDataFiltered = !!p.filteredRows,
|
|
826
|
+
allowCellPreview = o.allowCellPreview;
|
|
827
|
+
|
|
828
|
+
row.className = rowClassName;
|
|
829
|
+
if (virtualIndex % 2 === 1)
|
|
830
|
+
row.className += ' ' + altRowClassName;
|
|
831
|
+
|
|
832
|
+
let rowData = rows[virtualIndex];
|
|
833
|
+
let rowIndex = isDataFiltered ? rowData['__i'] : virtualIndex;
|
|
834
|
+
|
|
835
|
+
row['vIndex'] = virtualIndex;
|
|
836
|
+
row['index'] = rowIndex;
|
|
837
|
+
|
|
838
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
839
|
+
let column = visibleColumns[colIndex];
|
|
840
|
+
let cell = createElement('div');
|
|
841
|
+
cell['columnName'] = column.name;
|
|
842
|
+
cell.setAttribute('data-column', column.name);
|
|
843
|
+
cell.className = cellClassName;
|
|
844
|
+
cell.style.width = column._finalWidth + 'px';
|
|
845
|
+
if (column.cellClasses) cell.className += ' ' + column.cellClasses;
|
|
846
|
+
if (allowCellPreview) {
|
|
847
|
+
p._bindCellHoverIn(cell);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
let cellInner = cell.appendChild(createElement('div'));
|
|
851
|
+
cellInner.innerHTML = this._getHtmlForCell(rowData, column);
|
|
852
|
+
|
|
853
|
+
row.appendChild(cell);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
row.addEventListener('click', row[RowClickEventSymbol] = (event) => {
|
|
857
|
+
this.emit('rowclick', {
|
|
858
|
+
event: event,
|
|
859
|
+
filteredRowIndex: virtualIndex,
|
|
860
|
+
rowIndex: rowIndex,
|
|
861
|
+
rowEl: row,
|
|
862
|
+
rowData: rowData
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
this.emit('rowcreate', {
|
|
867
|
+
filteredRowIndex: virtualIndex,
|
|
868
|
+
rowIndex: rowIndex,
|
|
869
|
+
rowEl: row,
|
|
870
|
+
rowData: rowData
|
|
871
|
+
});
|
|
872
|
+
},
|
|
873
|
+
|
|
874
|
+
onItemUnrender: (row) => {
|
|
875
|
+
if (row[RowClickEventSymbol]) {
|
|
876
|
+
row.removeEventListener('click', row[RowClickEventSymbol]);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
this._unbindCellEventsForRow(row);
|
|
880
|
+
|
|
881
|
+
this.emit('rowdestroy', row);
|
|
882
|
+
},
|
|
883
|
+
|
|
884
|
+
onScrollHeightChange: (height) => {
|
|
885
|
+
// only recalculate scrollbar width if height increased. we reset it in other situations.
|
|
886
|
+
if (height > p._lastVirtualScrollHeight && !p.scrollbarWidth) {
|
|
887
|
+
this._updateLastCellWidthFromScrollbar();
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
p._lastVirtualScrollHeight = height;
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
p.virtualListHelper.setCount((p.filteredRows ?? p.rows).length);
|
|
895
|
+
|
|
896
|
+
p.notifyRendererOfColumnsConfig();
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
trigger(eventName) {
|
|
900
|
+
const p = this._p;
|
|
901
|
+
if (!p) return;
|
|
902
|
+
|
|
903
|
+
let events = p.events;
|
|
904
|
+
|
|
905
|
+
if (hasOwnProperty.call(events, eventName)) {
|
|
906
|
+
let callbacks = events[eventName];
|
|
907
|
+
for (let i = 0; i < callbacks.length; i++) {
|
|
908
|
+
let item = callbacks[i];
|
|
909
|
+
if (item.once) {
|
|
910
|
+
callbacks.splice(i--, 1);
|
|
911
|
+
}
|
|
912
|
+
item.cb.apply(this, Array.prototype.slice.call(arguments, 1));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return this;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Register an event handler
|
|
921
|
+
* @param {(string|'*')?} event
|
|
922
|
+
* @param {function(any)} handler
|
|
923
|
+
* @returns {DGTable}
|
|
924
|
+
*/
|
|
925
|
+
on(/**string|'*'*/event, /**Function?*/handler) {
|
|
926
|
+
this._p.mitt.on(event, handler);
|
|
927
|
+
return this;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Register a one time event handler
|
|
932
|
+
* @param {(string|'*')?} event
|
|
933
|
+
* @param {function(any)} handler
|
|
934
|
+
* @returns {DGTable}
|
|
935
|
+
*/
|
|
936
|
+
once(/**string|'*'*/event, /**Function?*/handler) {
|
|
937
|
+
let wrapped = (value) => {
|
|
938
|
+
this._p.mitt.off(event, wrapped);
|
|
939
|
+
handler(value);
|
|
940
|
+
};
|
|
941
|
+
this._p.mitt.on(event, wrapped);
|
|
942
|
+
return this;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Remove an `handler` for `event`, all events for `event`, or all events completely.
|
|
947
|
+
* @param {(string|'*')?} event
|
|
948
|
+
* @param {function(any)} handler
|
|
949
|
+
* @returns {DGTable}
|
|
950
|
+
*/
|
|
951
|
+
off(/**(string|'*')?*/event, /**Function?*/handler) {
|
|
952
|
+
if (!event && !event) {
|
|
953
|
+
this._p.mitt.all.clear();
|
|
954
|
+
} else {
|
|
955
|
+
this._p.mitt.off(event, handler);
|
|
956
|
+
}
|
|
957
|
+
return this;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Emit an event
|
|
962
|
+
* @param {string} event
|
|
963
|
+
* @param {any?} value
|
|
964
|
+
* @returns {DGTable}
|
|
965
|
+
*/
|
|
966
|
+
emit(/**string|'*'*/event, /**any?*/value) {
|
|
967
|
+
this._p.mitt.emit(event, value);
|
|
968
|
+
return this;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Detect column width mode
|
|
973
|
+
* @private
|
|
974
|
+
* @param {Number|string} width
|
|
975
|
+
* @param {number} minWidth
|
|
976
|
+
* @returns {Object} parsed width
|
|
977
|
+
*/
|
|
978
|
+
_parseColumnWidth(width, minWidth) {
|
|
979
|
+
|
|
980
|
+
let widthSize = Math.max(0, parseFloat(width)),
|
|
981
|
+
widthMode = ColumnWidthMode.AUTO; // Default
|
|
982
|
+
|
|
983
|
+
if (widthSize > 0) {
|
|
984
|
+
// Well, it's sure is not AUTO, as we have a value
|
|
985
|
+
|
|
986
|
+
if (width === widthSize + '%') {
|
|
987
|
+
// It's a percentage!
|
|
988
|
+
|
|
989
|
+
widthMode = ColumnWidthMode.RELATIVE;
|
|
990
|
+
widthSize /= 100;
|
|
991
|
+
} else if (widthSize > 0 && widthSize < 1) {
|
|
992
|
+
// It's a decimal value, as a relative value!
|
|
993
|
+
|
|
994
|
+
widthMode = ColumnWidthMode.RELATIVE;
|
|
995
|
+
} else {
|
|
996
|
+
// It's an absolute size!
|
|
997
|
+
|
|
998
|
+
if (widthSize < minWidth) {
|
|
999
|
+
widthSize = minWidth;
|
|
1000
|
+
}
|
|
1001
|
+
widthMode = ColumnWidthMode.ABSOLUTE;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return { width: widthSize, mode: widthMode };
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* @private
|
|
1010
|
+
* @param {COLUMN_OPTIONS} columnData
|
|
1011
|
+
*/
|
|
1012
|
+
_initColumnFromData(columnData) {
|
|
1013
|
+
|
|
1014
|
+
let parsedWidth = this._parseColumnWidth(columnData.width, columnData.ignoreMin ? 0 : this._o.minColumnWidth);
|
|
1015
|
+
|
|
1016
|
+
let col = {
|
|
1017
|
+
name: columnData.name,
|
|
1018
|
+
label: columnData.label === undefined ? columnData.name : columnData.label,
|
|
1019
|
+
width: parsedWidth.width,
|
|
1020
|
+
widthMode: parsedWidth.mode,
|
|
1021
|
+
resizable: columnData.resizable === undefined ? true : columnData.resizable,
|
|
1022
|
+
sortable: columnData.sortable === undefined ? true : columnData.sortable,
|
|
1023
|
+
movable: columnData.movable === undefined ? true : columnData.movable,
|
|
1024
|
+
visible: columnData.visible === undefined ? true : columnData.visible,
|
|
1025
|
+
cellClasses: columnData.cellClasses === undefined ? this._o.cellClasses : columnData.cellClasses,
|
|
1026
|
+
ignoreMin: columnData.ignoreMin === undefined ? false : !!columnData.ignoreMin
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
col.dataPath = columnData.dataPath === undefined ? col.name : columnData.dataPath;
|
|
1030
|
+
col.comparePath = columnData.comparePath === undefined ? col.dataPath : columnData.comparePath;
|
|
1031
|
+
|
|
1032
|
+
if (typeof col.dataPath === 'string') {
|
|
1033
|
+
col.dataPath = col.dataPath.split('.');
|
|
1034
|
+
}
|
|
1035
|
+
if (typeof col.comparePath === 'string') {
|
|
1036
|
+
col.comparePath = col.comparePath.split('.');
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
return col;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Destroy, releasing all memory, events and DOM elements
|
|
1044
|
+
* @public
|
|
1045
|
+
* @expose
|
|
1046
|
+
*/
|
|
1047
|
+
destroy() {
|
|
1048
|
+
let p = this._p || {},
|
|
1049
|
+
el = this.el;
|
|
1050
|
+
|
|
1051
|
+
if (this.__removed) {
|
|
1052
|
+
return this;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
if (p.resizer) {
|
|
1056
|
+
p.resizer.remove();
|
|
1057
|
+
p.resizer = null;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
p.virtualListHelper?.destroy();
|
|
1061
|
+
p.virtualListHelper = null;
|
|
1062
|
+
|
|
1063
|
+
// Using quotes for __super__ because Google Closure Compiler has a bug...
|
|
1064
|
+
|
|
1065
|
+
this._destroyHeaderCells();
|
|
1066
|
+
|
|
1067
|
+
p.table?.remove();
|
|
1068
|
+
p.tbody?.remove();
|
|
1069
|
+
|
|
1070
|
+
if (p.workerListeners) {
|
|
1071
|
+
for (let j = 0; j < p.workerListeners.length; j++) {
|
|
1072
|
+
let worker = p.workerListeners[j];
|
|
1073
|
+
worker.worker.removeEventListener('message', worker.listener, false);
|
|
1074
|
+
}
|
|
1075
|
+
p.workerListeners.length = 0;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
p.rows.length = p.columns.length = 0;
|
|
1079
|
+
|
|
1080
|
+
if (p._deferredRender) {
|
|
1081
|
+
clearTimeout(p._deferredRender);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Cleanup
|
|
1085
|
+
for (let prop in this) {
|
|
1086
|
+
if (hasOwnProperty.call(this, prop)) {
|
|
1087
|
+
this[prop] = null;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
this.__removed = true;
|
|
1092
|
+
|
|
1093
|
+
if (el) {
|
|
1094
|
+
el.remove();
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
return this;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Backwards compatibility
|
|
1101
|
+
close() {
|
|
1102
|
+
this.destroy();
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Backwards compatibility
|
|
1106
|
+
remove() {
|
|
1107
|
+
this.destroy();
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* @private
|
|
1112
|
+
* @returns {DGTable} self
|
|
1113
|
+
*/
|
|
1114
|
+
_unbindCellEventsForTable() {
|
|
1115
|
+
const p = this._p;
|
|
1116
|
+
|
|
1117
|
+
if (p.headerRow) {
|
|
1118
|
+
for (let i = 0, rows = p.headerRow.childNodes, rowCount = rows.length; i < rowCount; i++) {
|
|
1119
|
+
let rowToClean = rows[i];
|
|
1120
|
+
for (let j = 0, cells = rowToClean.childNodes, cellCount = cells.length; j < cellCount; j++) {
|
|
1121
|
+
p._unbindCellHoverIn(cells[j]);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
return this;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* @private
|
|
1131
|
+
* @param {HTMLElement} rowToClean
|
|
1132
|
+
* @returns {DGTable} self
|
|
1133
|
+
*/
|
|
1134
|
+
_unbindCellEventsForRow(rowToClean) {
|
|
1135
|
+
const p = this._p;
|
|
1136
|
+
for (let i = 0, cells = rowToClean.childNodes, cellCount = cells.length; i < cellCount; i++) {
|
|
1137
|
+
p._unbindCellHoverIn(cells[i]);
|
|
1138
|
+
}
|
|
1139
|
+
return this;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* @public
|
|
1144
|
+
* @expose
|
|
1145
|
+
* @returns {DGTable} self
|
|
1146
|
+
*/
|
|
1147
|
+
render() {
|
|
1148
|
+
const o = this._o,p = this._p;
|
|
1149
|
+
|
|
1150
|
+
if (!this.el.offsetParent) {
|
|
1151
|
+
if (!p._deferredRender) {
|
|
1152
|
+
p._deferredRender = setTimeout(() => {
|
|
1153
|
+
p._deferredRender = null;
|
|
1154
|
+
if (!this.__removed && this.el.offsetParent) {
|
|
1155
|
+
this.render();
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return this;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
if (p.tableSkeletonNeedsRendering === true) {
|
|
1164
|
+
p.tableSkeletonNeedsRendering = false;
|
|
1165
|
+
|
|
1166
|
+
if (o.width === DGTable.Width.AUTO) {
|
|
1167
|
+
// We need to do this to return to the specified widths instead. The arrows added to the column widths...
|
|
1168
|
+
this._clearSortArrows();
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
let lastScrollTop = p.table && p.table.parentNode ? p.table.scrollTop : NaN,
|
|
1172
|
+
lastScrollHorz = p.table && p.table.parentNode ? ScrollHelper_js.getScrollHorz(p.table) : NaN;
|
|
1173
|
+
|
|
1174
|
+
this._renderSkeletonBase().
|
|
1175
|
+
_renderSkeletonBody().
|
|
1176
|
+
tableWidthChanged(true, false) // Take this chance to calculate required column widths
|
|
1177
|
+
._renderSkeletonHeaderCells();
|
|
1178
|
+
|
|
1179
|
+
p.virtualListHelper.setCount((p.filteredRows ?? p.rows).length);
|
|
1180
|
+
|
|
1181
|
+
this._updateVirtualHeight();
|
|
1182
|
+
this._updateLastCellWidthFromScrollbar(true);
|
|
1183
|
+
this._updateTableWidth(true);
|
|
1184
|
+
|
|
1185
|
+
// Show sort arrows
|
|
1186
|
+
for (let i = 0; i < p.rows.sortColumn.length; i++) {
|
|
1187
|
+
this._showSortArrow(p.rows.sortColumn[i].column, p.rows.sortColumn[i].descending);
|
|
1188
|
+
}
|
|
1189
|
+
if (o.adjustColumnWidthForSortArrow && p.rows.sortColumn.length) {
|
|
1190
|
+
this.tableWidthChanged(true);
|
|
1191
|
+
} else if (!o.virtualTable) {
|
|
1192
|
+
this.tableWidthChanged();
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (!isNaN(lastScrollTop))
|
|
1196
|
+
p.table.scrollTop = lastScrollTop;
|
|
1197
|
+
|
|
1198
|
+
if (!isNaN(lastScrollHorz)) {
|
|
1199
|
+
ScrollHelper_js.setScrollHorz(p.table, lastScrollHorz);
|
|
1200
|
+
ScrollHelper_js.setScrollHorz(p.header, lastScrollHorz);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
this.emit('renderskeleton');
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
p.virtualListHelper.render();
|
|
1207
|
+
|
|
1208
|
+
this.emit('render');
|
|
1209
|
+
return this;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* Forces a full render of the table
|
|
1214
|
+
* @public
|
|
1215
|
+
* @expose
|
|
1216
|
+
* @param {boolean=true} render - Should render now?
|
|
1217
|
+
* @returns {DGTable} self
|
|
1218
|
+
*/
|
|
1219
|
+
clearAndRender(render) {
|
|
1220
|
+
let p = this._p;
|
|
1221
|
+
|
|
1222
|
+
p.tableSkeletonNeedsRendering = true;
|
|
1223
|
+
p.notifyRendererOfColumnsConfig?.();
|
|
1224
|
+
|
|
1225
|
+
if (render === undefined || render) {
|
|
1226
|
+
this.render();
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
return this;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Calculate the size required for the table body width (which is the row's width)
|
|
1234
|
+
* @private
|
|
1235
|
+
* @returns {number} calculated width
|
|
1236
|
+
*/
|
|
1237
|
+
_calculateTbodyWidth() {
|
|
1238
|
+
const p = this._p;
|
|
1239
|
+
|
|
1240
|
+
let tableClassName = this._o.tableClassName,
|
|
1241
|
+
rowClassName = tableClassName + '-row',
|
|
1242
|
+
cellClassName = tableClassName + '-cell',
|
|
1243
|
+
visibleColumns = p.visibleColumns,
|
|
1244
|
+
colCount = visibleColumns.length;
|
|
1245
|
+
|
|
1246
|
+
const row = createElement('div');
|
|
1247
|
+
row.className = rowClassName;
|
|
1248
|
+
row.style.float = 'left';
|
|
1249
|
+
|
|
1250
|
+
let sumActualWidth = 0;
|
|
1251
|
+
|
|
1252
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1253
|
+
const column = visibleColumns[colIndex];
|
|
1254
|
+
const cell = createElement('div');
|
|
1255
|
+
cell.className = cellClassName;
|
|
1256
|
+
cell.style.width = column.actualWidth + 'px';
|
|
1257
|
+
if (column.cellClasses) cell.className += ' ' + column.cellClasses;
|
|
1258
|
+
cell.appendChild(createElement('div'));
|
|
1259
|
+
row.appendChild(cell);
|
|
1260
|
+
sumActualWidth += column.actualWidth;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const thisWrapper = createElement('div');
|
|
1264
|
+
thisWrapper.className = this.el.className;
|
|
1265
|
+
Css_js.setCssProps(thisWrapper, {
|
|
1266
|
+
'z-index': -1,
|
|
1267
|
+
'position': 'absolute',
|
|
1268
|
+
'left': '0',
|
|
1269
|
+
'top': '-9999px',
|
|
1270
|
+
'float': 'left',
|
|
1271
|
+
'width': '1px',
|
|
1272
|
+
'overflow': 'hidden'
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
const tableDiv = createElement('div');
|
|
1276
|
+
tableDiv.className = tableClassName;
|
|
1277
|
+
thisWrapper.appendChild(tableDiv);
|
|
1278
|
+
const tableBodyDiv = createElement('div');
|
|
1279
|
+
tableBodyDiv.className = tableClassName + '-body';
|
|
1280
|
+
tableBodyDiv.style.width = sumActualWidth + 10000 + 'px';
|
|
1281
|
+
tableDiv.appendChild(tableBodyDiv);
|
|
1282
|
+
tableBodyDiv.appendChild(row);
|
|
1283
|
+
|
|
1284
|
+
document.body.appendChild(thisWrapper);
|
|
1285
|
+
|
|
1286
|
+
const fractionTest = createElement('div');
|
|
1287
|
+
Css_js.setCssProps(fractionTest, {
|
|
1288
|
+
border: '1.5px solid #000',
|
|
1289
|
+
width: '0',
|
|
1290
|
+
height: '0',
|
|
1291
|
+
position: 'absolute',
|
|
1292
|
+
left: '0',
|
|
1293
|
+
top: '-9999px'
|
|
1294
|
+
});
|
|
1295
|
+
document.body.appendChild(fractionTest);
|
|
1296
|
+
let fractionValue = parseFloat(getComputedStyle(fractionTest).borderWidth);
|
|
1297
|
+
let hasFractions = Math.round(fractionValue) !== fractionValue;
|
|
1298
|
+
fractionTest.remove();
|
|
1299
|
+
|
|
1300
|
+
let width = Css_js.getElementWidth(row, true, true, true);
|
|
1301
|
+
width -= p.scrollbarWidth || 0;
|
|
1302
|
+
|
|
1303
|
+
if (hasFractions) {
|
|
1304
|
+
width++;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
thisWrapper.remove();
|
|
1308
|
+
return width;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Sets the columns of the table
|
|
1313
|
+
* @public
|
|
1314
|
+
* @expose
|
|
1315
|
+
* @param {COLUMN_OPTIONS[]} columns - Column definitions array
|
|
1316
|
+
* @param {boolean=true} render - Should render now?
|
|
1317
|
+
* @returns {DGTable} self
|
|
1318
|
+
*/
|
|
1319
|
+
setColumns(columns, render) {
|
|
1320
|
+
const p = this._p;
|
|
1321
|
+
|
|
1322
|
+
columns = columns || [];
|
|
1323
|
+
|
|
1324
|
+
let normalizedCols = new ColumnCollection();
|
|
1325
|
+
for (let i = 0, order = 0; i < columns.length; i++) {
|
|
1326
|
+
|
|
1327
|
+
let columnData = columns[i];
|
|
1328
|
+
let normalizedColumn = this._initColumnFromData(columnData);
|
|
1329
|
+
|
|
1330
|
+
if (columnData.order !== undefined) {
|
|
1331
|
+
if (columnData.order > order) {
|
|
1332
|
+
order = columnData.order + 1;
|
|
1333
|
+
}
|
|
1334
|
+
normalizedColumn.order = columnData.order;
|
|
1335
|
+
} else {
|
|
1336
|
+
normalizedColumn.order = order++;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
normalizedCols.push(normalizedColumn);
|
|
1340
|
+
}
|
|
1341
|
+
normalizedCols.normalizeOrder();
|
|
1342
|
+
|
|
1343
|
+
p.columns = normalizedCols;
|
|
1344
|
+
p.visibleColumns = normalizedCols.getVisibleColumns();
|
|
1345
|
+
|
|
1346
|
+
this._ensureVisibleColumns().clearAndRender(render);
|
|
1347
|
+
|
|
1348
|
+
return this;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Add a column to the table
|
|
1353
|
+
* @public
|
|
1354
|
+
* @expose
|
|
1355
|
+
* @param {COLUMN_OPTIONS} columnData column properties
|
|
1356
|
+
* @param {string|number} [before=-1] column name or order to be inserted before
|
|
1357
|
+
* @param {boolean=true} render - Should render now?
|
|
1358
|
+
* @returns {DGTable} self
|
|
1359
|
+
*/
|
|
1360
|
+
addColumn(columnData, before, render) {
|
|
1361
|
+
const p = this._p;
|
|
1362
|
+
let columns = p.columns;
|
|
1363
|
+
|
|
1364
|
+
if (columnData && !columns.get(columnData.name)) {
|
|
1365
|
+
let beforeColumn = null;
|
|
1366
|
+
if (before !== undefined) {
|
|
1367
|
+
beforeColumn = columns.get(before) || columns.getByOrder(before);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
let column = this._initColumnFromData(columnData);
|
|
1371
|
+
column.order = beforeColumn ? beforeColumn.order : columns.getMaxOrder() + 1;
|
|
1372
|
+
|
|
1373
|
+
for (let i = columns.getMaxOrder(), to = column.order; i >= to; i--) {
|
|
1374
|
+
let col = columns.getByOrder(i);
|
|
1375
|
+
if (col) {
|
|
1376
|
+
col.order++;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
columns.push(column);
|
|
1381
|
+
columns.normalizeOrder();
|
|
1382
|
+
|
|
1383
|
+
p.visibleColumns = columns.getVisibleColumns();
|
|
1384
|
+
this._ensureVisibleColumns().clearAndRender(render);
|
|
1385
|
+
|
|
1386
|
+
this.emit('addcolumn', column.name);
|
|
1387
|
+
}
|
|
1388
|
+
return this;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* Remove a column from the table
|
|
1393
|
+
* @public
|
|
1394
|
+
* @expose
|
|
1395
|
+
* @param {string} column column name
|
|
1396
|
+
* @param {boolean=true} render - Should render now?
|
|
1397
|
+
* @returns {DGTable} self
|
|
1398
|
+
*/
|
|
1399
|
+
removeColumn(column, render) {
|
|
1400
|
+
const p = this._p;
|
|
1401
|
+
let columns = p.columns;
|
|
1402
|
+
|
|
1403
|
+
let colIdx = columns.indexOf(column);
|
|
1404
|
+
if (colIdx > -1) {
|
|
1405
|
+
columns.splice(colIdx, 1);
|
|
1406
|
+
columns.normalizeOrder();
|
|
1407
|
+
|
|
1408
|
+
p.visibleColumns = columns.getVisibleColumns();
|
|
1409
|
+
this._ensureVisibleColumns().clearAndRender(render);
|
|
1410
|
+
|
|
1411
|
+
this.emit('removecolumn', column);
|
|
1412
|
+
}
|
|
1413
|
+
return this;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Sets a new cell formatter.
|
|
1418
|
+
* @public
|
|
1419
|
+
* @expose
|
|
1420
|
+
* @param {function(value: *, columnName: string, row: Object):string|null} [formatter=null] - The cell formatter. Should return an HTML.
|
|
1421
|
+
* @returns {DGTable} self
|
|
1422
|
+
*/
|
|
1423
|
+
setCellFormatter(formatter) {
|
|
1424
|
+
if (!formatter) {
|
|
1425
|
+
formatter = (val) => typeof val === 'string' ? htmlEncode(val) : val;
|
|
1426
|
+
formatter[IsSafeSymbol] = true;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* @private
|
|
1431
|
+
* @field {Function} cellFormatter */
|
|
1432
|
+
this._o.cellFormatter = formatter;
|
|
1433
|
+
|
|
1434
|
+
return this;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/**
|
|
1438
|
+
* Sets a new header cell formatter.
|
|
1439
|
+
* @public
|
|
1440
|
+
* @expose
|
|
1441
|
+
* @param {function(label: string, columnName: string):string|null} [formatter=null] - The cell formatter. Should return an HTML.
|
|
1442
|
+
* @returns {DGTable} self
|
|
1443
|
+
*/
|
|
1444
|
+
setHeaderCellFormatter(formatter) {
|
|
1445
|
+
/**
|
|
1446
|
+
* @private
|
|
1447
|
+
* @field {Function} headerCellFormatter */
|
|
1448
|
+
this._o.headerCellFormatter = formatter || function (val) {
|
|
1449
|
+
return typeof val === 'string' ? htmlEncode(val) : val;
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
return this;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* @public
|
|
1457
|
+
* @expose
|
|
1458
|
+
* @param {function(row:Object,args:Object):boolean|null} [filterFunc=null] - The filter function to work with filters. Default is a by-colum filter.
|
|
1459
|
+
* @returns {DGTable} self
|
|
1460
|
+
*/
|
|
1461
|
+
setFilter(filterFunc) {
|
|
1462
|
+
/** @private
|
|
1463
|
+
* @field {Function} filter */
|
|
1464
|
+
this._o.filter = filterFunc;
|
|
1465
|
+
return this;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* @public
|
|
1470
|
+
* @expose
|
|
1471
|
+
* @param {Object|null} args - Options to pass to the filter function
|
|
1472
|
+
* @returns {DGTable} self
|
|
1473
|
+
*/
|
|
1474
|
+
filter(args) {
|
|
1475
|
+
const p = this._p;
|
|
1476
|
+
|
|
1477
|
+
let filterFunc = this._o.filter || ByColumnFilter;
|
|
1478
|
+
|
|
1479
|
+
// Deprecated use of older by-column filter
|
|
1480
|
+
if (typeof arguments[0] === 'string' && typeof arguments[1] === 'string') {
|
|
1481
|
+
args = {
|
|
1482
|
+
column: arguments[0],
|
|
1483
|
+
keyword: arguments[1],
|
|
1484
|
+
caseSensitive: arguments[2]
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
let hadFilter = !!p.filteredRows;
|
|
1489
|
+
if (p.filteredRows) {
|
|
1490
|
+
p.filteredRows = null; // Allow releasing array memory now
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// Shallow-clone the args, as the filter function may want to modify it for keeping state
|
|
1494
|
+
p.filterArgs = args == null ? null : typeof args === 'object' && !Array.isArray(args) ? Object.assign({}, args) : args;
|
|
1495
|
+
|
|
1496
|
+
if (p.filterArgs !== null) {
|
|
1497
|
+
p.filteredRows = p.rows.filteredCollection(filterFunc, p.filterArgs);
|
|
1498
|
+
|
|
1499
|
+
if (hadFilter || p.filteredRows) {
|
|
1500
|
+
this.clearAndRender();
|
|
1501
|
+
this.emit('filter', args);
|
|
1502
|
+
}
|
|
1503
|
+
} else
|
|
1504
|
+
{
|
|
1505
|
+
p.filterArgs = null;
|
|
1506
|
+
p.filteredRows = null;
|
|
1507
|
+
this.clearAndRender();
|
|
1508
|
+
this.emit('filterclear', {});
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
return this;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
/**
|
|
1515
|
+
* @public
|
|
1516
|
+
* @expose
|
|
1517
|
+
* @returns {DGTable} self
|
|
1518
|
+
*/
|
|
1519
|
+
clearFilter() {
|
|
1520
|
+
const p = this._p;
|
|
1521
|
+
|
|
1522
|
+
if (p.filteredRows) {
|
|
1523
|
+
p.filterArgs = null;
|
|
1524
|
+
p.filteredRows = null;
|
|
1525
|
+
this.clearAndRender();
|
|
1526
|
+
this.emit('filterclear', {});
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
return this;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* @private
|
|
1534
|
+
* @returns {DGTable} self
|
|
1535
|
+
*/
|
|
1536
|
+
_refilter() {
|
|
1537
|
+
const p = this._p;
|
|
1538
|
+
|
|
1539
|
+
if (p.filteredRows && p.filterArgs) {
|
|
1540
|
+
let filterFunc = this._o.filter || ByColumnFilter;
|
|
1541
|
+
p.filteredRows = p.rows.filteredCollection(filterFunc, p.filterArgs);
|
|
1542
|
+
}
|
|
1543
|
+
return this;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
/**
|
|
1547
|
+
* Set a new label to a column
|
|
1548
|
+
* @public
|
|
1549
|
+
* @expose
|
|
1550
|
+
* @param {string} column Name of the column
|
|
1551
|
+
* @param {string} label New label for the column
|
|
1552
|
+
* @returns {DGTable} self
|
|
1553
|
+
*/
|
|
1554
|
+
setColumnLabel(column, label) {
|
|
1555
|
+
const p = this._p;
|
|
1556
|
+
|
|
1557
|
+
let col = p.columns.get(column);
|
|
1558
|
+
if (col) {
|
|
1559
|
+
col.label = label === undefined ? col.name : label;
|
|
1560
|
+
|
|
1561
|
+
if (col.element) {
|
|
1562
|
+
for (let i = 0; i < col.element.firstChild.childNodes.length; i++) {
|
|
1563
|
+
let node = col.element.firstChild.childNodes[i];
|
|
1564
|
+
if (node.nodeType === 3) {
|
|
1565
|
+
node.textContent = col.label;
|
|
1566
|
+
break;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return this;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* Move a column to a new position
|
|
1576
|
+
* @public
|
|
1577
|
+
* @expose
|
|
1578
|
+
* @param {string|number} src Name or position of the column to be moved
|
|
1579
|
+
* @param {string|number} dest Name of the column currently in the desired position, or the position itself
|
|
1580
|
+
* @param {boolean} [visibleOnly=true] Should consider only visible columns and visible-relative indexes
|
|
1581
|
+
* @returns {DGTable} self
|
|
1582
|
+
*/
|
|
1583
|
+
moveColumn(src, dest) {let visibleOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
1584
|
+
const o = this._o,p = this._p;
|
|
1585
|
+
|
|
1586
|
+
let columns = p.columns,
|
|
1587
|
+
col,destCol;
|
|
1588
|
+
|
|
1589
|
+
let columnsArray = visibleOnly ? p.visibleColumns : columns.getColumns();
|
|
1590
|
+
|
|
1591
|
+
if (typeof src === 'string') {
|
|
1592
|
+
col = columns.get(src);
|
|
1593
|
+
} else if (typeof src === 'number') {
|
|
1594
|
+
col = columnsArray[src];
|
|
1595
|
+
}
|
|
1596
|
+
if (typeof dest === 'string') {
|
|
1597
|
+
destCol = columns.get(dest);
|
|
1598
|
+
} else if (typeof dest === 'number') {
|
|
1599
|
+
destCol = columnsArray[dest];
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
if (col && destCol && src !== dest) {
|
|
1603
|
+
let srcOrder = col.order,destOrder = destCol.order;
|
|
1604
|
+
|
|
1605
|
+
let visibleColumns = columns.moveColumn(col, destCol).getVisibleColumns();
|
|
1606
|
+
|
|
1607
|
+
if (p.visibleColumns.length !== visibleColumns.length ||
|
|
1608
|
+
p.visibleColumns.some((x, i) => x !== visibleColumns[i])) {
|
|
1609
|
+
|
|
1610
|
+
p.visibleColumns = visibleColumns;
|
|
1611
|
+
this._ensureVisibleColumns();
|
|
1612
|
+
|
|
1613
|
+
if (o.virtualTable) {
|
|
1614
|
+
this.clearAndRender();
|
|
1615
|
+
} else {
|
|
1616
|
+
const headerCells = DomCompat_js.scopedSelectorAll(p.headerRow, `>div.${o.tableClassName}-header-cell`);
|
|
1617
|
+
let beforePos = srcOrder < destOrder ? destOrder + 1 : destOrder,
|
|
1618
|
+
fromPos = srcOrder;
|
|
1619
|
+
headerCells[0].parentNode.insertBefore(headerCells[fromPos], headerCells[beforePos]);
|
|
1620
|
+
|
|
1621
|
+
let srcWidth = p.visibleColumns[srcOrder];
|
|
1622
|
+
srcWidth = (srcWidth.actualWidthConsideringScrollbarWidth || srcWidth.actualWidth) + 'px';
|
|
1623
|
+
let destWidth = p.visibleColumns[destOrder];
|
|
1624
|
+
destWidth = (destWidth.actualWidthConsideringScrollbarWidth || destWidth.actualWidth) + 'px';
|
|
1625
|
+
|
|
1626
|
+
let tbodyChildren = p.tbody.childNodes;
|
|
1627
|
+
for (let i = 0, count = tbodyChildren.length; i < count; i++) {
|
|
1628
|
+
let row = tbodyChildren[i];
|
|
1629
|
+
if (row.nodeType !== 1) continue;
|
|
1630
|
+
row.insertBefore(row.childNodes[fromPos], row.childNodes[beforePos]);
|
|
1631
|
+
row.childNodes[destOrder].firstChild.style.width = destWidth;
|
|
1632
|
+
row.childNodes[srcOrder].firstChild.style.width = srcWidth;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
this.emit('movecolumn', { name: col.name, src: srcOrder, dest: destOrder });
|
|
1638
|
+
}
|
|
1639
|
+
return this;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
/**
|
|
1643
|
+
* Sort the table
|
|
1644
|
+
* @public
|
|
1645
|
+
* @expose
|
|
1646
|
+
* @param {string?} column Name of the column to sort on (or null to remove sort arrow)
|
|
1647
|
+
* @param {boolean=} descending Sort in descending order
|
|
1648
|
+
* @param {boolean} [add=false] Should this sort be on top of the existing sort? (For multiple column sort)
|
|
1649
|
+
* @returns {DGTable} self
|
|
1650
|
+
*/
|
|
1651
|
+
sort(column, descending, add) {
|
|
1652
|
+
const o = this._o,p = this._p;
|
|
1653
|
+
|
|
1654
|
+
let columns = p.columns,
|
|
1655
|
+
col = columns.get(column);
|
|
1656
|
+
|
|
1657
|
+
let currentSort = p.rows.sortColumn;
|
|
1658
|
+
|
|
1659
|
+
if (col) {
|
|
1660
|
+
if (add) {// Add the sort to current sort stack
|
|
1661
|
+
|
|
1662
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
1663
|
+
if (currentSort[i].column === col.name) {
|
|
1664
|
+
if (i < currentSort.length - 1) {
|
|
1665
|
+
currentSort.length = 0;
|
|
1666
|
+
} else {
|
|
1667
|
+
descending = currentSort[currentSort.length - 1].descending;
|
|
1668
|
+
currentSort.splice(currentSort.length - 1, 1);
|
|
1669
|
+
}
|
|
1670
|
+
break;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
if (o.sortableColumns > 0 /* allow manual sort when disabled */ && currentSort.length >= o.sortableColumns || currentSort.length >= p.visibleColumns.length) {
|
|
1674
|
+
currentSort.length = 0;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
} else {// Sort only by this column
|
|
1678
|
+
currentSort.length = 0;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// Default to ascending
|
|
1682
|
+
descending = descending === undefined ? false : descending;
|
|
1683
|
+
|
|
1684
|
+
// Set the required column in the front of the stack
|
|
1685
|
+
currentSort.push({
|
|
1686
|
+
column: col.name,
|
|
1687
|
+
comparePath: col.comparePath || col.dataPath,
|
|
1688
|
+
descending: !!descending
|
|
1689
|
+
});
|
|
1690
|
+
} else {
|
|
1691
|
+
currentSort.length = 0;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
this._clearSortArrows();
|
|
1695
|
+
|
|
1696
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
1697
|
+
this._showSortArrow(currentSort[i].column, currentSort[i].descending);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
if (o.adjustColumnWidthForSortArrow && !p.tableSkeletonNeedsRendering) {
|
|
1701
|
+
this.tableWidthChanged(true);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
p.rows.sortColumn = currentSort;
|
|
1705
|
+
|
|
1706
|
+
let comparator;
|
|
1707
|
+
if (currentSort.length) {
|
|
1708
|
+
comparator = p.rows.sort(!!p.filteredRows);
|
|
1709
|
+
if (p.filteredRows) {
|
|
1710
|
+
p.filteredRows.sort(!!p.filteredRows);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
if (p.virtualListHelper)
|
|
1715
|
+
p.virtualListHelper.invalidate().render();
|
|
1716
|
+
|
|
1717
|
+
// Build output for event, with option names that will survive compilers
|
|
1718
|
+
let sorts = [];
|
|
1719
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
1720
|
+
sorts.push({ 'column': currentSort[i].column, 'descending': currentSort[i].descending });
|
|
1721
|
+
}
|
|
1722
|
+
this.emit('sort', { sorts: sorts, comparator: comparator });
|
|
1723
|
+
|
|
1724
|
+
return this;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
/**
|
|
1728
|
+
* Re-sort the table using current sort specifiers
|
|
1729
|
+
* @public
|
|
1730
|
+
* @expose
|
|
1731
|
+
* @returns {DGTable} self
|
|
1732
|
+
*/
|
|
1733
|
+
resort() {
|
|
1734
|
+
const p = this._p;
|
|
1735
|
+
let columns = p.columns;
|
|
1736
|
+
|
|
1737
|
+
let currentSort = p.rows.sortColumn;
|
|
1738
|
+
if (currentSort.length) {
|
|
1739
|
+
|
|
1740
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
1741
|
+
if (!columns.get(currentSort[i].column)) {
|
|
1742
|
+
currentSort.splice(i--, 1);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
let comparator;
|
|
1747
|
+
p.rows.sortColumn = currentSort;
|
|
1748
|
+
if (currentSort.length) {
|
|
1749
|
+
comparator = p.rows.sort(!!p.filteredRows);
|
|
1750
|
+
if (p.filteredRows) {
|
|
1751
|
+
p.filteredRows.sort(!!p.filteredRows);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// Build output for event, with option names that will survive compilers
|
|
1756
|
+
let sorts = [];
|
|
1757
|
+
for (let i = 0; i < currentSort.length; i++) {
|
|
1758
|
+
sorts.push({ 'column': currentSort[i].column, 'descending': currentSort[i].descending });
|
|
1759
|
+
}
|
|
1760
|
+
this.emit('sort', { sorts: sorts, resort: true, comparator: comparator });
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
return this;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* Make sure there's at least one column visible
|
|
1768
|
+
* @private
|
|
1769
|
+
* @expose
|
|
1770
|
+
* @returns {DGTable} self
|
|
1771
|
+
*/
|
|
1772
|
+
_ensureVisibleColumns() {
|
|
1773
|
+
const p = this._p;
|
|
1774
|
+
|
|
1775
|
+
if (p.visibleColumns.length === 0 && p.columns.length) {
|
|
1776
|
+
p.columns[0].visible = true;
|
|
1777
|
+
p.visibleColumns.push(p.columns[0]);
|
|
1778
|
+
this.emit('showcolumn', p.columns[0].name);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
return this;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
/**
|
|
1785
|
+
* Show or hide a column
|
|
1786
|
+
* @public
|
|
1787
|
+
* @expose
|
|
1788
|
+
* @param {string} column Unique column name
|
|
1789
|
+
* @param {boolean} visible New visibility mode for the column
|
|
1790
|
+
* @returns {DGTable} self
|
|
1791
|
+
*/
|
|
1792
|
+
setColumnVisible(column, visible) {
|
|
1793
|
+
const p = this._p;
|
|
1794
|
+
|
|
1795
|
+
let col = p.columns.get(column);
|
|
1796
|
+
|
|
1797
|
+
//noinspection PointlessBooleanExpressionJS
|
|
1798
|
+
visible = !!visible;
|
|
1799
|
+
|
|
1800
|
+
if (col && !!col.visible !== visible) {
|
|
1801
|
+
col.visible = visible;
|
|
1802
|
+
p.visibleColumns = p.columns.getVisibleColumns();
|
|
1803
|
+
this.emit(visible ? 'showcolumn' : 'hidecolumn', column);
|
|
1804
|
+
this._ensureVisibleColumns();
|
|
1805
|
+
this.clearAndRender();
|
|
1806
|
+
}
|
|
1807
|
+
return this;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/**
|
|
1811
|
+
* Get the visibility mode of a column
|
|
1812
|
+
* @public
|
|
1813
|
+
* @expose
|
|
1814
|
+
* @returns {boolean} true if visible
|
|
1815
|
+
*/
|
|
1816
|
+
isColumnVisible(column) {
|
|
1817
|
+
const p = this._p;
|
|
1818
|
+
let col = p.columns.get(column);
|
|
1819
|
+
if (col) {
|
|
1820
|
+
return col.visible;
|
|
1821
|
+
}
|
|
1822
|
+
return false;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/**
|
|
1826
|
+
* Globally set the minimum column width
|
|
1827
|
+
* @public
|
|
1828
|
+
* @expose
|
|
1829
|
+
* @param {number} minColumnWidth Minimum column width
|
|
1830
|
+
* @returns {DGTable} self
|
|
1831
|
+
*/
|
|
1832
|
+
setMinColumnWidth(minColumnWidth) {
|
|
1833
|
+
let o = this._o;
|
|
1834
|
+
minColumnWidth = Math.max(minColumnWidth, 0);
|
|
1835
|
+
if (o.minColumnWidth !== minColumnWidth) {
|
|
1836
|
+
o.minColumnWidth = minColumnWidth;
|
|
1837
|
+
this.tableWidthChanged(true);
|
|
1838
|
+
}
|
|
1839
|
+
return this;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/**
|
|
1843
|
+
* Get the current minimum column width
|
|
1844
|
+
* @public
|
|
1845
|
+
* @expose
|
|
1846
|
+
* @returns {number} Minimum column width
|
|
1847
|
+
*/
|
|
1848
|
+
getMinColumnWidth() {
|
|
1849
|
+
return this._o.minColumnWidth;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* Set the limit on concurrent columns sorted
|
|
1854
|
+
* @public
|
|
1855
|
+
* @expose
|
|
1856
|
+
* @param {number} sortableColumns How many sortable columns to allow?
|
|
1857
|
+
* @returns {DGTable} self
|
|
1858
|
+
*/
|
|
1859
|
+
setSortableColumns(sortableColumns) {
|
|
1860
|
+
const p = this._p,o = this._o;
|
|
1861
|
+
if (o.sortableColumns !== sortableColumns) {
|
|
1862
|
+
o.sortableColumns = sortableColumns;
|
|
1863
|
+
if (p.table) {
|
|
1864
|
+
const headerCells = DomCompat_js.scopedSelectorAll(p.headerRow, `>div.${o.tableClassName}-header-cell`);
|
|
1865
|
+
for (let i = 0, len = headerCells.length; i < len; i++) {
|
|
1866
|
+
const cell = headerCells[i];
|
|
1867
|
+
cell.classList[o.sortableColumns > 0 && p.visibleColumns[i].sortable ? 'add' : 'remove']('sortable');
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return this;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
/**
|
|
1875
|
+
* Get the limit on concurrent columns sorted
|
|
1876
|
+
* @public
|
|
1877
|
+
* @expose
|
|
1878
|
+
* @returns {number} How many sortable columns are allowed?
|
|
1879
|
+
*/
|
|
1880
|
+
getSortableColumns() {
|
|
1881
|
+
return this._o.sortableColumns;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
/**
|
|
1885
|
+
* @public
|
|
1886
|
+
* @expose
|
|
1887
|
+
* @param {boolean?} movableColumns=true are the columns movable?
|
|
1888
|
+
* @returns {DGTable} self
|
|
1889
|
+
*/
|
|
1890
|
+
setMovableColumns(movableColumns) {
|
|
1891
|
+
let o = this._o;
|
|
1892
|
+
//noinspection PointlessBooleanExpressionJS
|
|
1893
|
+
movableColumns = movableColumns === undefined ? true : !!movableColumns;
|
|
1894
|
+
if (o.movableColumns !== movableColumns) {
|
|
1895
|
+
o.movableColumns = movableColumns;
|
|
1896
|
+
}
|
|
1897
|
+
return this;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* @public
|
|
1902
|
+
* @expose
|
|
1903
|
+
* @returns {boolean} are the columns movable?
|
|
1904
|
+
*/
|
|
1905
|
+
getMovableColumns() {
|
|
1906
|
+
return this._o.movableColumns;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
/**
|
|
1910
|
+
* @public
|
|
1911
|
+
* @expose
|
|
1912
|
+
* @param {boolean} resizableColumns=true are the columns resizable?
|
|
1913
|
+
* @returns {DGTable} self
|
|
1914
|
+
*/
|
|
1915
|
+
setResizableColumns(resizableColumns) {
|
|
1916
|
+
let o = this._o;
|
|
1917
|
+
//noinspection PointlessBooleanExpressionJS
|
|
1918
|
+
resizableColumns = resizableColumns === undefined ? true : !!resizableColumns;
|
|
1919
|
+
if (o.resizableColumns !== resizableColumns) {
|
|
1920
|
+
o.resizableColumns = resizableColumns;
|
|
1921
|
+
}
|
|
1922
|
+
return this;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
* @public
|
|
1927
|
+
* @expose
|
|
1928
|
+
* @returns {boolean} are the columns resizable?
|
|
1929
|
+
*/
|
|
1930
|
+
getResizableColumns() {
|
|
1931
|
+
return this._o.resizableColumns;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* Sets a functions that supplies comparators dynamically
|
|
1936
|
+
* @public
|
|
1937
|
+
* @expose
|
|
1938
|
+
* @param {{function(columnName: string, descending: boolean, defaultComparator: {function(a:any,b:any):number}):{function(a:any,b:any):number}}|null|undefined} comparatorCallback a function that returns the comparator for a specific column
|
|
1939
|
+
* @returns {DGTable} self
|
|
1940
|
+
*/
|
|
1941
|
+
setOnComparatorRequired(comparatorCallback) {
|
|
1942
|
+
let o = this._o;
|
|
1943
|
+
if (o.onComparatorRequired !== comparatorCallback) {
|
|
1944
|
+
o.onComparatorRequired = comparatorCallback;
|
|
1945
|
+
}
|
|
1946
|
+
return this;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
// Backwards compatibility
|
|
1950
|
+
setComparatorCallback(comparatorCallback) {
|
|
1951
|
+
return this.setOnComparatorRequired(comparatorCallback);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* sets custom sorting function for a data set
|
|
1956
|
+
* @public
|
|
1957
|
+
* @expose
|
|
1958
|
+
* @param {{function(data: any[], sort: function(any[]):any[]):any[]}|null|undefined} customSortingProvider provides a custom sorting function (not the comparator, but a sort() alternative) for a data set
|
|
1959
|
+
* @returns {DGTable} self
|
|
1960
|
+
*/
|
|
1961
|
+
setCustomSortingProvider(customSortingProvider) {
|
|
1962
|
+
let o = this._o;
|
|
1963
|
+
if (o.customSortingProvider !== customSortingProvider) {
|
|
1964
|
+
o.customSortingProvider = customSortingProvider;
|
|
1965
|
+
}
|
|
1966
|
+
return this;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* Set a new width to a column
|
|
1971
|
+
* @public
|
|
1972
|
+
* @expose
|
|
1973
|
+
* @param {string} column name of the column to resize
|
|
1974
|
+
* @param {number|string} width new column as pixels, or relative size (0.5, 50%)
|
|
1975
|
+
* @returns {DGTable} self
|
|
1976
|
+
*/
|
|
1977
|
+
setColumnWidth(column, width) {
|
|
1978
|
+
|
|
1979
|
+
const p = this._p;
|
|
1980
|
+
|
|
1981
|
+
let col = p.columns.get(column);
|
|
1982
|
+
|
|
1983
|
+
let parsedWidth = this._parseColumnWidth(width, col.ignoreMin ? 0 : this._o.minColumnWidth);
|
|
1984
|
+
|
|
1985
|
+
if (col) {
|
|
1986
|
+
let oldWidth = this._serializeColumnWidth(col);
|
|
1987
|
+
|
|
1988
|
+
col.width = parsedWidth.width;
|
|
1989
|
+
col.widthMode = parsedWidth.mode;
|
|
1990
|
+
|
|
1991
|
+
let newWidth = this._serializeColumnWidth(col);
|
|
1992
|
+
|
|
1993
|
+
if (oldWidth !== newWidth) {
|
|
1994
|
+
this.tableWidthChanged(true); // Calculate actual sizes
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
this.emit('columnwidth', { name: col.name, width: newWidth, oldWidth: oldWidth });
|
|
1998
|
+
}
|
|
1999
|
+
return this;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* @public
|
|
2004
|
+
* @expose
|
|
2005
|
+
* @param {string} column name of the column
|
|
2006
|
+
* @returns {string|null} the serialized width of the specified column, or null if column not found
|
|
2007
|
+
*/
|
|
2008
|
+
getColumnWidth(column) {
|
|
2009
|
+
const p = this._p;
|
|
2010
|
+
|
|
2011
|
+
let col = p.columns.get(column);
|
|
2012
|
+
if (col) {
|
|
2013
|
+
return this._serializeColumnWidth(col);
|
|
2014
|
+
}
|
|
2015
|
+
return null;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
/**
|
|
2019
|
+
* @public
|
|
2020
|
+
* @expose
|
|
2021
|
+
* @param {string} column name of the column
|
|
2022
|
+
* @returns {SERIALIZED_COLUMN|null} configuration for all columns
|
|
2023
|
+
*/
|
|
2024
|
+
getColumnConfig(column) {
|
|
2025
|
+
const p = this._p;
|
|
2026
|
+
let col = p.columns.get(column);
|
|
2027
|
+
if (col) {
|
|
2028
|
+
return {
|
|
2029
|
+
'order': col.order,
|
|
2030
|
+
'width': this._serializeColumnWidth(col),
|
|
2031
|
+
'visible': col.visible,
|
|
2032
|
+
'label': col.label
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
return null;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
/**
|
|
2039
|
+
* Returns a config object for the columns, to allow saving configurations for next time...
|
|
2040
|
+
* @public
|
|
2041
|
+
* @expose
|
|
2042
|
+
* @returns {Object} configuration for all columns
|
|
2043
|
+
*/
|
|
2044
|
+
getColumnsConfig() {
|
|
2045
|
+
const p = this._p;
|
|
2046
|
+
|
|
2047
|
+
let config = {};
|
|
2048
|
+
for (let i = 0; i < p.columns.length; i++) {
|
|
2049
|
+
config[p.columns[i].name] = this.getColumnConfig(p.columns[i].name);
|
|
2050
|
+
}
|
|
2051
|
+
return config;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* Returns an array of the currently sorted columns
|
|
2056
|
+
* @public
|
|
2057
|
+
* @expose
|
|
2058
|
+
* @returns {Array.<SERIALIZED_COLUMN_SORT>} configuration for all columns
|
|
2059
|
+
*/
|
|
2060
|
+
getSortedColumns() {
|
|
2061
|
+
const p = this._p;
|
|
2062
|
+
|
|
2063
|
+
let sorted = [];
|
|
2064
|
+
for (let i = 0; i < p.rows.sortColumn.length; i++) {
|
|
2065
|
+
let sort = p.rows.sortColumn[i];
|
|
2066
|
+
sorted.push({ column: sort.column, descending: sort.descending });
|
|
2067
|
+
}
|
|
2068
|
+
return sorted;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
/**
|
|
2072
|
+
* Returns the HTML string for a specific cell. Can be used externally for special cases (i.e. when setting a fresh HTML in the cell preview through the callback).
|
|
2073
|
+
* @public
|
|
2074
|
+
* @expose
|
|
2075
|
+
* @param {number} rowIndex - index of the row
|
|
2076
|
+
* @param {string} columnName - name of the column
|
|
2077
|
+
* @returns {string|null} HTML string for the specified cell
|
|
2078
|
+
*/
|
|
2079
|
+
getHtmlForRowCell(rowIndex, columnName) {
|
|
2080
|
+
const p = this._p;
|
|
2081
|
+
|
|
2082
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1) return null;
|
|
2083
|
+
let column = p.columns.get(columnName);
|
|
2084
|
+
if (!column) return null;
|
|
2085
|
+
let rowData = p.rows[rowIndex];
|
|
2086
|
+
|
|
2087
|
+
return this._getHtmlForCell(rowData, column);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
/**
|
|
2091
|
+
* Returns the HTML string for a specific cell. Can be used externally for special cases (i.e. when setting a fresh HTML in the cell preview through the callback).
|
|
2092
|
+
* @public
|
|
2093
|
+
* @expose
|
|
2094
|
+
* @param {Object} rowData - row data
|
|
2095
|
+
* @param {Object} columnName - column data
|
|
2096
|
+
* @returns {string|null} HTML string for the specified cell
|
|
2097
|
+
*/
|
|
2098
|
+
getHtmlForRowDataCell(rowData, columnName) {
|
|
2099
|
+
const p = this._p;
|
|
2100
|
+
|
|
2101
|
+
let column = p.columns.get(columnName);
|
|
2102
|
+
if (!column) return null;
|
|
2103
|
+
|
|
2104
|
+
return this._getHtmlForCell(rowData, column);
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
/**
|
|
2108
|
+
* Returns the HTML string for a specific cell. Can be used externally for special cases (i.e. when setting a fresh HTML in the cell preview through the callback).
|
|
2109
|
+
* @private
|
|
2110
|
+
* @expose
|
|
2111
|
+
* @param {Object} rowData - row data
|
|
2112
|
+
* @param {Object} column - column data
|
|
2113
|
+
* @returns {string} HTML string for the specified cell
|
|
2114
|
+
*/
|
|
2115
|
+
_getHtmlForCell(rowData, column) {
|
|
2116
|
+
let dataPath = column.dataPath;
|
|
2117
|
+
let colValue = rowData[dataPath[0]];
|
|
2118
|
+
for (let dataPathIndex = 1; dataPathIndex < dataPath.length; dataPathIndex++) {
|
|
2119
|
+
if (colValue == null) break;
|
|
2120
|
+
colValue = colValue && colValue[dataPath[dataPathIndex]];
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
const formatter = this._o.cellFormatter;
|
|
2124
|
+
let content;
|
|
2125
|
+
|
|
2126
|
+
if (formatter[IsSafeSymbol]) {
|
|
2127
|
+
content = formatter(colValue, column.name, rowData);
|
|
2128
|
+
} else {
|
|
2129
|
+
try {
|
|
2130
|
+
content = formatter(colValue, column.name, rowData);
|
|
2131
|
+
} catch (err) {
|
|
2132
|
+
content = '[ERROR]';
|
|
2133
|
+
// eslint-disable-next-line no-console
|
|
2134
|
+
console.error('Failed to generate content for cell ' + column.name, err);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
if (content === undefined || content === null) {
|
|
2139
|
+
content = '';
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
return content;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
/**
|
|
2146
|
+
* Returns the y pos of a row by index
|
|
2147
|
+
* @public
|
|
2148
|
+
* @expose
|
|
2149
|
+
* @param {number} rowIndex - index of the row
|
|
2150
|
+
* @returns {number|null} Y pos
|
|
2151
|
+
*/
|
|
2152
|
+
getRowYPos(rowIndex) {
|
|
2153
|
+
const p = this._p;
|
|
2154
|
+
|
|
2155
|
+
return p.virtualListHelper.getItemPosition(rowIndex) || null;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
/**
|
|
2159
|
+
* Returns the row data for a specific row
|
|
2160
|
+
* @public
|
|
2161
|
+
* @expose
|
|
2162
|
+
* @param {number} row index of the row
|
|
2163
|
+
* @returns {Object} Row data
|
|
2164
|
+
*/
|
|
2165
|
+
getDataForRow(row) {
|
|
2166
|
+
const p = this._p;
|
|
2167
|
+
|
|
2168
|
+
if (row < 0 || row > p.rows.length - 1) return null;
|
|
2169
|
+
return p.rows[row];
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
/**
|
|
2173
|
+
* Gets the number of rows
|
|
2174
|
+
* @public
|
|
2175
|
+
* @expose
|
|
2176
|
+
* @returns {number} Row count
|
|
2177
|
+
*/
|
|
2178
|
+
getRowCount() {
|
|
2179
|
+
const p = this._p;
|
|
2180
|
+
return p.rows ? p.rows.length : 0;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
/**
|
|
2184
|
+
* Returns the actual row index for specific row (out of the full data set, not filtered)
|
|
2185
|
+
* @public
|
|
2186
|
+
* @expose
|
|
2187
|
+
* @param {Object} rowData - Row data to find
|
|
2188
|
+
* @returns {number} Row index
|
|
2189
|
+
*/
|
|
2190
|
+
getIndexForRow(rowData) {
|
|
2191
|
+
const p = this._p;
|
|
2192
|
+
return p.rows.indexOf(rowData);
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
/**
|
|
2196
|
+
* Gets the number of filtered rows
|
|
2197
|
+
* @public
|
|
2198
|
+
* @expose
|
|
2199
|
+
* @returns {number} Filtered row count
|
|
2200
|
+
*/
|
|
2201
|
+
getFilteredRowCount() {
|
|
2202
|
+
const p = this._p;
|
|
2203
|
+
return (p.filteredRows || p.rows).length;
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
/**
|
|
2207
|
+
* Returns the filtered row index for specific row
|
|
2208
|
+
* @public
|
|
2209
|
+
* @expose
|
|
2210
|
+
* @param {Object} rowData - Row data to find
|
|
2211
|
+
* @returns {number} Row index
|
|
2212
|
+
*/
|
|
2213
|
+
getIndexForFilteredRow(rowData) {
|
|
2214
|
+
const p = this._p;
|
|
2215
|
+
return (p.filteredRows || p.rows).indexOf(rowData);
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
/**
|
|
2219
|
+
* Returns the row data for a specific row
|
|
2220
|
+
* @public
|
|
2221
|
+
* @expose
|
|
2222
|
+
* @param {number} row index of the filtered row
|
|
2223
|
+
* @returns {Object} Row data
|
|
2224
|
+
*/
|
|
2225
|
+
getDataForFilteredRow(row) {
|
|
2226
|
+
const p = this._p;
|
|
2227
|
+
if (row < 0 || row > (p.filteredRows || p.rows).length - 1) return null;
|
|
2228
|
+
return (p.filteredRows || p.rows)[row];
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* Returns DOM element of the header row
|
|
2233
|
+
* @public
|
|
2234
|
+
* @expose
|
|
2235
|
+
* @returns {Element} Row element
|
|
2236
|
+
*/
|
|
2237
|
+
getHeaderRowElement() {
|
|
2238
|
+
return this._p.headerRow;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
/**
|
|
2242
|
+
* @private
|
|
2243
|
+
* @param {Element} el
|
|
2244
|
+
* @returns {number} width
|
|
2245
|
+
*/
|
|
2246
|
+
_horizontalPadding(el) {
|
|
2247
|
+
const style = getComputedStyle(el);
|
|
2248
|
+
return (parseFloat(style.paddingLeft) || 0) + (
|
|
2249
|
+
parseFloat(style.paddingRight) || 0);
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
/**
|
|
2253
|
+
* @private
|
|
2254
|
+
* @param {Element} el
|
|
2255
|
+
* @returns {number} width
|
|
2256
|
+
*/
|
|
2257
|
+
_horizontalBorderWidth(el) {
|
|
2258
|
+
const style = getComputedStyle(el);
|
|
2259
|
+
return (parseFloat(style.borderLeftWidth) || 0) + (
|
|
2260
|
+
parseFloat(style.borderRightWidth) || 0);
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
/**
|
|
2264
|
+
* @private
|
|
2265
|
+
* @returns {number} width
|
|
2266
|
+
*/
|
|
2267
|
+
_calculateWidthAvailableForColumns() {
|
|
2268
|
+
const o = this._o,p = this._p;
|
|
2269
|
+
|
|
2270
|
+
// Changing display mode briefly, to prevent taking in account the parent's scrollbar width when we are the cause for it
|
|
2271
|
+
let oldDisplay, lastScrollTop, lastScrollLeft;
|
|
2272
|
+
if (p.table) {
|
|
2273
|
+
lastScrollTop = p.table ? p.table.scrollTop : 0;
|
|
2274
|
+
lastScrollLeft = p.table ? p.table.scrollLeft : 0;
|
|
2275
|
+
|
|
2276
|
+
if (o.virtualTable) {
|
|
2277
|
+
oldDisplay = p.table.style.display;
|
|
2278
|
+
p.table.style.display = 'none';
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
let detectedWidth = Css_js.getElementWidth(this.el);
|
|
2283
|
+
|
|
2284
|
+
if (p.table) {
|
|
2285
|
+
if (o.virtualTable) {
|
|
2286
|
+
p.table.style.display = oldDisplay;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
p.table.scrollTop = lastScrollTop;
|
|
2290
|
+
p.table.scrollLeft = lastScrollLeft;
|
|
2291
|
+
p.header.scrollLeft = lastScrollLeft;
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
let tableClassName = o.tableClassName;
|
|
2295
|
+
|
|
2296
|
+
const thisWrapper = createElement('div');
|
|
2297
|
+
thisWrapper.className = this.el.className;
|
|
2298
|
+
Css_js.setCssProps(thisWrapper, {
|
|
2299
|
+
'z-index': -1,
|
|
2300
|
+
'position': 'absolute',
|
|
2301
|
+
left: '0',
|
|
2302
|
+
top: '-9999px'
|
|
2303
|
+
});
|
|
2304
|
+
let header = createElement('div');
|
|
2305
|
+
header.className = `${tableClassName}-header`;
|
|
2306
|
+
thisWrapper.appendChild(header);
|
|
2307
|
+
let headerRow = createElement('div');
|
|
2308
|
+
headerRow.className = `${tableClassName}-header-row`;
|
|
2309
|
+
header.appendChild(headerRow);
|
|
2310
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2311
|
+
const column = p.visibleColumns[i];
|
|
2312
|
+
const cell = createElement('div');
|
|
2313
|
+
cell.className = `${tableClassName}-header-cell ${column.cellClasses || ''}`;
|
|
2314
|
+
cell['columnName'] = column.name;
|
|
2315
|
+
cell.appendChild(createElement('div'));
|
|
2316
|
+
headerRow.appendChild(cell);
|
|
2317
|
+
}
|
|
2318
|
+
document.body.appendChild(thisWrapper);
|
|
2319
|
+
|
|
2320
|
+
detectedWidth -= this._horizontalBorderWidth(headerRow);
|
|
2321
|
+
|
|
2322
|
+
let cells = DomCompat_js.scopedSelectorAll(headerRow, `>div.${tableClassName}-header-cell`);
|
|
2323
|
+
for (const cell of cells) {
|
|
2324
|
+
const cellStyle = getComputedStyle(cell);
|
|
2325
|
+
let isBoxing = cellStyle.boxSizing === 'border-box';
|
|
2326
|
+
if (!isBoxing) {
|
|
2327
|
+
detectedWidth -=
|
|
2328
|
+
(parseFloat(cellStyle.borderRightWidth) || 0) + (
|
|
2329
|
+
parseFloat(cellStyle.borderLeftWidth) || 0) +
|
|
2330
|
+
this._horizontalPadding(cell); // CELL's padding
|
|
2331
|
+
|
|
2332
|
+
const colName = cell['columnName'];
|
|
2333
|
+
const column = p.columns.get(colName);
|
|
2334
|
+
if (column)
|
|
2335
|
+
detectedWidth -= column.arrowProposedWidth || 0;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
thisWrapper.remove();
|
|
2340
|
+
|
|
2341
|
+
return Math.max(0, detectedWidth);
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
_getTextWidth(text) {
|
|
2345
|
+
let tableClassName = this._o.tableClassName;
|
|
2346
|
+
|
|
2347
|
+
const tableWrapper = createElement('div');
|
|
2348
|
+
tableWrapper.className = this.el.className;
|
|
2349
|
+
const header = createElement('div');
|
|
2350
|
+
header.className = tableClassName + '-header';
|
|
2351
|
+
const headerRow = createElement('div');
|
|
2352
|
+
headerRow.className = tableClassName + '-header-row';
|
|
2353
|
+
const cell = createElement('div');
|
|
2354
|
+
cell.className = tableClassName + '-header-cell';
|
|
2355
|
+
const cellContent = createElement('div');
|
|
2356
|
+
cellContent.textContent = text;
|
|
2357
|
+
|
|
2358
|
+
cell.appendChild(cellContent);
|
|
2359
|
+
headerRow.appendChild(cell);
|
|
2360
|
+
header.appendChild(headerRow);
|
|
2361
|
+
tableWrapper.appendChild(header);
|
|
2362
|
+
Css_js.setCssProps(tableWrapper, {
|
|
2363
|
+
position: 'absolute',
|
|
2364
|
+
top: '-9999px',
|
|
2365
|
+
visibility: 'hidden'
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
document.body.appendChild(tableWrapper);
|
|
2369
|
+
|
|
2370
|
+
let width = Css_js.getElementWidth(cell);
|
|
2371
|
+
|
|
2372
|
+
tableWrapper.remove();
|
|
2373
|
+
|
|
2374
|
+
return width;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
/**
|
|
2378
|
+
* Notify the table that its width has changed
|
|
2379
|
+
* @public
|
|
2380
|
+
* @expose
|
|
2381
|
+
* @param {boolean} [forceUpdate=false]
|
|
2382
|
+
* @param {boolean} [renderColumns=true]
|
|
2383
|
+
* @returns {DGTable} self
|
|
2384
|
+
*/
|
|
2385
|
+
tableWidthChanged(forceUpdate, renderColumns) {
|
|
2386
|
+
|
|
2387
|
+
let o = this._o,
|
|
2388
|
+
p = this._p,
|
|
2389
|
+
detectedWidth = this._calculateWidthAvailableForColumns(),
|
|
2390
|
+
sizeLeft = detectedWidth,
|
|
2391
|
+
relatives = 0;
|
|
2392
|
+
|
|
2393
|
+
if (!p.table) return this;
|
|
2394
|
+
|
|
2395
|
+
renderColumns = renderColumns === undefined || renderColumns;
|
|
2396
|
+
|
|
2397
|
+
let tableWidthBeforeCalculations = 0;
|
|
2398
|
+
|
|
2399
|
+
if (!p.tbody) {
|
|
2400
|
+
renderColumns = false;
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
if (renderColumns) {
|
|
2404
|
+
tableWidthBeforeCalculations = parseFloat(p.tbody.style.minWidth) || 0;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
if (sizeLeft !== p.lastDetectedWidth || forceUpdate) {
|
|
2408
|
+
p.lastDetectedWidth = detectedWidth;
|
|
2409
|
+
|
|
2410
|
+
let absWidthTotal = 0,changedColumnIndexes = [],totalRelativePercentage = 0;
|
|
2411
|
+
|
|
2412
|
+
for (let i = 0; i < p.columns.length; i++) {
|
|
2413
|
+
p.columns[i].actualWidthConsideringScrollbarWidth = null;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2417
|
+
let col = p.visibleColumns[i];
|
|
2418
|
+
if (col.widthMode === ColumnWidthMode.ABSOLUTE) {
|
|
2419
|
+
let width = col.width;
|
|
2420
|
+
width += col.arrowProposedWidth || 0; // Sort-arrow width
|
|
2421
|
+
if (!col.ignoreMin && width < o.minColumnWidth) {
|
|
2422
|
+
width = o.minColumnWidth;
|
|
2423
|
+
}
|
|
2424
|
+
sizeLeft -= width;
|
|
2425
|
+
absWidthTotal += width;
|
|
2426
|
+
|
|
2427
|
+
// Update actualWidth
|
|
2428
|
+
if (width !== col.actualWidth) {
|
|
2429
|
+
col.actualWidth = width;
|
|
2430
|
+
changedColumnIndexes.push(i);
|
|
2431
|
+
}
|
|
2432
|
+
} else if (col.widthMode === ColumnWidthMode.AUTO) {
|
|
2433
|
+
let width = this._getTextWidth(col.label) + 20;
|
|
2434
|
+
width += col.arrowProposedWidth || 0; // Sort-arrow width
|
|
2435
|
+
if (!col.ignoreMin && width < o.minColumnWidth) {
|
|
2436
|
+
width = o.minColumnWidth;
|
|
2437
|
+
}
|
|
2438
|
+
sizeLeft -= width;
|
|
2439
|
+
absWidthTotal += width;
|
|
2440
|
+
|
|
2441
|
+
// Update actualWidth
|
|
2442
|
+
if (width !== col.actualWidth) {
|
|
2443
|
+
col.actualWidth = width;
|
|
2444
|
+
if (!o.convertColumnWidthsToRelative) {
|
|
2445
|
+
changedColumnIndexes.push(i);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
} else if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
2449
|
+
totalRelativePercentage += col.width;
|
|
2450
|
+
relatives++;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// Normalize relative sizes if needed
|
|
2455
|
+
if (o.convertColumnWidthsToRelative) {
|
|
2456
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2457
|
+
let col = p.visibleColumns[i];
|
|
2458
|
+
if (col.widthMode === ColumnWidthMode.AUTO) {
|
|
2459
|
+
col.widthMode = ColumnWidthMode.RELATIVE;
|
|
2460
|
+
sizeLeft += col.actualWidth;
|
|
2461
|
+
col.width = col.actualWidth / absWidthTotal;
|
|
2462
|
+
totalRelativePercentage += col.width;
|
|
2463
|
+
relatives++;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// Normalize relative sizes if needed
|
|
2469
|
+
if (relatives && (totalRelativePercentage < 1 && o.relativeWidthGrowsToFillWidth ||
|
|
2470
|
+
totalRelativePercentage > 1 && o.relativeWidthShrinksToFillWidth)) {
|
|
2471
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2472
|
+
let col = p.visibleColumns[i];
|
|
2473
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
2474
|
+
col.width /= totalRelativePercentage;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
let sizeLeftForRelative = Math.max(0, sizeLeft); // Use this as the space to take the relative widths out of
|
|
2480
|
+
if (sizeLeftForRelative === 0) {
|
|
2481
|
+
sizeLeftForRelative = p.table.clientWidth;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
let minColumnWidthRelative = o.minColumnWidth / sizeLeftForRelative;
|
|
2485
|
+
if (isNaN(minColumnWidthRelative)) {
|
|
2486
|
+
minColumnWidthRelative = 0;
|
|
2487
|
+
}
|
|
2488
|
+
if (minColumnWidthRelative > 0) {
|
|
2489
|
+
let extraRelative = 0,delta;
|
|
2490
|
+
|
|
2491
|
+
// First pass - make sure they are all constrained to the minimum width
|
|
2492
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2493
|
+
let col = p.visibleColumns[i];
|
|
2494
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
2495
|
+
if (!col.ignoreMin && col.width < minColumnWidthRelative) {
|
|
2496
|
+
extraRelative += minColumnWidthRelative - col.width;
|
|
2497
|
+
col.width = minColumnWidthRelative;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// Second pass - try to take the extra width out of the other columns to compensate
|
|
2503
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2504
|
+
let col = p.visibleColumns[i];
|
|
2505
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
2506
|
+
if (!col.ignoreMin && col.width > minColumnWidthRelative) {
|
|
2507
|
+
if (extraRelative > 0) {
|
|
2508
|
+
delta = Math.min(extraRelative, col.width - minColumnWidthRelative);
|
|
2509
|
+
col.width -= delta;
|
|
2510
|
+
extraRelative -= delta;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// Try to fill width
|
|
2518
|
+
if (o.autoFillTableWidth && sizeLeft > 0) {
|
|
2519
|
+
let nonResizableTotal = 0;
|
|
2520
|
+
let sizeLeftToFill = sizeLeft;
|
|
2521
|
+
|
|
2522
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2523
|
+
let col = p.visibleColumns[i];
|
|
2524
|
+
if (!col.resizable && col.widthMode === ColumnWidthMode.ABSOLUTE)
|
|
2525
|
+
nonResizableTotal += col.width;
|
|
2526
|
+
|
|
2527
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE)
|
|
2528
|
+
sizeLeftToFill -= Math.round(sizeLeftForRelative * col.width);
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
let conv = (detectedWidth - nonResizableTotal) / (detectedWidth - sizeLeftToFill - nonResizableTotal) || NaN;
|
|
2532
|
+
for (let i = 0; i < p.visibleColumns.length && sizeLeftToFill > 0; i++) {
|
|
2533
|
+
let col = p.visibleColumns[i];
|
|
2534
|
+
if (!col.resizable && col.widthMode === ColumnWidthMode.ABSOLUTE)
|
|
2535
|
+
continue;
|
|
2536
|
+
|
|
2537
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
2538
|
+
col.width *= conv;
|
|
2539
|
+
} else {
|
|
2540
|
+
let width = col.actualWidth * conv;
|
|
2541
|
+
if (col.actualWidth !== width) {
|
|
2542
|
+
col.actualWidth = width;
|
|
2543
|
+
if (changedColumnIndexes.indexOf(i) === -1)
|
|
2544
|
+
changedColumnIndexes.push(i);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// Materialize relative sizes
|
|
2551
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
2552
|
+
let col = p.visibleColumns[i];
|
|
2553
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
2554
|
+
let width = Math.round(sizeLeftForRelative * col.width);
|
|
2555
|
+
sizeLeft -= width;
|
|
2556
|
+
relatives--;
|
|
2557
|
+
|
|
2558
|
+
// Take care of rounding errors
|
|
2559
|
+
if (relatives === 0 && sizeLeft === 1) {// Take care of rounding errors
|
|
2560
|
+
width++;
|
|
2561
|
+
sizeLeft--;
|
|
2562
|
+
}
|
|
2563
|
+
if (sizeLeft === -1) {
|
|
2564
|
+
width--;
|
|
2565
|
+
sizeLeft++;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// Update actualWidth
|
|
2569
|
+
if (width !== col.actualWidth) {
|
|
2570
|
+
col.actualWidth = width;
|
|
2571
|
+
changedColumnIndexes.push(i);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
if (p.visibleColumns.length) {
|
|
2577
|
+
// (There should always be at least 1 column visible, but just in case)
|
|
2578
|
+
p.visibleColumns[p.visibleColumns.length - 1].actualWidthConsideringScrollbarWidth =
|
|
2579
|
+
p.visibleColumns[p.visibleColumns.length - 1].actualWidth - (p.scrollbarWidth || 0);
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
p.notifyRendererOfColumnsConfig?.();
|
|
2583
|
+
|
|
2584
|
+
if (renderColumns) {
|
|
2585
|
+
let tableWidth = this._calculateTbodyWidth();
|
|
2586
|
+
|
|
2587
|
+
if (tableWidthBeforeCalculations < tableWidth) {
|
|
2588
|
+
this._updateTableWidth(false);
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
for (let i = 0; i < changedColumnIndexes.length; i++) {
|
|
2592
|
+
this._resizeColumnElements(changedColumnIndexes[i]);
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
if (tableWidthBeforeCalculations > tableWidth) {
|
|
2596
|
+
this._updateTableWidth(false);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
return this;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
/**
|
|
2605
|
+
* Notify the table that its height has changed
|
|
2606
|
+
* @public
|
|
2607
|
+
* @expose
|
|
2608
|
+
* @returns {DGTable} self
|
|
2609
|
+
*/
|
|
2610
|
+
tableHeightChanged() {
|
|
2611
|
+
let o = this._o,
|
|
2612
|
+
p = this._p;
|
|
2613
|
+
|
|
2614
|
+
if (!p.table) {
|
|
2615
|
+
return this;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
const tableStyle = getComputedStyle(p.table);
|
|
2619
|
+
|
|
2620
|
+
let height = Css_js.getElementHeight(this.el, true) - (
|
|
2621
|
+
parseFloat(tableStyle.borderTopWidth) || 0) // Subtract top border of inner element
|
|
2622
|
+
- (parseFloat(tableStyle.borderBottomWidth) || 0); // Subtract bottom border of inner element
|
|
2623
|
+
|
|
2624
|
+
if (height !== o.height) {
|
|
2625
|
+
|
|
2626
|
+
o.height = height;
|
|
2627
|
+
|
|
2628
|
+
if (p.tbody) {
|
|
2629
|
+
// At least 1 pixel - to show scrollbars correctly.
|
|
2630
|
+
p.tbody.style.height = Math.max(o.height - Css_js.getElementHeight(p.header, true, true, true), 1) + 'px';
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
if (o.virtualTable) {
|
|
2634
|
+
this.clearAndRender();
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
return this;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
/**
|
|
2642
|
+
* Add rows to the table
|
|
2643
|
+
* @public
|
|
2644
|
+
* @expose
|
|
2645
|
+
* @param {Object[]} data - array of rows to add to the table
|
|
2646
|
+
* @param {number} [at=-1] - where to add the rows at
|
|
2647
|
+
* @param {boolean} [resort=false] - should resort all rows?
|
|
2648
|
+
* @param {boolean} [render=true]
|
|
2649
|
+
* @returns {DGTable} self
|
|
2650
|
+
*/
|
|
2651
|
+
addRows(data, at, resort, render) {
|
|
2652
|
+
let p = this._p;
|
|
2653
|
+
|
|
2654
|
+
if (typeof at === 'boolean') {
|
|
2655
|
+
render = resort;
|
|
2656
|
+
resort = at;
|
|
2657
|
+
at = -1;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
if (typeof at !== 'number')
|
|
2661
|
+
at = -1;
|
|
2662
|
+
|
|
2663
|
+
if (at < 0 || at > p.rows.length)
|
|
2664
|
+
at = p.rows.length;
|
|
2665
|
+
|
|
2666
|
+
render = render === undefined ? true : !!render;
|
|
2667
|
+
|
|
2668
|
+
if (data) {
|
|
2669
|
+
p.rows.add(data, at);
|
|
2670
|
+
|
|
2671
|
+
if (p.filteredRows || resort && p.rows.sortColumn.length) {
|
|
2672
|
+
|
|
2673
|
+
if (resort && p.rows.sortColumn.length) {
|
|
2674
|
+
this.resort();
|
|
2675
|
+
} else {
|
|
2676
|
+
this._refilter();
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
p.tableSkeletonNeedsRendering = true;
|
|
2680
|
+
|
|
2681
|
+
if (render) {
|
|
2682
|
+
// Render the skeleton with all rows from scratch
|
|
2683
|
+
this.render();
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
} else if (render) {
|
|
2687
|
+
p.virtualListHelper.addItemsAt(data.length, at);
|
|
2688
|
+
|
|
2689
|
+
if (this._o.virtualTable) {
|
|
2690
|
+
this._updateVirtualHeight().
|
|
2691
|
+
_updateLastCellWidthFromScrollbar() // Detect vertical scrollbar height
|
|
2692
|
+
.render().
|
|
2693
|
+
_updateTableWidth(false); // Update table width to suit the required width considering vertical scrollbar
|
|
2694
|
+
|
|
2695
|
+
} else if (p.tbody) {
|
|
2696
|
+
this.render().
|
|
2697
|
+
_updateLastCellWidthFromScrollbar() // Detect vertical scrollbar height, and update existing last cells
|
|
2698
|
+
._updateTableWidth(true); // Update table width to suit the required width considering vertical scrollbar
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
this.emit('addrows', { count: data.length, clear: false });
|
|
2703
|
+
}
|
|
2704
|
+
return this;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
/**
|
|
2708
|
+
* Removes a row from the table
|
|
2709
|
+
* @public
|
|
2710
|
+
* @expose
|
|
2711
|
+
* @param {number} rowIndex - index
|
|
2712
|
+
* @param {number} count - how many rows to remove
|
|
2713
|
+
* @param {boolean=true} render
|
|
2714
|
+
* @returns {DGTable} self
|
|
2715
|
+
*/
|
|
2716
|
+
removeRows(rowIndex, count, render) {
|
|
2717
|
+
let p = this._p;
|
|
2718
|
+
|
|
2719
|
+
if (typeof count !== 'number' || count <= 0) return this;
|
|
2720
|
+
|
|
2721
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1) return this;
|
|
2722
|
+
|
|
2723
|
+
p.rows.splice(rowIndex, count);
|
|
2724
|
+
render = render === undefined ? true : !!render;
|
|
2725
|
+
|
|
2726
|
+
if (p.filteredRows) {
|
|
2727
|
+
this._refilter();
|
|
2728
|
+
|
|
2729
|
+
p.tableSkeletonNeedsRendering = true;
|
|
2730
|
+
|
|
2731
|
+
if (render) {
|
|
2732
|
+
// Render the skeleton with all rows from scratch
|
|
2733
|
+
this.render();
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
} else if (render) {
|
|
2737
|
+
p.virtualListHelper.removeItemsAt(count, rowIndex);
|
|
2738
|
+
|
|
2739
|
+
if (this._o.virtualTable) {
|
|
2740
|
+
this._updateVirtualHeight().
|
|
2741
|
+
_updateLastCellWidthFromScrollbar().
|
|
2742
|
+
render().
|
|
2743
|
+
_updateTableWidth(false); // Update table width to suit the required width considering vertical scrollbar
|
|
2744
|
+
} else {
|
|
2745
|
+
this.render().
|
|
2746
|
+
_updateLastCellWidthFromScrollbar().
|
|
2747
|
+
_updateTableWidth(true); // Update table width to suit the required width considering vertical scrollbar
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
return this;
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Removes a row from the table
|
|
2756
|
+
* @public
|
|
2757
|
+
* @expose
|
|
2758
|
+
* @param {number} rowIndex - index
|
|
2759
|
+
* @param {boolean=true} render
|
|
2760
|
+
* @returns {DGTable} self
|
|
2761
|
+
*/
|
|
2762
|
+
removeRow(rowIndex, render) {
|
|
2763
|
+
return this.removeRows(rowIndex, 1, render);
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
/**
|
|
2767
|
+
* Refreshes the row specified
|
|
2768
|
+
* @public
|
|
2769
|
+
* @expose
|
|
2770
|
+
* @param {number} rowIndex index
|
|
2771
|
+
* @param {boolean} render should render the changes immediately?
|
|
2772
|
+
* @returns {DGTable} self
|
|
2773
|
+
*/
|
|
2774
|
+
refreshRow(rowIndex) {let render = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
2775
|
+
let p = this._p;
|
|
2776
|
+
|
|
2777
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1)
|
|
2778
|
+
return this;
|
|
2779
|
+
|
|
2780
|
+
// Find out if the row is in the rendered dataset
|
|
2781
|
+
let filteredRowIndex = -1;
|
|
2782
|
+
if (p.filteredRows && (filteredRowIndex = p.filteredRows.indexOf(p.rows[rowIndex])) === -1)
|
|
2783
|
+
return this;
|
|
2784
|
+
|
|
2785
|
+
if (filteredRowIndex === -1) {
|
|
2786
|
+
filteredRowIndex = rowIndex;
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
p.virtualListHelper.refreshItemAt(filteredRowIndex);
|
|
2790
|
+
|
|
2791
|
+
if (render)
|
|
2792
|
+
p.virtualListHelper.render();
|
|
2793
|
+
|
|
2794
|
+
return this;
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
/**
|
|
2798
|
+
* Get the DOM element for the specified row, if it exists
|
|
2799
|
+
* @public
|
|
2800
|
+
* @expose
|
|
2801
|
+
* @param {number} rowIndex index
|
|
2802
|
+
* @returns {Element|null} row or null
|
|
2803
|
+
*/
|
|
2804
|
+
getRowElement(rowIndex) {
|
|
2805
|
+
let p = this._p;
|
|
2806
|
+
|
|
2807
|
+
if (rowIndex < 0 || rowIndex > p.rows.length - 1)
|
|
2808
|
+
return null;
|
|
2809
|
+
|
|
2810
|
+
// Find out if the row is in the rendered dataset
|
|
2811
|
+
let filteredRowIndex = -1;
|
|
2812
|
+
if (p.filteredRows && (filteredRowIndex = p.filteredRows.indexOf(p.rows[rowIndex])) === -1)
|
|
2813
|
+
return null;
|
|
2814
|
+
|
|
2815
|
+
if (filteredRowIndex === -1) {
|
|
2816
|
+
filteredRowIndex = rowIndex;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
return p.virtualListHelper.getItemElementAt(filteredRowIndex) || null;
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
/**
|
|
2823
|
+
* Refreshes all virtual rows
|
|
2824
|
+
* @public
|
|
2825
|
+
* @expose
|
|
2826
|
+
* @returns {DGTable} self
|
|
2827
|
+
*/
|
|
2828
|
+
refreshAllVirtualRows() {
|
|
2829
|
+
const p = this._p;
|
|
2830
|
+
p.virtualListHelper.invalidate().render();
|
|
2831
|
+
return this;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
/**
|
|
2835
|
+
* Replace the whole dataset
|
|
2836
|
+
* @public
|
|
2837
|
+
* @expose
|
|
2838
|
+
* @param {Object[]} data array of rows to add to the table
|
|
2839
|
+
* @param {boolean} [resort=false] should resort all rows?
|
|
2840
|
+
* @returns {DGTable} self
|
|
2841
|
+
*/
|
|
2842
|
+
setRows(data, resort) {
|
|
2843
|
+
let p = this._p;
|
|
2844
|
+
|
|
2845
|
+
// this.scrollTop = this.$el.find('.table').scrollTop();
|
|
2846
|
+
p.rows.reset(data);
|
|
2847
|
+
|
|
2848
|
+
if (resort && p.rows.sortColumn.length) {
|
|
2849
|
+
this.resort();
|
|
2850
|
+
} else {
|
|
2851
|
+
this._refilter();
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
this.clearAndRender().trigger('addrows', { count: data.length, clear: true });
|
|
2855
|
+
|
|
2856
|
+
return this;
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
/**
|
|
2860
|
+
* Creates a URL representing the data in the specified element.
|
|
2861
|
+
* This uses the Blob or BlobBuilder of the modern browsers.
|
|
2862
|
+
* The url can be used for a Web Worker.
|
|
2863
|
+
* @public
|
|
2864
|
+
* @expose
|
|
2865
|
+
* @param {string} id Id of the element containing your data
|
|
2866
|
+
* @returns {string|null} the url, or null if not supported
|
|
2867
|
+
*/
|
|
2868
|
+
getUrlForElementContent(id) {
|
|
2869
|
+
let blob,
|
|
2870
|
+
el = document.getElementById(id);
|
|
2871
|
+
if (el) {
|
|
2872
|
+
let data = el.textContent;
|
|
2873
|
+
if (typeof Blob === 'function') {
|
|
2874
|
+
blob = new Blob([data]);
|
|
2875
|
+
} else {
|
|
2876
|
+
let BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
|
|
2877
|
+
if (!BlobBuilder) {
|
|
2878
|
+
return null;
|
|
2879
|
+
}
|
|
2880
|
+
let builder = new BlobBuilder();
|
|
2881
|
+
builder.append(data);
|
|
2882
|
+
blob = builder.getBlob();
|
|
2883
|
+
}
|
|
2884
|
+
return (window.URL || window.webkitURL).createObjectURL(blob);
|
|
2885
|
+
}
|
|
2886
|
+
return null;
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
/**
|
|
2890
|
+
* @public
|
|
2891
|
+
* @expose
|
|
2892
|
+
* @returns {boolean} A value indicating whether Web Workers are supported
|
|
2893
|
+
*/
|
|
2894
|
+
isWorkerSupported() {
|
|
2895
|
+
return window['Worker'] instanceof Function;
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
/**
|
|
2899
|
+
* Creates a Web Worker for updating the table.
|
|
2900
|
+
* @public
|
|
2901
|
+
* @expose
|
|
2902
|
+
* @param {string} url Url to the script for the Web Worker
|
|
2903
|
+
* @param {boolean} [start=true] if true, starts the Worker immediately
|
|
2904
|
+
* @param {boolean} [resort=false]
|
|
2905
|
+
* @returns {Worker|null} the Web Worker, or null if not supported
|
|
2906
|
+
*/
|
|
2907
|
+
createWebWorker(url, start, resort) {
|
|
2908
|
+
if (this.isWorkerSupported()) {
|
|
2909
|
+
let p = this._p;
|
|
2910
|
+
|
|
2911
|
+
let worker = new Worker(url);
|
|
2912
|
+
let listener = (evt) => {
|
|
2913
|
+
if (evt.data.append) {
|
|
2914
|
+
this.addRows(evt.data.rows, resort);
|
|
2915
|
+
} else {
|
|
2916
|
+
this.setRows(evt.data.rows, resort);
|
|
2917
|
+
}
|
|
2918
|
+
};
|
|
2919
|
+
worker.addEventListener('message', listener, false);
|
|
2920
|
+
if (!p.workerListeners) {
|
|
2921
|
+
p.workerListeners = [];
|
|
2922
|
+
}
|
|
2923
|
+
p.workerListeners.push({ worker: worker, listener: listener });
|
|
2924
|
+
if (start || start === undefined) {
|
|
2925
|
+
worker.postMessage(null);
|
|
2926
|
+
}
|
|
2927
|
+
return worker;
|
|
2928
|
+
}
|
|
2929
|
+
return null;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
/**
|
|
2933
|
+
* Unbinds a Web Worker from the table, stopping updates.
|
|
2934
|
+
* @public
|
|
2935
|
+
* @expose
|
|
2936
|
+
* @param {Worker} worker the Web Worker
|
|
2937
|
+
* @returns {DGTable} self
|
|
2938
|
+
*/
|
|
2939
|
+
unbindWebWorker(worker) {
|
|
2940
|
+
let p = this._p;
|
|
2941
|
+
|
|
2942
|
+
if (p.workerListeners) {
|
|
2943
|
+
for (let j = 0; j < p.workerListeners.length; j++) {
|
|
2944
|
+
if (p.workerListeners[j].worker === worker) {
|
|
2945
|
+
worker.removeEventListener('message', p.workerListeners[j].listener, false);
|
|
2946
|
+
p.workerListeners.splice(j, 1);
|
|
2947
|
+
j--;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
return this;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
/**
|
|
2956
|
+
* A synonym for hideCellPreview()
|
|
2957
|
+
* @public
|
|
2958
|
+
* @expose
|
|
2959
|
+
* @returns {DGTable} self
|
|
2960
|
+
*/
|
|
2961
|
+
abortCellPreview() {
|
|
2962
|
+
this.hideCellPreview();
|
|
2963
|
+
return this;
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
/**
|
|
2967
|
+
* Cancel a resize in progress
|
|
2968
|
+
* @expose
|
|
2969
|
+
* @private
|
|
2970
|
+
* @returns {DGTable} self
|
|
2971
|
+
*/
|
|
2972
|
+
cancelColumnResize() {
|
|
2973
|
+
const p = this._p;
|
|
2974
|
+
|
|
2975
|
+
if (p.resizer) {
|
|
2976
|
+
p.resizer.remove();
|
|
2977
|
+
p.resizer = null;
|
|
2978
|
+
p.eventsSink.remove(document, '.colresize');
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
return this;
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
_onTableScrolledHorizontally() {
|
|
2985
|
+
const p = this._p;
|
|
2986
|
+
|
|
2987
|
+
p.header.scrollLeft = p.table.scrollLeft;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
/**previousElementSibling
|
|
2991
|
+
* Reverse-calculate the column to resize from mouse position
|
|
2992
|
+
* @private
|
|
2993
|
+
* @param {MouseEvent} event mouse event
|
|
2994
|
+
* @returns {string|null} name of the column which the mouse is over, or null if the mouse is not in resize position
|
|
2995
|
+
*/
|
|
2996
|
+
_getColumnByResizePosition(event) {
|
|
2997
|
+
let o = this._o,
|
|
2998
|
+
rtl = this._isTableRtl();
|
|
2999
|
+
|
|
3000
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3001
|
+
if (headerCell[OriginalCellSymbol]) {
|
|
3002
|
+
headerCell = headerCell[OriginalCellSymbol];
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
let previousElementSibling = headerCell.previousSibling;
|
|
3006
|
+
while (previousElementSibling && previousElementSibling.nodeType !== 1) {
|
|
3007
|
+
previousElementSibling = previousElementSibling.previousSibling;
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
let firstCol = !previousElementSibling;
|
|
3011
|
+
|
|
3012
|
+
let mouseX = (event.pageX || event.clientX) - Css_js.getElementOffset(headerCell).left;
|
|
3013
|
+
|
|
3014
|
+
if (rtl) {
|
|
3015
|
+
if (!firstCol && Css_js.getElementWidth(headerCell, true, true, true) - mouseX <= o.resizeAreaWidth / 2) {
|
|
3016
|
+
return previousElementSibling['columnName'];
|
|
3017
|
+
} else if (mouseX <= o.resizeAreaWidth / 2) {
|
|
3018
|
+
return headerCell['columnName'];
|
|
3019
|
+
}
|
|
3020
|
+
} else {
|
|
3021
|
+
if (!firstCol && mouseX <= o.resizeAreaWidth / 2) {
|
|
3022
|
+
return previousElementSibling['columnName'];
|
|
3023
|
+
} else if (Css_js.getElementWidth(headerCell, true, true, true) - mouseX <= o.resizeAreaWidth / 2) {
|
|
3024
|
+
return headerCell['columnName'];
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
return null;
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
/**
|
|
3032
|
+
* @param {TouchEvent} event
|
|
3033
|
+
*/
|
|
3034
|
+
_onTouchStartColumnHeader(event) {
|
|
3035
|
+
const p = this._p;
|
|
3036
|
+
|
|
3037
|
+
if (p.currentTouchId) return;
|
|
3038
|
+
|
|
3039
|
+
let startTouch = event.changedTouches[0];
|
|
3040
|
+
p.currentTouchId = startTouch.identifier;
|
|
3041
|
+
|
|
3042
|
+
let cellEl = event.currentTarget;
|
|
3043
|
+
|
|
3044
|
+
let startPos = { x: startTouch.pageX, y: startTouch.pageY },
|
|
3045
|
+
currentPos = startPos,
|
|
3046
|
+
distanceTreshold = 9;
|
|
3047
|
+
|
|
3048
|
+
let tapAndHoldTimeout;
|
|
3049
|
+
|
|
3050
|
+
let unbind = function () {
|
|
3051
|
+
p.currentTouchId = null;
|
|
3052
|
+
p.eventsSink.remove(cellEl, '.colheader');
|
|
3053
|
+
clearTimeout(tapAndHoldTimeout);
|
|
3054
|
+
};
|
|
3055
|
+
|
|
3056
|
+
let fakeMouseEvent = function (name) {
|
|
3057
|
+
for (const k of event)
|
|
3058
|
+
event[k];for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {args[_key - 1] = arguments[_key];}
|
|
3059
|
+
|
|
3060
|
+
for (const obj of args) {
|
|
3061
|
+
for (const key of ['target', 'clientX', 'clientY', 'offsetX', 'offsetY', 'screenX', 'screenY', 'pageX', 'pageY', 'which']) {
|
|
3062
|
+
if (obj[key] != null)
|
|
3063
|
+
obj[key];
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
return new MouseEvent(name, event);
|
|
3068
|
+
};
|
|
3069
|
+
|
|
3070
|
+
cellEl.dispatchEvent(
|
|
3071
|
+
fakeMouseEvent('mousedown', event.changedTouches[0], { button: 0, target: event.target })
|
|
3072
|
+
);
|
|
3073
|
+
|
|
3074
|
+
tapAndHoldTimeout = setTimeout(() => {
|
|
3075
|
+
unbind();
|
|
3076
|
+
|
|
3077
|
+
p.eventsSink.
|
|
3078
|
+
add(cellEl, 'touchend.colheader', (event) => {
|
|
3079
|
+
// Prevent simulated mouse events after touchend
|
|
3080
|
+
if (!isInputElementEvent(event))
|
|
3081
|
+
event.preventDefault();
|
|
3082
|
+
|
|
3083
|
+
p.eventsSink.remove(cellEl, '.colheader');
|
|
3084
|
+
}, { once: true }).
|
|
3085
|
+
add(cellEl, 'touchcancel.colheader', (_event) => {
|
|
3086
|
+
p.eventsSink.remove(cellEl, '.colheader');
|
|
3087
|
+
}, { once: true });
|
|
3088
|
+
|
|
3089
|
+
let distanceTravelled = Math.sqrt(Math.pow(Math.abs(currentPos.x - startPos.x), 2) + Math.pow(Math.abs(currentPos.y - startPos.y), 2));
|
|
3090
|
+
|
|
3091
|
+
if (distanceTravelled < distanceTreshold) {
|
|
3092
|
+
this.cancelColumnResize();
|
|
3093
|
+
|
|
3094
|
+
cellEl.dispatchEvent(
|
|
3095
|
+
fakeMouseEvent('mouseup', event.changedTouches[0], { button: 2, target: event.target })
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
}, 500);
|
|
3100
|
+
|
|
3101
|
+
p.eventsSink.
|
|
3102
|
+
add(cellEl, 'touchend.colheader', (event) => {
|
|
3103
|
+
let touch = find(event.changedTouches, (touch) => touch.identifier === p.currentTouchId);
|
|
3104
|
+
if (!touch) return;
|
|
3105
|
+
|
|
3106
|
+
unbind();
|
|
3107
|
+
|
|
3108
|
+
// Prevent simulated mouse events after touchend
|
|
3109
|
+
if (!isInputElementEvent(event))
|
|
3110
|
+
event.preventDefault();
|
|
3111
|
+
|
|
3112
|
+
currentPos = { x: touch.pageX, y: touch.pageY };
|
|
3113
|
+
let distanceTravelled = Math.sqrt(Math.pow(Math.abs(currentPos.x - startPos.x), 2) + Math.pow(Math.abs(currentPos.y - startPos.y), 2));
|
|
3114
|
+
|
|
3115
|
+
if (distanceTravelled < distanceTreshold || p.resizer) {
|
|
3116
|
+
cellEl.dispatchEvent(
|
|
3117
|
+
fakeMouseEvent('mouseup', touch, { 0: 2, target: event.target })
|
|
3118
|
+
);
|
|
3119
|
+
|
|
3120
|
+
cellEl.dispatchEvent(
|
|
3121
|
+
fakeMouseEvent('click', touch, { button: 0, target: event.target })
|
|
3122
|
+
);
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
}).
|
|
3126
|
+
add(cellEl, 'touchcancel.colheader', unbind).
|
|
3127
|
+
add(cellEl, 'touchmove.colheader', (event) => {
|
|
3128
|
+
let touch = find(event.changedTouches, (touch) => touch.identifier === p.currentTouchId);
|
|
3129
|
+
if (!touch) return;
|
|
3130
|
+
|
|
3131
|
+
// Keep track of current position, so we know if we need to cancel the tap-and-hold
|
|
3132
|
+
currentPos = { x: touch.pageX, y: touch.pageY };
|
|
3133
|
+
|
|
3134
|
+
if (p.resizer) {
|
|
3135
|
+
event.preventDefault();
|
|
3136
|
+
|
|
3137
|
+
cellEl.dispatchEvent(
|
|
3138
|
+
fakeMouseEvent('mousemove', touch, { target: event.target })
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
/**
|
|
3145
|
+
* @param {MouseEvent} event
|
|
3146
|
+
*/
|
|
3147
|
+
_onMouseDownColumnHeader(event) {
|
|
3148
|
+
if (event.button !== 0) return this; // Only treat left-clicks
|
|
3149
|
+
|
|
3150
|
+
let o = this._o,
|
|
3151
|
+
p = this._p,
|
|
3152
|
+
col = this._getColumnByResizePosition(event);
|
|
3153
|
+
|
|
3154
|
+
if (col) {
|
|
3155
|
+
let column = p.columns.get(col);
|
|
3156
|
+
if (!o.resizableColumns || !column || !column.resizable) {
|
|
3157
|
+
return false;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
let rtl = this._isTableRtl();
|
|
3161
|
+
|
|
3162
|
+
if (p.resizer) {
|
|
3163
|
+
p.resizer.remove();
|
|
3164
|
+
}
|
|
3165
|
+
p.resizer = createElement('div');
|
|
3166
|
+
p.resizer.className = o.resizerClassName;
|
|
3167
|
+
Css_js.setCssProps(p.resizer, {
|
|
3168
|
+
position: 'absolute',
|
|
3169
|
+
display: 'block',
|
|
3170
|
+
zIndex: -1,
|
|
3171
|
+
visibility: 'hidden',
|
|
3172
|
+
width: '2px',
|
|
3173
|
+
background: '#000',
|
|
3174
|
+
opacity: 0.7
|
|
3175
|
+
});
|
|
3176
|
+
this.el.appendChild(p.resizer);
|
|
3177
|
+
|
|
3178
|
+
let selectedHeaderCell = column.element,
|
|
3179
|
+
commonAncestor = p.resizer.parentNode;
|
|
3180
|
+
|
|
3181
|
+
const commonAncestorStyle = getComputedStyle(commonAncestor);
|
|
3182
|
+
const selectedHeaderCellStyle = getComputedStyle(selectedHeaderCell);
|
|
3183
|
+
|
|
3184
|
+
let posCol = Css_js.getElementOffset(selectedHeaderCell),
|
|
3185
|
+
posRelative = Css_js.getElementOffset(commonAncestor);
|
|
3186
|
+
posRelative.left += parseFloat(commonAncestorStyle.borderLeftWidth) || 0;
|
|
3187
|
+
posRelative.top += parseFloat(commonAncestorStyle.borderTopWidth) || 0;
|
|
3188
|
+
posCol.left -= posRelative.left;
|
|
3189
|
+
posCol.top -= posRelative.top;
|
|
3190
|
+
posCol.top -= parseFloat(selectedHeaderCellStyle.borderTopWidth) || 0;
|
|
3191
|
+
let resizerWidth = Css_js.getElementWidth(p.resizer, true, true, true);
|
|
3192
|
+
if (rtl) {
|
|
3193
|
+
posCol.left -= Math.ceil((parseFloat(selectedHeaderCellStyle.borderLeftWidth) || 0) / 2);
|
|
3194
|
+
posCol.left -= Math.ceil(resizerWidth / 2);
|
|
3195
|
+
} else {
|
|
3196
|
+
posCol.left += Css_js.getElementWidth(selectedHeaderCell, true, true, true);
|
|
3197
|
+
posCol.left += Math.ceil((parseFloat(selectedHeaderCellStyle.borderRightWidth) || 0) / 2);
|
|
3198
|
+
posCol.left -= Math.ceil(resizerWidth / 2);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
Css_js.setCssProps(p.resizer, {
|
|
3202
|
+
'z-index': '10',
|
|
3203
|
+
'visibility': 'visible',
|
|
3204
|
+
'left': posCol.left + 'px',
|
|
3205
|
+
'top': posCol.top + 'px',
|
|
3206
|
+
'height': Css_js.getElementHeight(this.el, false, false, false) + 'px'
|
|
3207
|
+
});
|
|
3208
|
+
p.resizer['columnName'] = selectedHeaderCell['columnName'];
|
|
3209
|
+
|
|
3210
|
+
try {p.resizer.style.zIndex = '';}
|
|
3211
|
+
catch (ignored) {/* we're ok with this */}
|
|
3212
|
+
|
|
3213
|
+
p.eventsSink.
|
|
3214
|
+
add(document, 'mousemove.colresize', this._onMouseMoveResizeArea.bind(this)).
|
|
3215
|
+
add(document, 'mouseup.colresize', this._onEndDragColumnHeader.bind(this));
|
|
3216
|
+
|
|
3217
|
+
event.preventDefault();
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
/**
|
|
3222
|
+
* @param {MouseEvent} event event
|
|
3223
|
+
*/
|
|
3224
|
+
_onMouseMoveColumnHeader(event) {
|
|
3225
|
+
let o = this._o,
|
|
3226
|
+
p = this._p;
|
|
3227
|
+
|
|
3228
|
+
if (o.resizableColumns) {
|
|
3229
|
+
let col = this._getColumnByResizePosition(event);
|
|
3230
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3231
|
+
if (!col || !p.columns.get(col).resizable) {
|
|
3232
|
+
headerCell.style.cursor = '';
|
|
3233
|
+
} else {
|
|
3234
|
+
headerCell.style.cursor = 'e-resize';
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
/**
|
|
3240
|
+
* @param {MouseEvent} event
|
|
3241
|
+
*/
|
|
3242
|
+
_onMouseUpColumnHeader(event) {
|
|
3243
|
+
if (event.button === 2) {
|
|
3244
|
+
let o = this._o;
|
|
3245
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3246
|
+
let bounds = Css_js.getElementOffset(headerCell);
|
|
3247
|
+
bounds['width'] = Css_js.getElementWidth(headerCell, true, true, true);
|
|
3248
|
+
bounds['height'] = Css_js.getElementHeight(headerCell, true, true, true);
|
|
3249
|
+
this.emit('headercontextmenu', {
|
|
3250
|
+
name: headerCell['columnName'],
|
|
3251
|
+
pageX: event.pageX,
|
|
3252
|
+
pageY: event.pageY,
|
|
3253
|
+
bounds: bounds
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
return this;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
/**
|
|
3260
|
+
* @private
|
|
3261
|
+
* @param {MouseEvent} event event
|
|
3262
|
+
*/
|
|
3263
|
+
_onMouseLeaveColumnHeader(event) {
|
|
3264
|
+
let o = this._o;
|
|
3265
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3266
|
+
headerCell.style.cursor = '';
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
/**
|
|
3270
|
+
* @private
|
|
3271
|
+
* @param {MouseEvent} event event
|
|
3272
|
+
*/
|
|
3273
|
+
_onClickColumnHeader(event) {
|
|
3274
|
+
if (isInputElementEvent(event))
|
|
3275
|
+
return;
|
|
3276
|
+
|
|
3277
|
+
if (!this._getColumnByResizePosition(event)) {
|
|
3278
|
+
let o = this._o,
|
|
3279
|
+
p = this._p;
|
|
3280
|
+
|
|
3281
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3282
|
+
if (o.sortableColumns) {
|
|
3283
|
+
let column = p.columns.get(headerCell['columnName']);
|
|
3284
|
+
let currentSort = p.rows.sortColumn;
|
|
3285
|
+
if (column && column.sortable) {
|
|
3286
|
+
let shouldAdd = true;
|
|
3287
|
+
|
|
3288
|
+
let lastSort = currentSort.length ? currentSort[currentSort.length - 1] : null;
|
|
3289
|
+
|
|
3290
|
+
if (lastSort && lastSort.column === column.name) {
|
|
3291
|
+
if (!lastSort.descending || !o.allowCancelSort) {
|
|
3292
|
+
lastSort.descending = !lastSort.descending;
|
|
3293
|
+
} else {
|
|
3294
|
+
shouldAdd = false;
|
|
3295
|
+
currentSort.splice(currentSort.length - 1, 1);
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
if (shouldAdd) {
|
|
3300
|
+
this.sort(column.name, undefined, true).render();
|
|
3301
|
+
} else {
|
|
3302
|
+
this.sort(); // just refresh current situation
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
/**
|
|
3310
|
+
* @private
|
|
3311
|
+
* @param {DragEvent} event event
|
|
3312
|
+
*/
|
|
3313
|
+
_onStartDragColumnHeader(event) {
|
|
3314
|
+
let o = this._o,
|
|
3315
|
+
p = this._p;
|
|
3316
|
+
|
|
3317
|
+
if (o.movableColumns) {
|
|
3318
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3319
|
+
let column = p.columns.get(headerCell['columnName']);
|
|
3320
|
+
if (column && column.movable) {
|
|
3321
|
+
headerCell.style.opacity = 0.35;
|
|
3322
|
+
p.dragId = Math.random() * 0x9999999; // Recognize this ID on drop
|
|
3323
|
+
event.dataTransfer.setData('text', JSON.stringify({ dragId: p.dragId, column: column.name }));
|
|
3324
|
+
} else {
|
|
3325
|
+
event.preventDefault();
|
|
3326
|
+
}
|
|
3327
|
+
} else {
|
|
3328
|
+
event.preventDefault();
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
return undefined;
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
/**
|
|
3335
|
+
* @private
|
|
3336
|
+
* @param {MouseEvent} event event
|
|
3337
|
+
*/
|
|
3338
|
+
_onMouseMoveResizeArea(event) {
|
|
3339
|
+
|
|
3340
|
+
let p = this._p;
|
|
3341
|
+
|
|
3342
|
+
let column = p.columns.get(p.resizer['columnName']);
|
|
3343
|
+
let rtl = this._isTableRtl();
|
|
3344
|
+
|
|
3345
|
+
let selectedHeaderCell = column.element,
|
|
3346
|
+
commonAncestor = p.resizer.parentNode;
|
|
3347
|
+
|
|
3348
|
+
const commonAncestorStyle = getComputedStyle(commonAncestor);
|
|
3349
|
+
const selectedHeaderCellStyle = getComputedStyle(selectedHeaderCell);
|
|
3350
|
+
|
|
3351
|
+
let posCol = Css_js.getElementOffset(selectedHeaderCell),
|
|
3352
|
+
posRelative = Css_js.getElementOffset(commonAncestor);
|
|
3353
|
+
posRelative.left += parseFloat(commonAncestorStyle.borderLeftWidth) || 0;
|
|
3354
|
+
posCol.left -= posRelative.left;
|
|
3355
|
+
let resizerWidth = Css_js.getElementWidth(p.resizer, true, true, true);
|
|
3356
|
+
|
|
3357
|
+
let isBoxing = selectedHeaderCellStyle.boxSizing === 'border-box';
|
|
3358
|
+
|
|
3359
|
+
let actualX = event.pageX - posRelative.left;
|
|
3360
|
+
let minX = posCol.left;
|
|
3361
|
+
|
|
3362
|
+
minX -= Math.ceil(resizerWidth / 2);
|
|
3363
|
+
|
|
3364
|
+
if (rtl) {
|
|
3365
|
+
minX += Css_js.getElementWidth(selectedHeaderCell, true, true, true);
|
|
3366
|
+
minX -= column.ignoreMin ? 0 : this._o.minColumnWidth;
|
|
3367
|
+
|
|
3368
|
+
if (!isBoxing) {
|
|
3369
|
+
minX -= Math.ceil((parseFloat(selectedHeaderCellStyle.borderLeftWidth) || 0) / 2);
|
|
3370
|
+
minX -= this._horizontalPadding(selectedHeaderCell);
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
if (actualX > minX) {
|
|
3374
|
+
actualX = minX;
|
|
3375
|
+
}
|
|
3376
|
+
} else {
|
|
3377
|
+
minX += column.ignoreMin ? 0 : this._o.minColumnWidth;
|
|
3378
|
+
|
|
3379
|
+
if (!isBoxing) {
|
|
3380
|
+
minX += Math.ceil((parseFloat(selectedHeaderCellStyle.borderRightWidth) || 0) / 2);
|
|
3381
|
+
minX += this._horizontalPadding(selectedHeaderCell);
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
if (actualX < minX) {
|
|
3385
|
+
actualX = minX;
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
p.resizer.style.left = actualX + 'px';
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
/**
|
|
3393
|
+
* @private
|
|
3394
|
+
* @param {DragEvent} event event
|
|
3395
|
+
*/
|
|
3396
|
+
_onEndDragColumnHeader(event) {
|
|
3397
|
+
|
|
3398
|
+
let o = this._o,
|
|
3399
|
+
p = this._p;
|
|
3400
|
+
|
|
3401
|
+
if (!p.resizer) {
|
|
3402
|
+
event.target.style.opacity = null;
|
|
3403
|
+
} else {
|
|
3404
|
+
p.eventsSink.remove(document, '.colresize');
|
|
3405
|
+
|
|
3406
|
+
let column = p.columns.get(p.resizer['columnName']);
|
|
3407
|
+
let rtl = this._isTableRtl();
|
|
3408
|
+
|
|
3409
|
+
let selectedHeaderCell = column.element,
|
|
3410
|
+
selectedHeaderCellInner = selectedHeaderCell.firstChild,
|
|
3411
|
+
commonAncestor = p.resizer.parentNode;
|
|
3412
|
+
|
|
3413
|
+
const commonAncestorStyle = getComputedStyle(commonAncestor);
|
|
3414
|
+
const selectedHeaderCellStyle = getComputedStyle(selectedHeaderCell);
|
|
3415
|
+
|
|
3416
|
+
let posCol = Css_js.getElementOffset(selectedHeaderCell),
|
|
3417
|
+
posRelative = Css_js.getElementOffset(commonAncestor);
|
|
3418
|
+
posRelative.left += parseFloat(commonAncestorStyle.borderLeftWidth) || 0;
|
|
3419
|
+
posCol.left -= posRelative.left;
|
|
3420
|
+
let resizerWidth = Css_js.getElementWidth(p.resizer, true, true, true);
|
|
3421
|
+
|
|
3422
|
+
let isBoxing = selectedHeaderCellStyle.boxSizing === 'border-box';
|
|
3423
|
+
|
|
3424
|
+
let actualX = event.pageX - posRelative.left;
|
|
3425
|
+
let baseX = posCol.left,minX = posCol.left;
|
|
3426
|
+
let width = 0;
|
|
3427
|
+
|
|
3428
|
+
baseX -= Math.ceil(resizerWidth / 2);
|
|
3429
|
+
|
|
3430
|
+
if (rtl) {
|
|
3431
|
+
if (!isBoxing) {
|
|
3432
|
+
actualX += this._horizontalPadding(selectedHeaderCell);
|
|
3433
|
+
const innerComputedStyle = getComputedStyle(selectedHeaderCellInner || selectedHeaderCell);
|
|
3434
|
+
actualX += parseFloat(innerComputedStyle.borderLeftWidth) || 0;
|
|
3435
|
+
actualX += parseFloat(innerComputedStyle.borderRightWidth) || 0;
|
|
3436
|
+
actualX += column.arrowProposedWidth || 0; // Sort-arrow width
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
baseX += Css_js.getElementWidth(selectedHeaderCell, true, true, true);
|
|
3440
|
+
|
|
3441
|
+
minX = baseX - (column.ignoreMin ? 0 : this._o.minColumnWidth);
|
|
3442
|
+
if (actualX > minX) {
|
|
3443
|
+
actualX = minX;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
width = baseX - actualX;
|
|
3447
|
+
} else {
|
|
3448
|
+
if (!isBoxing) {
|
|
3449
|
+
actualX -= this._horizontalPadding(selectedHeaderCell);
|
|
3450
|
+
const innerComputedStyle = getComputedStyle(selectedHeaderCellInner || selectedHeaderCell);
|
|
3451
|
+
actualX -= parseFloat(innerComputedStyle.borderLeftWidth) || 0;
|
|
3452
|
+
actualX -= parseFloat(innerComputedStyle.borderRightWidth) || 0;
|
|
3453
|
+
actualX -= column.arrowProposedWidth || 0; // Sort-arrow width
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
minX = baseX + (column.ignoreMin ? 0 : this._o.minColumnWidth);
|
|
3457
|
+
if (actualX < minX) {
|
|
3458
|
+
actualX = minX;
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
width = actualX - baseX;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
p.resizer.remove();
|
|
3465
|
+
p.resizer = null;
|
|
3466
|
+
|
|
3467
|
+
let sizeToSet = width;
|
|
3468
|
+
|
|
3469
|
+
if (column.widthMode === ColumnWidthMode.RELATIVE) {
|
|
3470
|
+
let sizeLeft = this._calculateWidthAvailableForColumns();
|
|
3471
|
+
//sizeLeft -= p.table.offsetWidth - p.table.clientWidth;
|
|
3472
|
+
|
|
3473
|
+
let totalRelativePercentage = 0;
|
|
3474
|
+
let relatives = 0;
|
|
3475
|
+
|
|
3476
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
3477
|
+
let col = p.visibleColumns[i];
|
|
3478
|
+
if (col.name === column.name) continue;
|
|
3479
|
+
|
|
3480
|
+
if (col.widthMode === ColumnWidthMode.RELATIVE) {
|
|
3481
|
+
totalRelativePercentage += col.width;
|
|
3482
|
+
relatives++;
|
|
3483
|
+
} else {
|
|
3484
|
+
sizeLeft -= col.actualWidth;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
sizeLeft = Math.max(1, sizeLeft);
|
|
3489
|
+
if (sizeLeft === 1)
|
|
3490
|
+
sizeLeft = p.table.clientWidth;
|
|
3491
|
+
sizeToSet = width / sizeLeft;
|
|
3492
|
+
|
|
3493
|
+
if (relatives > 0) {
|
|
3494
|
+
// When there's more than one relative overall,
|
|
3495
|
+
// we can do relative enlarging/shrinking.
|
|
3496
|
+
// Otherwise, we can end up having a 0 width.
|
|
3497
|
+
|
|
3498
|
+
let unNormalizedSizeToSet = sizeToSet / ((1 - sizeToSet) / totalRelativePercentage);
|
|
3499
|
+
|
|
3500
|
+
totalRelativePercentage += sizeToSet;
|
|
3501
|
+
|
|
3502
|
+
// Account for relative widths scaling later
|
|
3503
|
+
if (totalRelativePercentage < 1 && o.relativeWidthGrowsToFillWidth ||
|
|
3504
|
+
totalRelativePercentage > 1 && o.relativeWidthShrinksToFillWidth) {
|
|
3505
|
+
sizeToSet = unNormalizedSizeToSet;
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
sizeToSet *= 100;
|
|
3510
|
+
sizeToSet += '%';
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
this.setColumnWidth(column.name, sizeToSet);
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
/**
|
|
3518
|
+
* @private
|
|
3519
|
+
* @param {DragEvent} event event
|
|
3520
|
+
*/
|
|
3521
|
+
_onDragEnterColumnHeader(event) {
|
|
3522
|
+
let o = this._o,
|
|
3523
|
+
p = this._p;
|
|
3524
|
+
|
|
3525
|
+
if (o.movableColumns) {
|
|
3526
|
+
let dataTransferred = event.dataTransfer.getData('text');
|
|
3527
|
+
if (dataTransferred) {
|
|
3528
|
+
dataTransferred = JSON.parse(dataTransferred);
|
|
3529
|
+
} else
|
|
3530
|
+
{
|
|
3531
|
+
dataTransferred = null; // WebKit does not provide the dataTransfer on dragenter?..
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3535
|
+
if (!dataTransferred ||
|
|
3536
|
+
p.dragId === dataTransferred.dragId && headerCell['columnName'] !== dataTransferred.column) {
|
|
3537
|
+
|
|
3538
|
+
let column = p.columns.get(headerCell['columnName']);
|
|
3539
|
+
if (column && (column.movable || column !== p.visibleColumns[0])) {
|
|
3540
|
+
headerCell.classList.add('drag-over');
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
/**
|
|
3547
|
+
* @private
|
|
3548
|
+
* @param {DragEvent} event event
|
|
3549
|
+
*/
|
|
3550
|
+
_onDragOverColumnHeader(event) {
|
|
3551
|
+
event.preventDefault();
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
/**
|
|
3555
|
+
* @private
|
|
3556
|
+
* @param {DragEvent} event event
|
|
3557
|
+
*/
|
|
3558
|
+
_onDragLeaveColumnHeader(event) {
|
|
3559
|
+
let o = this._o;
|
|
3560
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3561
|
+
if (!event.relatedTarget.contains(headerCell.firstChild)) {
|
|
3562
|
+
headerCell.classList.remove('drag-over');
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
/**
|
|
3567
|
+
* @private
|
|
3568
|
+
* @param {DragEvent} event event
|
|
3569
|
+
*/
|
|
3570
|
+
_onDropColumnHeader(event) {
|
|
3571
|
+
event.preventDefault();
|
|
3572
|
+
|
|
3573
|
+
let o = this._o,
|
|
3574
|
+
p = this._p;
|
|
3575
|
+
|
|
3576
|
+
let dataTransferred = JSON.parse(event.dataTransfer.getData('text'));
|
|
3577
|
+
let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
|
|
3578
|
+
if (o.movableColumns && dataTransferred.dragId === p.dragId) {
|
|
3579
|
+
let srcColName = dataTransferred.column,
|
|
3580
|
+
destColName = headerCell['columnName'],
|
|
3581
|
+
srcCol = p.columns.get(srcColName),
|
|
3582
|
+
destCol = p.columns.get(destColName);
|
|
3583
|
+
if (srcCol && destCol && srcCol.movable && (destCol.movable || destCol !== p.visibleColumns[0])) {
|
|
3584
|
+
this.moveColumn(srcColName, destColName);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
headerCell.classList.remove('drag-over');
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
/**
|
|
3591
|
+
* @private
|
|
3592
|
+
* @returns {DGTable} self
|
|
3593
|
+
*/
|
|
3594
|
+
_clearSortArrows() {
|
|
3595
|
+
let p = this._p;
|
|
3596
|
+
|
|
3597
|
+
if (p.table) {
|
|
3598
|
+
let tableClassName = this._o.tableClassName;
|
|
3599
|
+
let sortedColumns = DomCompat_js.scopedSelectorAll(p.headerRow, `>div.${tableClassName}-header-cell.sorted`);
|
|
3600
|
+
let arrows = Array.prototype.slice.call(sortedColumns, 0).map((el) => DomCompat_js.scopedSelector(el, '>div>.sort-arrow')).filter((el) => !!el);
|
|
3601
|
+
for (const arrow of arrows) {
|
|
3602
|
+
let col = p.columns.get(arrow.parentNode.parentNode['columnName']);
|
|
3603
|
+
if (col) {
|
|
3604
|
+
col.arrowProposedWidth = 0;
|
|
3605
|
+
}
|
|
3606
|
+
arrow.remove();
|
|
3607
|
+
}
|
|
3608
|
+
for (const sortedColumn of sortedColumns) {
|
|
3609
|
+
sortedColumn.classList.remove('sorted', 'desc');
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
return this;
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
/**
|
|
3616
|
+
* @private
|
|
3617
|
+
* @param {string} column the name of the sort column
|
|
3618
|
+
* @param {boolean} descending table is sorted descending
|
|
3619
|
+
* @returns {boolean} self
|
|
3620
|
+
*/
|
|
3621
|
+
_showSortArrow(column, descending) {
|
|
3622
|
+
let p = this._p;
|
|
3623
|
+
|
|
3624
|
+
let col = p.columns.get(column);
|
|
3625
|
+
if (!col) return false;
|
|
3626
|
+
|
|
3627
|
+
let arrow = createElement('span');
|
|
3628
|
+
arrow.className = 'sort-arrow';
|
|
3629
|
+
|
|
3630
|
+
if (col.element) {
|
|
3631
|
+
col.element.className += descending ? ' sorted desc' : ' sorted';
|
|
3632
|
+
col.element.firstChild.insertBefore(arrow, col.element.firstChild.firstChild);
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
if (col.widthMode !== ColumnWidthMode.RELATIVE && this._o.adjustColumnWidthForSortArrow) {
|
|
3636
|
+
col.arrowProposedWidth = arrow.scrollWidth + (
|
|
3637
|
+
parseFloat(getComputedStyle(arrow).marginRight) || 0) + (
|
|
3638
|
+
parseFloat(getComputedStyle(arrow).marginLeft) || 0);
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
return true;
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
/**
|
|
3645
|
+
* @private
|
|
3646
|
+
* @param {number} cellIndex index of the column in the DOM
|
|
3647
|
+
* @returns {DGTable} self
|
|
3648
|
+
*/
|
|
3649
|
+
_resizeColumnElements(cellIndex) {
|
|
3650
|
+
let p = this._p;
|
|
3651
|
+
|
|
3652
|
+
const headerCells = p.headerRow.querySelectorAll(`div.${this._o.tableClassName}-header-cell`);
|
|
3653
|
+
const headerCell = headerCells[cellIndex];
|
|
3654
|
+
let col = p.columns.get(headerCell['columnName']);
|
|
3655
|
+
|
|
3656
|
+
if (col) {
|
|
3657
|
+
headerCell.style.width = (col.actualWidthConsideringScrollbarWidth || col.actualWidth) + 'px';
|
|
3658
|
+
|
|
3659
|
+
let width = (col.actualWidthConsideringScrollbarWidth || col.actualWidth) + 'px';
|
|
3660
|
+
let tbodyChildren = p.tbody.childNodes;
|
|
3661
|
+
for (let i = 0, count = tbodyChildren.length; i < count; i++) {
|
|
3662
|
+
let rowEl = tbodyChildren[i];
|
|
3663
|
+
if (rowEl.nodeType !== 1) continue;
|
|
3664
|
+
rowEl.childNodes[cellIndex].style.width = width;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
return this;
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
/**
|
|
3672
|
+
* @returns {DGTable} self
|
|
3673
|
+
* */
|
|
3674
|
+
_destroyHeaderCells() {
|
|
3675
|
+
let p = this._p;
|
|
3676
|
+
|
|
3677
|
+
if (p.headerRow) {
|
|
3678
|
+
p.headerRow = null;
|
|
3679
|
+
}
|
|
3680
|
+
return this;
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3683
|
+
/**
|
|
3684
|
+
* @private
|
|
3685
|
+
* @returns {DGTable} self
|
|
3686
|
+
*/
|
|
3687
|
+
_renderSkeletonBase() {
|
|
3688
|
+
let p = this._p,
|
|
3689
|
+
o = this._o;
|
|
3690
|
+
|
|
3691
|
+
// Clean up old elements
|
|
3692
|
+
|
|
3693
|
+
p.virtualListHelper?.destroy();
|
|
3694
|
+
p.virtualListHelper = null;
|
|
3695
|
+
|
|
3696
|
+
if (p.table && o.virtualTable) {
|
|
3697
|
+
p.table.remove();
|
|
3698
|
+
p.table = p.tbody = null;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3701
|
+
this._destroyHeaderCells();
|
|
3702
|
+
p.currentTouchId = null;
|
|
3703
|
+
if (p.header) {
|
|
3704
|
+
p.header.remove();
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
// Create new base elements
|
|
3708
|
+
let tableClassName = o.tableClassName,
|
|
3709
|
+
header = createElement('div'),
|
|
3710
|
+
headerRow = createElement('div');
|
|
3711
|
+
|
|
3712
|
+
header.className = `${tableClassName}-header`;
|
|
3713
|
+
headerRow.className = `${tableClassName}-header-row`;
|
|
3714
|
+
|
|
3715
|
+
p.header = header;
|
|
3716
|
+
p.headerRow = headerRow;
|
|
3717
|
+
header.appendChild(headerRow);
|
|
3718
|
+
this.el.prepend(header);
|
|
3719
|
+
|
|
3720
|
+
relativizeElement(this.el);
|
|
3721
|
+
|
|
3722
|
+
if (o.width === DGTable.Width.SCROLL) {
|
|
3723
|
+
this.el.style.overflow = 'hidden';
|
|
3724
|
+
} else {
|
|
3725
|
+
this.el.style.overflow = '';
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
if (!o.height && o.virtualTable) {
|
|
3729
|
+
o.height = Css_js.getElementHeight(this.el, true);
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3732
|
+
return this;
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3735
|
+
_bindHeaderColumnEvents(columnEl) {
|
|
3736
|
+
const inner = columnEl.firstChild;
|
|
3737
|
+
columnEl.addEventListener('mousedown', this._onMouseDownColumnHeader.bind(this));
|
|
3738
|
+
columnEl.addEventListener('mousemove', this._onMouseMoveColumnHeader.bind(this));
|
|
3739
|
+
columnEl.addEventListener('mouseup', this._onMouseUpColumnHeader.bind(this));
|
|
3740
|
+
columnEl.addEventListener('mouseleave', this._onMouseLeaveColumnHeader.bind(this));
|
|
3741
|
+
columnEl.addEventListener('touchstart', this._onTouchStartColumnHeader.bind(this));
|
|
3742
|
+
columnEl.addEventListener('dragstart', this._onStartDragColumnHeader.bind(this));
|
|
3743
|
+
columnEl.addEventListener('click', this._onClickColumnHeader.bind(this));
|
|
3744
|
+
columnEl.addEventListener('contextmenu', (event) => {event.preventDefault();});
|
|
3745
|
+
inner.addEventListener('dragenter', this._onDragEnterColumnHeader.bind(this));
|
|
3746
|
+
inner.addEventListener('dragover', this._onDragOverColumnHeader.bind(this));
|
|
3747
|
+
inner.addEventListener('dragleave', this._onDragLeaveColumnHeader.bind(this));
|
|
3748
|
+
inner.addEventListener('drop', this._onDropColumnHeader.bind(this));
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3751
|
+
/**
|
|
3752
|
+
* @private
|
|
3753
|
+
* @returns {DGTable} self
|
|
3754
|
+
*/
|
|
3755
|
+
_renderSkeletonHeaderCells() {
|
|
3756
|
+
let p = this._p,
|
|
3757
|
+
o = this._o;
|
|
3758
|
+
|
|
3759
|
+
let allowCellPreview = o.allowCellPreview,
|
|
3760
|
+
allowHeaderCellPreview = o.allowHeaderCellPreview;
|
|
3761
|
+
|
|
3762
|
+
let tableClassName = o.tableClassName,
|
|
3763
|
+
headerCellClassName = tableClassName + '-header-cell',
|
|
3764
|
+
headerRow = p.headerRow;
|
|
3765
|
+
|
|
3766
|
+
// Create header cells
|
|
3767
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
3768
|
+
let column = p.visibleColumns[i];
|
|
3769
|
+
if (column.visible) {
|
|
3770
|
+
let cell = createElement('div');
|
|
3771
|
+
cell.draggable = true;
|
|
3772
|
+
cell.className = headerCellClassName;
|
|
3773
|
+
cell.style.width = column.actualWidth + 'px';
|
|
3774
|
+
if (o.sortableColumns && column.sortable) {
|
|
3775
|
+
cell.className += ' sortable';
|
|
3776
|
+
}
|
|
3777
|
+
cell['columnName'] = column.name;
|
|
3778
|
+
cell.setAttribute('data-column', column.name);
|
|
3779
|
+
|
|
3780
|
+
let cellInside = createElement('div');
|
|
3781
|
+
cellInside.innerHTML = o.headerCellFormatter(column.label, column.name);
|
|
3782
|
+
cell.appendChild(cellInside);
|
|
3783
|
+
if (allowCellPreview && allowHeaderCellPreview) {
|
|
3784
|
+
p._bindCellHoverIn(cell);
|
|
3785
|
+
}
|
|
3786
|
+
headerRow.appendChild(cell);
|
|
3787
|
+
|
|
3788
|
+
p.visibleColumns[i].element = cell;
|
|
3789
|
+
|
|
3790
|
+
this._bindHeaderColumnEvents(cell);
|
|
3791
|
+
this._disableCssSelect(cell);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
this.emit('headerrowcreate', headerRow);
|
|
3796
|
+
|
|
3797
|
+
return this;
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
/**
|
|
3801
|
+
* @private
|
|
3802
|
+
* @returns {DGTable} self
|
|
3803
|
+
*/
|
|
3804
|
+
_renderSkeletonBody() {
|
|
3805
|
+
let p = this._p,
|
|
3806
|
+
o = this._o;
|
|
3807
|
+
|
|
3808
|
+
let tableClassName = o.tableClassName;
|
|
3809
|
+
|
|
3810
|
+
// Calculate virtual row heights
|
|
3811
|
+
if (o.virtualTable && !p.virtualRowHeight) {
|
|
3812
|
+
let createDummyRow = () => {
|
|
3813
|
+
let row = createElement('div'),
|
|
3814
|
+
cell = row.appendChild(createElement('div')),
|
|
3815
|
+
cellInner = cell.appendChild(createElement('div'));
|
|
3816
|
+
row.className = `${tableClassName}-row`;
|
|
3817
|
+
cell.className = `${tableClassName}-cell`;
|
|
3818
|
+
cellInner.innerHTML = '0';
|
|
3819
|
+
row.style.visibility = 'hidden';
|
|
3820
|
+
row.style.position = 'absolute';
|
|
3821
|
+
return row;
|
|
3822
|
+
};
|
|
3823
|
+
|
|
3824
|
+
const dummyWrapper = createElement('div');
|
|
3825
|
+
dummyWrapper.className = this.el.className;
|
|
3826
|
+
Css_js.setCssProps(dummyWrapper, {
|
|
3827
|
+
'z-index': -1,
|
|
3828
|
+
'position': 'absolute',
|
|
3829
|
+
'left': '0',
|
|
3830
|
+
'top': '-9999px',
|
|
3831
|
+
'width': '1px',
|
|
3832
|
+
'overflow': 'hidden'
|
|
3833
|
+
});
|
|
3834
|
+
|
|
3835
|
+
const dummyTable = createElement('div');
|
|
3836
|
+
dummyTable.className = tableClassName;
|
|
3837
|
+
dummyWrapper.appendChild(dummyTable);
|
|
3838
|
+
|
|
3839
|
+
const dummyTbody = createElement('div');
|
|
3840
|
+
dummyTbody.className = `${tableClassName}-body`;
|
|
3841
|
+
dummyTbody.style.width = '99999px';
|
|
3842
|
+
dummyTable.appendChild(dummyTbody);
|
|
3843
|
+
|
|
3844
|
+
document.body.appendChild(dummyWrapper);
|
|
3845
|
+
|
|
3846
|
+
let row1 = createDummyRow(),row2 = createDummyRow(),row3 = createDummyRow();
|
|
3847
|
+
dummyTbody.appendChild(row1);
|
|
3848
|
+
dummyTbody.appendChild(row2);
|
|
3849
|
+
dummyTbody.appendChild(row3);
|
|
3850
|
+
|
|
3851
|
+
p.virtualRowHeightFirst = Css_js.getElementHeight(row1, true, true, true);
|
|
3852
|
+
p.virtualRowHeight = Css_js.getElementHeight(row2, true, true, true);
|
|
3853
|
+
p.virtualRowHeightLast = Css_js.getElementHeight(row3, true, true, true);
|
|
3854
|
+
|
|
3855
|
+
dummyWrapper.remove();
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3858
|
+
// Create inner table and tbody
|
|
3859
|
+
if (!p.table) {
|
|
3860
|
+
let fragment = document.createDocumentFragment();
|
|
3861
|
+
|
|
3862
|
+
// Create the inner table element
|
|
3863
|
+
let table = createElement('div');
|
|
3864
|
+
table.className = tableClassName;
|
|
3865
|
+
|
|
3866
|
+
if (o.virtualTable) {
|
|
3867
|
+
table.className += ' virtual';
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
const tableStyle = getComputedStyle(table);
|
|
3871
|
+
|
|
3872
|
+
let tableHeight = o.height - Css_js.getElementHeight(p.header, true, true, true);
|
|
3873
|
+
if (tableStyle.boxSizing !== 'border-box') {
|
|
3874
|
+
tableHeight -= parseFloat(tableStyle.borderTopWidth) || 0;
|
|
3875
|
+
tableHeight -= parseFloat(tableStyle.borderBottomWidth) || 0;
|
|
3876
|
+
tableHeight -= parseFloat(tableStyle.paddingTop) || 0;
|
|
3877
|
+
tableHeight -= parseFloat(tableStyle.paddingBottom) || 0;
|
|
3878
|
+
}
|
|
3879
|
+
p.visibleHeight = tableHeight;
|
|
3880
|
+
Css_js.setCssProps(table, {
|
|
3881
|
+
height: o.height ? tableHeight + 'px' : 'auto',
|
|
3882
|
+
display: 'block',
|
|
3883
|
+
overflowY: 'auto',
|
|
3884
|
+
overflowX: o.width === DGTable.Width.SCROLL ? 'auto' : 'hidden'
|
|
3885
|
+
});
|
|
3886
|
+
fragment.appendChild(table);
|
|
3887
|
+
|
|
3888
|
+
// Create the "tbody" element
|
|
3889
|
+
let tbody = createElement('div');
|
|
3890
|
+
tbody.className = o.tableClassName + '-body';
|
|
3891
|
+
tbody.style.minHeight = '1px';
|
|
3892
|
+
p.table = table;
|
|
3893
|
+
p.tbody = tbody;
|
|
3894
|
+
|
|
3895
|
+
relativizeElement(tbody);
|
|
3896
|
+
relativizeElement(table);
|
|
3897
|
+
|
|
3898
|
+
table.appendChild(tbody);
|
|
3899
|
+
this.el.appendChild(fragment);
|
|
3900
|
+
|
|
3901
|
+
this._setupVirtualTable();
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
return this;
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
/**
|
|
3908
|
+
* @private
|
|
3909
|
+
* @returns {DGTable} self
|
|
3910
|
+
* @deprecated
|
|
3911
|
+
*/
|
|
3912
|
+
_renderSkeleton() {
|
|
3913
|
+
return this;
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
/**
|
|
3917
|
+
* @private
|
|
3918
|
+
* @returns {DGTable} self
|
|
3919
|
+
*/
|
|
3920
|
+
_updateVirtualHeight() {
|
|
3921
|
+
const o = this._o,p = this._p;
|
|
3922
|
+
|
|
3923
|
+
if (!p.tbody)
|
|
3924
|
+
return this;
|
|
3925
|
+
|
|
3926
|
+
if (o.virtualTable) {
|
|
3927
|
+
const virtualHeight = p.virtualListHelper.estimateFullHeight();
|
|
3928
|
+
p.lastVirtualScrollHeight = virtualHeight;
|
|
3929
|
+
p.tbody.style.height = virtualHeight + 'px';
|
|
3930
|
+
} else {
|
|
3931
|
+
p.tbody.style.height = '';
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
return this;
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
/**
|
|
3938
|
+
* @private
|
|
3939
|
+
* @returns {DGTable} self
|
|
3940
|
+
*/
|
|
3941
|
+
_updateLastCellWidthFromScrollbar(force) {
|
|
3942
|
+
|
|
3943
|
+
const p = this._p;
|
|
3944
|
+
|
|
3945
|
+
// Calculate scrollbar's width and reduce from lat column's width
|
|
3946
|
+
let scrollbarWidth = p.table.offsetWidth - p.table.clientWidth;
|
|
3947
|
+
if (scrollbarWidth !== p.scrollbarWidth || force) {
|
|
3948
|
+
p.scrollbarWidth = scrollbarWidth;
|
|
3949
|
+
for (let i = 0; i < p.columns.length; i++) {
|
|
3950
|
+
p.columns[i].actualWidthConsideringScrollbarWidth = null;
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
if (p.scrollbarWidth > 0 && p.visibleColumns.length > 0) {
|
|
3954
|
+
// (There should always be at least 1 column visible, but just in case)
|
|
3955
|
+
let lastColIndex = p.visibleColumns.length - 1;
|
|
3956
|
+
|
|
3957
|
+
p.visibleColumns[lastColIndex].actualWidthConsideringScrollbarWidth = p.visibleColumns[lastColIndex].actualWidth - p.scrollbarWidth;
|
|
3958
|
+
let lastColWidth = p.visibleColumns[lastColIndex].actualWidthConsideringScrollbarWidth + 'px';
|
|
3959
|
+
let tbodyChildren = p.tbody.childNodes;
|
|
3960
|
+
for (let i = 0, count = tbodyChildren.length; i < count; i++) {
|
|
3961
|
+
let row = tbodyChildren[i];
|
|
3962
|
+
if (row.nodeType !== 1) continue;
|
|
3963
|
+
row.childNodes[lastColIndex].style.width = lastColWidth;
|
|
3964
|
+
}
|
|
3965
|
+
|
|
3966
|
+
p.headerRow.childNodes[lastColIndex].style.width = lastColWidth;
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3969
|
+
p.notifyRendererOfColumnsConfig?.();
|
|
3970
|
+
}
|
|
3971
|
+
|
|
3972
|
+
return this;
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
/**
|
|
3976
|
+
* Explicitly set the width of the table based on the sum of the column widths
|
|
3977
|
+
* @private
|
|
3978
|
+
* @param {boolean} parentSizeMayHaveChanged Parent size may have changed, treat rendering accordingly
|
|
3979
|
+
* @returns {DGTable} self
|
|
3980
|
+
*/
|
|
3981
|
+
_updateTableWidth(parentSizeMayHaveChanged) {
|
|
3982
|
+
const o = this._o,p = this._p;
|
|
3983
|
+
let width = this._calculateTbodyWidth();
|
|
3984
|
+
|
|
3985
|
+
p.tbody.style.minWidth = width + 'px';
|
|
3986
|
+
p.headerRow.style.minWidth = width + (p.scrollbarWidth || 0) + 'px';
|
|
3987
|
+
|
|
3988
|
+
p.eventsSink.remove(p.table, 'scroll');
|
|
3989
|
+
|
|
3990
|
+
if (o.width === DGTable.Width.AUTO) {
|
|
3991
|
+
// Update wrapper element's size to fully contain the table body
|
|
3992
|
+
|
|
3993
|
+
Css_js.setElementWidth(p.table, Css_js.getElementWidth(p.tbody, true, true, true));
|
|
3994
|
+
Css_js.setElementWidth(this.el, Css_js.getElementWidth(p.table, true, true, true));
|
|
3995
|
+
|
|
3996
|
+
} else if (o.width === DGTable.Width.SCROLL) {
|
|
3997
|
+
|
|
3998
|
+
if (parentSizeMayHaveChanged) {
|
|
3999
|
+
let lastScrollTop = p.table ? p.table.scrollTop : 0,
|
|
4000
|
+
lastScrollLeft = p.table ? p.table.scrollLeft : 0;
|
|
4001
|
+
|
|
4002
|
+
// BUGFIX: Relayout before recording the widths
|
|
4003
|
+
webkitRenderBugfix(this.el);
|
|
4004
|
+
|
|
4005
|
+
p.table.crollTop = lastScrollTop;
|
|
4006
|
+
p.table.scrollLeft = lastScrollLeft;
|
|
4007
|
+
p.header.scrollLeft = lastScrollLeft;
|
|
4008
|
+
}
|
|
4009
|
+
|
|
4010
|
+
p.eventsSink.add(p.table, 'scroll', this._onTableScrolledHorizontally.bind(this));
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
return this;
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
/**
|
|
4017
|
+
* @private
|
|
4018
|
+
* @returns {boolean}
|
|
4019
|
+
*/
|
|
4020
|
+
_isTableRtl() {
|
|
4021
|
+
return getComputedStyle(this._p.table).direction === 'rtl';
|
|
4022
|
+
}
|
|
4023
|
+
|
|
4024
|
+
/**
|
|
4025
|
+
* @private
|
|
4026
|
+
* @param {Object} column column object
|
|
4027
|
+
* @returns {string}
|
|
4028
|
+
*/
|
|
4029
|
+
_serializeColumnWidth(column) {
|
|
4030
|
+
return column.widthMode === ColumnWidthMode.AUTO ? 'auto' :
|
|
4031
|
+
column.widthMode === ColumnWidthMode.RELATIVE ? column.width * 100 + '%' :
|
|
4032
|
+
column.width;
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
/**
|
|
4036
|
+
* @private
|
|
4037
|
+
* @param {HTMLElement} el
|
|
4038
|
+
*/
|
|
4039
|
+
_disableCssSelect(el) {
|
|
4040
|
+
const style = el.style;
|
|
4041
|
+
// Disable these to allow our own context menu events without interruption
|
|
4042
|
+
style['-webkit-touch-callout'] = 'none';
|
|
4043
|
+
style['-webkit-user-select'] = 'none';
|
|
4044
|
+
style['-moz-user-select'] = 'none';
|
|
4045
|
+
style['-ms-user-select'] = 'none';
|
|
4046
|
+
style['-o-user-select'] = 'none';
|
|
4047
|
+
style['user-select'] = 'none';
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
/**
|
|
4051
|
+
* @private
|
|
4052
|
+
* @param {HTMLElement} el
|
|
4053
|
+
*/
|
|
4054
|
+
_cellMouseOverEvent(el) {
|
|
4055
|
+
const o = this._o,p = this._p;
|
|
4056
|
+
|
|
4057
|
+
let elInner = el.firstElementChild;
|
|
4058
|
+
|
|
4059
|
+
if (!(elInner.scrollWidth - elInner.clientWidth > 1 ||
|
|
4060
|
+
elInner.scrollHeight - elInner.clientHeight > 1))
|
|
4061
|
+
return;
|
|
4062
|
+
|
|
4063
|
+
this.hideCellPreview();
|
|
4064
|
+
p.abortCellPreview = false;
|
|
4065
|
+
|
|
4066
|
+
const rowEl = el.parentElement;
|
|
4067
|
+
const previewCell = createElement('div');
|
|
4068
|
+
previewCell.innerHTML = el.innerHTML;
|
|
4069
|
+
previewCell.className = o.cellPreviewClassName;
|
|
4070
|
+
|
|
4071
|
+
let isHeaderCell = el.classList.contains(`${o.tableClassName}-header-cell`);
|
|
4072
|
+
if (isHeaderCell) {
|
|
4073
|
+
previewCell.classList.add('header');
|
|
4074
|
+
if (el.classList.contains('sortable')) {
|
|
4075
|
+
previewCell.classList.add('sortable');
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
previewCell.draggable = true;
|
|
4079
|
+
|
|
4080
|
+
this._bindHeaderColumnEvents(previewCell);
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
const elStyle = getComputedStyle(el);
|
|
4084
|
+
const elInnerStyle = getComputedStyle(elInner);
|
|
4085
|
+
|
|
4086
|
+
let paddingL = parseFloat(elStyle.paddingLeft) || 0,
|
|
4087
|
+
paddingR = parseFloat(elStyle.paddingRight) || 0,
|
|
4088
|
+
paddingT = parseFloat(elStyle.paddingTop) || 0,
|
|
4089
|
+
paddingB = parseFloat(elStyle.paddingBottom) || 0;
|
|
4090
|
+
|
|
4091
|
+
let requiredWidth = elInner.scrollWidth + (el.clientWidth - elInner.offsetWidth);
|
|
4092
|
+
|
|
4093
|
+
let borderBox = elStyle.boxSizing === 'border-box';
|
|
4094
|
+
if (borderBox) {
|
|
4095
|
+
previewCell.style.boxSizing = 'border-box';
|
|
4096
|
+
} else {
|
|
4097
|
+
requiredWidth -= paddingL + paddingR;
|
|
4098
|
+
previewCell.style.marginTop = (parseFloat(elStyle.borderTopWidth) || 0) + 'px';
|
|
4099
|
+
}
|
|
4100
|
+
|
|
4101
|
+
if (!p.transparentBgColor1) {
|
|
4102
|
+
// Detect browser's transparent spec
|
|
4103
|
+
let tempDiv = document.createElement('div');
|
|
4104
|
+
document.body.appendChild(tempDiv);
|
|
4105
|
+
tempDiv.style.backgroundColor = 'transparent';
|
|
4106
|
+
p.transparentBgColor1 = getComputedStyle(tempDiv).backgroundColor;
|
|
4107
|
+
tempDiv.style.backgroundColor = 'rgba(0,0,0,0)';
|
|
4108
|
+
p.transparentBgColor2 = getComputedStyle(tempDiv).backgroundColor;
|
|
4109
|
+
tempDiv.remove();
|
|
4110
|
+
}
|
|
4111
|
+
|
|
4112
|
+
let css = {
|
|
4113
|
+
'box-sizing': borderBox ? 'border-box' : 'content-box',
|
|
4114
|
+
'width': requiredWidth,
|
|
4115
|
+
'min-height': Math.max(Css_js.getElementHeight(el), parseFloat(elStyle.minHeight) || 0) + 'px',
|
|
4116
|
+
'padding-left': paddingL,
|
|
4117
|
+
'padding-right': paddingR,
|
|
4118
|
+
'padding-top': paddingT,
|
|
4119
|
+
'padding-bottom': paddingB,
|
|
4120
|
+
'overflow': 'hidden',
|
|
4121
|
+
'position': 'absolute',
|
|
4122
|
+
'z-index': '-1',
|
|
4123
|
+
'left': '0',
|
|
4124
|
+
'top': '0',
|
|
4125
|
+
'cursor': elStyle.cursor
|
|
4126
|
+
};
|
|
4127
|
+
|
|
4128
|
+
let bgColor = elStyle.backgroundColor;
|
|
4129
|
+
if (bgColor === p.transparentBgColor1 || bgColor === p.transparentBgColor2) {
|
|
4130
|
+
bgColor = getComputedStyle(rowEl).backgroundColor;
|
|
4131
|
+
}
|
|
4132
|
+
if (bgColor === p.transparentBgColor1 || bgColor === p.transparentBgColor2) {
|
|
4133
|
+
bgColor = '#fff';
|
|
4134
|
+
}
|
|
4135
|
+
css['background-color'] = bgColor;
|
|
4136
|
+
|
|
4137
|
+
Css_js.setCssProps(previewCell, css);
|
|
4138
|
+
Css_js.setCssProps(previewCell.firstChild, {
|
|
4139
|
+
'direction': elInnerStyle.direction,
|
|
4140
|
+
'white-space': elInnerStyle.whiteSpace,
|
|
4141
|
+
'min-height': elInnerStyle.minHeight,
|
|
4142
|
+
'line-height': elInnerStyle.lineHeight,
|
|
4143
|
+
'font': elInnerStyle.font
|
|
4144
|
+
});
|
|
4145
|
+
|
|
4146
|
+
this.el.appendChild(previewCell);
|
|
4147
|
+
|
|
4148
|
+
if (isHeaderCell) {
|
|
4149
|
+
this._disableCssSelect(previewCell);
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4152
|
+
previewCell['rowVIndex'] = rowEl['vIndex'];
|
|
4153
|
+
let rowIndex = previewCell['rowIndex'] = rowEl['index'];
|
|
4154
|
+
previewCell['columnName'] = p.visibleColumns[nativeIndexOf.call(rowEl.childNodes, el)].name;
|
|
4155
|
+
|
|
4156
|
+
try {
|
|
4157
|
+
let selection = SelectionHelper.saveSelection(el);
|
|
4158
|
+
if (selection)
|
|
4159
|
+
SelectionHelper.restoreSelection(previewCell, selection);
|
|
4160
|
+
} catch (ignored) {/* we're ok with this */}
|
|
4161
|
+
|
|
4162
|
+
this.emit(
|
|
4163
|
+
'cellpreview', {
|
|
4164
|
+
el: previewCell.firstElementChild,
|
|
4165
|
+
name: previewCell['columnName'],
|
|
4166
|
+
rowIndex: rowIndex,
|
|
4167
|
+
rowData: rowIndex == null ? null : p.rows[rowIndex],
|
|
4168
|
+
cell: el,
|
|
4169
|
+
cellEl: elInner
|
|
4170
|
+
}
|
|
4171
|
+
);
|
|
4172
|
+
|
|
4173
|
+
if (p.abortCellPreview) {
|
|
4174
|
+
previewCell.remove();
|
|
4175
|
+
return;
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
if (rowIndex != null) {
|
|
4179
|
+
previewCell.addEventListener('click', (event) => {
|
|
4180
|
+
this.emit('rowclick', {
|
|
4181
|
+
event: event,
|
|
4182
|
+
filteredRowIndex: rowEl['vIndex'],
|
|
4183
|
+
rowIndex: rowIndex,
|
|
4184
|
+
rowEl: rowEl,
|
|
4185
|
+
rowData: p.rows[rowIndex]
|
|
4186
|
+
});
|
|
4187
|
+
});
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
let parent = this.el;
|
|
4191
|
+
let scrollParent = parent === window ? document : parent;
|
|
4192
|
+
|
|
4193
|
+
const parentStyle = getComputedStyle(parent);
|
|
4194
|
+
|
|
4195
|
+
let offset = Css_js.getElementOffset(el);
|
|
4196
|
+
let parentOffset = Css_js.getElementOffset(parent);
|
|
4197
|
+
let rtl = elStyle.float === 'right';
|
|
4198
|
+
let prop = rtl ? 'right' : 'left';
|
|
4199
|
+
|
|
4200
|
+
// Handle RTL, go from the other side
|
|
4201
|
+
if (rtl) {
|
|
4202
|
+
let windowWidth = window.innerWidth;
|
|
4203
|
+
offset.right = windowWidth - (offset.left + Css_js.getElementWidth(el, true, true, true));
|
|
4204
|
+
parentOffset.right = windowWidth - (parentOffset.left + Css_js.getElementWidth(parent, true, true, true));
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4207
|
+
// If the parent has borders, then it would offset the offset...
|
|
4208
|
+
offset.left -= parseFloat(parentStyle.borderLeftWidth) || 0;
|
|
4209
|
+
if (prop === 'right')
|
|
4210
|
+
offset.right -= parseFloat(parentStyle.borderRightWidth) || 0;
|
|
4211
|
+
offset.top -= parseFloat(parentStyle.borderTopWidth) || 0;
|
|
4212
|
+
|
|
4213
|
+
// Handle border widths of the element being offset
|
|
4214
|
+
offset[prop] += parseFloat(elStyle[`border-${prop}-width`]) || 0;
|
|
4215
|
+
offset.top += parseFloat(elStyle.borderTopWidth) || parseFloat(elStyle.borderBottomWidth) || 0;
|
|
4216
|
+
|
|
4217
|
+
// Subtract offsets to get offset relative to parent
|
|
4218
|
+
offset.left -= parentOffset.left;
|
|
4219
|
+
if (prop === 'right')
|
|
4220
|
+
offset.right -= parentOffset.right;
|
|
4221
|
+
offset.top -= parentOffset.top;
|
|
4222
|
+
|
|
4223
|
+
// Constrain horizontally
|
|
4224
|
+
let minHorz = 0,
|
|
4225
|
+
maxHorz = Css_js.getElementWidth(parent, false, false, false) - Css_js.getElementWidth(previewCell, true, true, true);
|
|
4226
|
+
offset[prop] = offset[prop] < minHorz ?
|
|
4227
|
+
minHorz :
|
|
4228
|
+
offset[prop] > maxHorz ? maxHorz : offset[prop];
|
|
4229
|
+
|
|
4230
|
+
// Constrain vertically
|
|
4231
|
+
let totalHeight = Css_js.getElementHeight(el, true, true, true);
|
|
4232
|
+
let maxTop = scrollParent.scrollTop + Css_js.getElementHeight(parent, true) - totalHeight;
|
|
4233
|
+
if (offset.top > maxTop) {
|
|
4234
|
+
offset.top = Math.max(0, maxTop);
|
|
4235
|
+
}
|
|
4236
|
+
|
|
4237
|
+
// Apply css to preview cell
|
|
4238
|
+
let previewCss = {
|
|
4239
|
+
'top': offset.top + 'px',
|
|
4240
|
+
'z-index': 9999
|
|
4241
|
+
};
|
|
4242
|
+
previewCss[prop] = offset[prop] + 'px';
|
|
4243
|
+
Css_js.setCssProps(previewCell, previewCss);
|
|
4244
|
+
|
|
4245
|
+
previewCell[OriginalCellSymbol] = el;
|
|
4246
|
+
p.cellPreviewCell = previewCell;
|
|
4247
|
+
el[PreviewCellSymbol] = previewCell;
|
|
4248
|
+
|
|
4249
|
+
p._bindCellHoverOut(el);
|
|
4250
|
+
p._bindCellHoverOut(previewCell);
|
|
4251
|
+
|
|
4252
|
+
// Avoid interfering with wheel scrolling the table
|
|
4253
|
+
previewCell.addEventListener('wheel', () => {
|
|
4254
|
+
// Let the table naturally scroll with the wheel
|
|
4255
|
+
this.hideCellPreview();
|
|
4256
|
+
});
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
/**
|
|
4260
|
+
* @private
|
|
4261
|
+
* @param {HTMLElement} _el
|
|
4262
|
+
*/
|
|
4263
|
+
_cellMouseOutEvent(_el) {
|
|
4264
|
+
this.hideCellPreview();
|
|
4265
|
+
}
|
|
4266
|
+
|
|
4267
|
+
/**
|
|
4268
|
+
* Hides the current cell preview,
|
|
4269
|
+
* or prevents the one that is currently trying to show (in the 'cellpreview' event)
|
|
4270
|
+
* @public
|
|
4271
|
+
* @expose
|
|
4272
|
+
* @returns {DGTable} self
|
|
4273
|
+
*/
|
|
4274
|
+
hideCellPreview() {
|
|
4275
|
+
const p = this._p;
|
|
4276
|
+
|
|
4277
|
+
if (p.cellPreviewCell) {
|
|
4278
|
+
let previewCell = p.cellPreviewCell;
|
|
4279
|
+
let origCell = previewCell[OriginalCellSymbol];
|
|
4280
|
+
let selection;
|
|
4281
|
+
|
|
4282
|
+
try {
|
|
4283
|
+
selection = SelectionHelper.saveSelection(previewCell);
|
|
4284
|
+
} catch (ignored) {/* we're ok with this */}
|
|
4285
|
+
|
|
4286
|
+
p.cellPreviewCell.remove();
|
|
4287
|
+
p._unbindCellHoverOut(origCell);
|
|
4288
|
+
p._unbindCellHoverOut(previewCell);
|
|
4289
|
+
|
|
4290
|
+
try {
|
|
4291
|
+
if (selection)
|
|
4292
|
+
SelectionHelper.restoreSelection(origCell, selection);
|
|
4293
|
+
} catch (ignored) {/* we're ok with this */}
|
|
4294
|
+
|
|
4295
|
+
this.emit('cellpreviewdestroy', {
|
|
4296
|
+
el: previewCell.firstChild,
|
|
4297
|
+
name: previewCell['columnName'],
|
|
4298
|
+
rowIndex: previewCell['rowIndex'],
|
|
4299
|
+
rowData: previewCell['rowIndex'] == null ? null : p.rows[previewCell['rowIndex']],
|
|
4300
|
+
cell: origCell,
|
|
4301
|
+
cellEl: origCell.firstChild
|
|
4302
|
+
});
|
|
4303
|
+
|
|
4304
|
+
delete origCell[PreviewCellSymbol];
|
|
4305
|
+
delete previewCell[OriginalCellSymbol];
|
|
4306
|
+
|
|
4307
|
+
p.cellPreviewCell = null;
|
|
4308
|
+
p.abortCellPreview = false;
|
|
4309
|
+
} else {
|
|
4310
|
+
p.abortCellPreview = true;
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
return this;
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
|
|
4317
|
+
/**
|
|
4318
|
+
* @public
|
|
4319
|
+
* @expose
|
|
4320
|
+
* @type {string}
|
|
4321
|
+
*/
|
|
4322
|
+
DGTable.VERSION = '@@VERSION';
|
|
4323
|
+
|
|
4324
|
+
// It's a shame the Google Closure Compiler does not support exposing a nested @param
|
|
4325
|
+
|
|
4326
|
+
/**
|
|
4327
|
+
* @typedef {Object} SERIALIZED_COLUMN
|
|
4328
|
+
* @property {number|null|undefined} [order=0]
|
|
4329
|
+
* @property {string|null|undefined} [width='auto']
|
|
4330
|
+
* @property {boolean|null|undefined} [visible=true]
|
|
4331
|
+
* */
|
|
4332
|
+
|
|
4333
|
+
/**
|
|
4334
|
+
* @typedef {Object} SERIALIZED_COLUMN_SORT
|
|
4335
|
+
* @property {string|null|undefined} [column='']
|
|
4336
|
+
* @property {boolean|null|undefined} [descending=false]
|
|
4337
|
+
* */
|
|
4338
|
+
|
|
4339
|
+
/**
|
|
4340
|
+
* @enum {ColumnWidthMode|number|undefined}
|
|
4341
|
+
* @const
|
|
4342
|
+
* @typedef {ColumnWidthMode}
|
|
4343
|
+
*/
|
|
4344
|
+
const ColumnWidthMode = {
|
|
4345
|
+
/** @const*/AUTO: 0,
|
|
4346
|
+
/** @const*/ABSOLUTE: 1,
|
|
4347
|
+
/** @const*/RELATIVE: 2
|
|
4348
|
+
};
|
|
4349
|
+
|
|
4350
|
+
/**
|
|
4351
|
+
* @enum {DGTable.Width|string|undefined}
|
|
4352
|
+
* @const
|
|
4353
|
+
* @typedef {DGTable.Width}
|
|
4354
|
+
*/
|
|
4355
|
+
DGTable.Width = {
|
|
4356
|
+
/** @const*/NONE: 'none',
|
|
4357
|
+
/** @const*/AUTO: 'auto',
|
|
4358
|
+
/** @const*/SCROLL: 'scroll'
|
|
4359
|
+
};
|
|
4360
|
+
|
|
4361
|
+
return DGTable;
|
|
4362
|
+
|
|
4363
|
+
}));
|
|
4364
|
+
|
|
4365
|
+
//# sourceMappingURL=lib.umd.js.map
|