@danielgindi/dgtable.js 2.0.7 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +547 -282
- package/dist/SelectionHelper.d.ts +24 -0
- package/dist/SelectionHelper.d.ts.map +1 -0
- package/dist/by_column_filter.d.ts +14 -0
- package/dist/by_column_filter.d.ts.map +1 -0
- package/dist/cell_preview.d.ts +28 -0
- package/dist/cell_preview.d.ts.map +1 -0
- package/dist/column_collection.d.ts +41 -0
- package/dist/column_collection.d.ts.map +1 -0
- package/dist/column_resize.d.ts +25 -0
- package/dist/column_resize.d.ts.map +1 -0
- package/dist/constants.d.ts +19 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/header_events.d.ts +63 -0
- package/dist/header_events.d.ts.map +1 -0
- package/dist/helpers.d.ts +50 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/internal.d.ts +56 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/lib.cjs.js +6909 -3929
- package/dist/lib.cjs.js.map +1 -1
- package/dist/lib.cjs.min.js +2 -2
- package/dist/lib.cjs.min.js.map +1 -1
- package/dist/lib.es6.js +6911 -3931
- package/dist/lib.es6.js.map +1 -1
- package/dist/lib.es6.min.js +2 -2
- package/dist/lib.es6.min.js.map +1 -1
- package/dist/lib.umd.js +9251 -4346
- package/dist/lib.umd.js.map +1 -1
- package/dist/lib.umd.min.js +2 -2
- package/dist/lib.umd.min.js.map +1 -1
- package/dist/private_types.d.ts +145 -0
- package/dist/private_types.d.ts.map +1 -0
- package/dist/rendering.d.ts +57 -0
- package/dist/rendering.d.ts.map +1 -0
- package/dist/row_collection.d.ts +38 -0
- package/dist/row_collection.d.ts.map +1 -0
- package/dist/types.d.ts +221 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/util.d.ts +9 -0
- package/dist/util.d.ts.map +1 -0
- package/eslint.config.mjs +1 -0
- package/package.json +17 -12
- package/src/SelectionHelper.ts +90 -0
- package/src/by_column_filter.ts +36 -0
- package/src/cell_preview.ts +325 -0
- package/src/column_collection.ts +131 -0
- package/src/column_resize.ts +363 -0
- package/src/constants.ts +22 -0
- package/src/header_events.ts +369 -0
- package/src/helpers.ts +291 -0
- package/src/index.ts +1628 -0
- package/src/internal.ts +263 -0
- package/src/private_types.ts +156 -0
- package/src/rendering.ts +771 -0
- package/src/row_collection.ts +197 -0
- package/src/types.ts +265 -0
- package/src/util.ts +27 -0
- package/tsconfig.json +38 -0
- package/src/SelectionHelper.js +0 -65
- package/src/by_column_filter.js +0 -25
- package/src/column_collection.js +0 -153
- package/src/index.js +0 -3991
- package/src/row_collection.js +0 -183
- package/src/util.js +0 -17
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header events functionality for DGTable
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { find } from './util';
|
|
6
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
7
|
+
// @ts-ignore - No type declarations available for this module
|
|
8
|
+
import { getElementWidth, getElementHeight, getElementOffset } from '@danielgindi/dom-utils/lib/Css.js';
|
|
9
|
+
import { isInputElementEvent } from './helpers';
|
|
10
|
+
import {
|
|
11
|
+
getColumnByResizePosition,
|
|
12
|
+
cancelColumnResize,
|
|
13
|
+
onMouseDownColumnHeader,
|
|
14
|
+
} from './column_resize';
|
|
15
|
+
import type { DGTableInterface } from './private_types';
|
|
16
|
+
import { RelatedTouchSymbol } from './private_types';
|
|
17
|
+
|
|
18
|
+
// Extended element types
|
|
19
|
+
interface HeaderCellElement extends HTMLElement {
|
|
20
|
+
columnName?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface DragData {
|
|
24
|
+
dragId: number;
|
|
25
|
+
column: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type PositionHost = {
|
|
29
|
+
pageX: number;
|
|
30
|
+
pageY: number;
|
|
31
|
+
clientX?: number;
|
|
32
|
+
identifier?: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type TouchOrMouseEvent = (MouseEvent | TouchEvent) & {
|
|
36
|
+
[RelatedTouchSymbol]?: PositionHost;
|
|
37
|
+
currentTarget: HTMLElement;
|
|
38
|
+
target: HTMLElement;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface TableWithSort extends DGTableInterface {
|
|
42
|
+
sort(column?: string, descending?: boolean, add?: boolean): this;
|
|
43
|
+
render(): this;
|
|
44
|
+
moveColumn(src: string, dest: string): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Handle touch start on column header
|
|
49
|
+
*/
|
|
50
|
+
export function onTouchStartColumnHeader(table: DGTableInterface, event: TouchEvent & { [RelatedTouchSymbol]?: PositionHost }): void {
|
|
51
|
+
const p = table._p;
|
|
52
|
+
|
|
53
|
+
if (p.currentTouchId) return;
|
|
54
|
+
|
|
55
|
+
const startTouch = event.changedTouches[0];
|
|
56
|
+
p.currentTouchId = startTouch.identifier;
|
|
57
|
+
|
|
58
|
+
const cellEl = event.currentTarget as HTMLElement;
|
|
59
|
+
|
|
60
|
+
const startPos = { x: startTouch.pageX, y: startTouch.pageY };
|
|
61
|
+
let currentPos = startPos;
|
|
62
|
+
const distanceTreshold = 9;
|
|
63
|
+
|
|
64
|
+
let tapAndHoldTimeout: ReturnType<typeof setTimeout>;
|
|
65
|
+
|
|
66
|
+
const unbind = function () {
|
|
67
|
+
p.currentTouchId = null;
|
|
68
|
+
p.eventsSink.remove(cellEl, '.colheader');
|
|
69
|
+
clearTimeout(tapAndHoldTimeout);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
(event as any)[RelatedTouchSymbol] = event.changedTouches[0];
|
|
73
|
+
onMouseDownColumnHeader(table, event as unknown as MouseEvent & { [RelatedTouchSymbol]?: PositionHost });
|
|
74
|
+
|
|
75
|
+
tapAndHoldTimeout = setTimeout(() => {
|
|
76
|
+
unbind();
|
|
77
|
+
|
|
78
|
+
p.eventsSink
|
|
79
|
+
.add(cellEl, 'touchend.colheader', (event: Event) => {
|
|
80
|
+
if (!isInputElementEvent(event))
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
|
|
83
|
+
p.eventsSink.remove(cellEl, '.colheader');
|
|
84
|
+
}, { once: true })
|
|
85
|
+
.add(cellEl, 'touchcancel.colheader', (_event: Event) => {
|
|
86
|
+
p.eventsSink.remove(cellEl, '.colheader');
|
|
87
|
+
}, { once: true });
|
|
88
|
+
|
|
89
|
+
const distanceTravelled = Math.sqrt(Math.pow(Math.abs(currentPos.x - startPos.x), 2) + Math.pow(Math.abs(currentPos.y - startPos.y), 2));
|
|
90
|
+
|
|
91
|
+
if (distanceTravelled < distanceTreshold) {
|
|
92
|
+
cancelColumnResize(table);
|
|
93
|
+
triggerColumnHeaderContextMenu(table, event);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}, 500);
|
|
97
|
+
|
|
98
|
+
p.eventsSink
|
|
99
|
+
.add(cellEl, 'touchend.colheader', (event: Event) => {
|
|
100
|
+
const touchEvent = event as TouchEvent & { [RelatedTouchSymbol]?: PositionHost };
|
|
101
|
+
const touch = find(Array.from(touchEvent.changedTouches), (t) => t.identifier === p.currentTouchId);
|
|
102
|
+
if (!touch) return;
|
|
103
|
+
|
|
104
|
+
unbind();
|
|
105
|
+
|
|
106
|
+
if (!isInputElementEvent(event))
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
|
|
109
|
+
currentPos = { x: touch.pageX, y: touch.pageY };
|
|
110
|
+
const distanceTravelled = Math.sqrt(Math.pow(Math.abs(currentPos.x - startPos.x), 2) + Math.pow(Math.abs(currentPos.y - startPos.y), 2));
|
|
111
|
+
|
|
112
|
+
if (distanceTravelled < distanceTreshold || p.resizer) {
|
|
113
|
+
(touchEvent as any)[RelatedTouchSymbol] = touch;
|
|
114
|
+
onSortOnColumnHeaderEvent(table, touchEvent);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
})
|
|
118
|
+
.add(cellEl, 'touchcancel.colheader', unbind)
|
|
119
|
+
.add(cellEl, 'touchmove.colheader', (event: Event) => {
|
|
120
|
+
const touchEvent = event as TouchEvent & { [RelatedTouchSymbol]?: PositionHost };
|
|
121
|
+
const touch = find(Array.from(touchEvent.changedTouches), (t) => t.identifier === p.currentTouchId);
|
|
122
|
+
if (!touch) return;
|
|
123
|
+
|
|
124
|
+
currentPos = { x: touch.pageX, y: touch.pageY };
|
|
125
|
+
|
|
126
|
+
if (p.resizer) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
|
|
129
|
+
(touchEvent as any)[RelatedTouchSymbol] = touch;
|
|
130
|
+
onMouseMoveColumnHeader(table, touchEvent);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Handle mouse move on column header
|
|
137
|
+
*/
|
|
138
|
+
export function onMouseMoveColumnHeader(table: DGTableInterface, event: Event): void {
|
|
139
|
+
const o = table._o;
|
|
140
|
+
const p = table._p;
|
|
141
|
+
|
|
142
|
+
if (!o.resizableColumns)
|
|
143
|
+
return;
|
|
144
|
+
|
|
145
|
+
const col = getColumnByResizePosition(table, event as MouseEvent);
|
|
146
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HTMLElement;
|
|
147
|
+
if (!headerCell) return;
|
|
148
|
+
|
|
149
|
+
if (!col || !p.columns.get(col)?.resizable) {
|
|
150
|
+
headerCell.style.cursor = '';
|
|
151
|
+
} else {
|
|
152
|
+
headerCell.style.cursor = 'e-resize';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Handle mouse up on column header
|
|
158
|
+
*/
|
|
159
|
+
export function onMouseUpColumnHeader(table: DGTableInterface, event: Event): void {
|
|
160
|
+
if ((event as MouseEvent).button !== 2)
|
|
161
|
+
return;
|
|
162
|
+
|
|
163
|
+
triggerColumnHeaderContextMenu(table, event);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Trigger context menu on column header
|
|
168
|
+
*/
|
|
169
|
+
export function triggerColumnHeaderContextMenu(table: DGTableInterface, event: Event): void {
|
|
170
|
+
const o = table._o;
|
|
171
|
+
|
|
172
|
+
const touchEvent = event as TouchEvent;
|
|
173
|
+
const mouseEvent = event as MouseEvent;
|
|
174
|
+
const positionHost: PositionHost = (event as TouchOrMouseEvent)[RelatedTouchSymbol] ?? touchEvent.changedTouches?.[0] ?? { pageX: mouseEvent.pageX, pageY: mouseEvent.pageY };
|
|
175
|
+
|
|
176
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HeaderCellElement;
|
|
177
|
+
if (!headerCell) return;
|
|
178
|
+
|
|
179
|
+
const bounds = getElementOffset(headerCell) as { left: number; top: number; width?: number; height?: number };
|
|
180
|
+
bounds.width = getElementWidth(headerCell, true, true, true);
|
|
181
|
+
bounds.height = getElementHeight(headerCell, true, true, true);
|
|
182
|
+
table.emit('headercontextmenu', {
|
|
183
|
+
columnName: headerCell.columnName,
|
|
184
|
+
pageX: positionHost.pageX,
|
|
185
|
+
pageY: positionHost.pageY,
|
|
186
|
+
bounds: bounds,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Handle mouse leave on column header
|
|
192
|
+
*/
|
|
193
|
+
export function onMouseLeaveColumnHeader(table: DGTableInterface, event: MouseEvent): void {
|
|
194
|
+
const o = table._o;
|
|
195
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HTMLElement;
|
|
196
|
+
if (headerCell) {
|
|
197
|
+
headerCell.style.cursor = '';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Handle sort click on column header
|
|
203
|
+
*/
|
|
204
|
+
export function onSortOnColumnHeaderEvent(table: DGTableInterface, event: Event): void {
|
|
205
|
+
if (isInputElementEvent(event))
|
|
206
|
+
return;
|
|
207
|
+
|
|
208
|
+
if (getColumnByResizePosition(table, event as MouseEvent))
|
|
209
|
+
return;
|
|
210
|
+
|
|
211
|
+
const o = table._o;
|
|
212
|
+
const p = table._p;
|
|
213
|
+
|
|
214
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HeaderCellElement;
|
|
215
|
+
if (!headerCell) return;
|
|
216
|
+
|
|
217
|
+
if (!o.sortableColumns)
|
|
218
|
+
return;
|
|
219
|
+
|
|
220
|
+
const column = p.columns.get(headerCell.columnName!);
|
|
221
|
+
const currentSort = p.rows.sortColumn;
|
|
222
|
+
if (column && column.sortable) {
|
|
223
|
+
let shouldAdd = true;
|
|
224
|
+
|
|
225
|
+
const lastSort = currentSort.length ? currentSort[currentSort.length - 1] : null;
|
|
226
|
+
|
|
227
|
+
if (lastSort && lastSort.column === column.name) {
|
|
228
|
+
if (!lastSort.descending || !o.allowCancelSort) {
|
|
229
|
+
lastSort.descending = !lastSort.descending;
|
|
230
|
+
} else {
|
|
231
|
+
shouldAdd = false;
|
|
232
|
+
currentSort.splice(currentSort.length - 1, 1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const tableWithSort = table as unknown as TableWithSort;
|
|
237
|
+
if (shouldAdd) {
|
|
238
|
+
tableWithSort.sort(column.name, undefined, true).render();
|
|
239
|
+
} else {
|
|
240
|
+
tableWithSort.sort(); // just refresh current situation
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Handle drag start on column header
|
|
247
|
+
*/
|
|
248
|
+
export function onStartDragColumnHeader(table: DGTableInterface, event: DragEvent): void {
|
|
249
|
+
const o = table._o;
|
|
250
|
+
const p = table._p;
|
|
251
|
+
|
|
252
|
+
if (o.movableColumns) {
|
|
253
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HeaderCellElement;
|
|
254
|
+
if (!headerCell) {
|
|
255
|
+
event.preventDefault();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const column = p.columns.get(headerCell.columnName!);
|
|
260
|
+
if (column && column.movable) {
|
|
261
|
+
headerCell.style.opacity = '0.35';
|
|
262
|
+
p.dragId = Math.random() * 0x9999999;
|
|
263
|
+
event.dataTransfer?.setData('text', JSON.stringify({ dragId: p.dragId, column: column.name }));
|
|
264
|
+
} else {
|
|
265
|
+
event.preventDefault();
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Handle drag end on column header
|
|
274
|
+
*/
|
|
275
|
+
export function onDragEndColumnHeader(table: DGTableInterface, event: DragEvent): void {
|
|
276
|
+
const p = table._p;
|
|
277
|
+
|
|
278
|
+
if (!p.resizer) {
|
|
279
|
+
(event.target as HTMLElement).style.opacity = '';
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Handle drag enter on column header
|
|
285
|
+
*/
|
|
286
|
+
export function onDragEnterColumnHeader(table: DGTableInterface, event: DragEvent): void {
|
|
287
|
+
const o = table._o;
|
|
288
|
+
const p = table._p;
|
|
289
|
+
|
|
290
|
+
if (o.movableColumns) {
|
|
291
|
+
let dataTransferred: DragData | null = null;
|
|
292
|
+
const dataStr = event.dataTransfer?.getData('text');
|
|
293
|
+
if (dataStr) {
|
|
294
|
+
try {
|
|
295
|
+
dataTransferred = JSON.parse(dataStr);
|
|
296
|
+
} catch {
|
|
297
|
+
dataTransferred = null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HeaderCellElement;
|
|
302
|
+
if (!headerCell) return;
|
|
303
|
+
|
|
304
|
+
if (!dataTransferred ||
|
|
305
|
+
(p.dragId === dataTransferred.dragId && headerCell.columnName !== dataTransferred.column)) {
|
|
306
|
+
|
|
307
|
+
const column = p.columns.get(headerCell.columnName!);
|
|
308
|
+
if (column && (column.movable || column !== p.visibleColumns[0])) {
|
|
309
|
+
headerCell.classList.add('drag-over');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Handle drag over on column header
|
|
317
|
+
*/
|
|
318
|
+
export function onDragOverColumnHeader(_table: DGTableInterface, event: DragEvent): void {
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Handle drag leave on column header
|
|
324
|
+
*/
|
|
325
|
+
export function onDragLeaveColumnHeader(table: DGTableInterface, event: DragEvent): void {
|
|
326
|
+
const o = table._o;
|
|
327
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HTMLElement;
|
|
328
|
+
if (!headerCell) return;
|
|
329
|
+
|
|
330
|
+
const relatedTarget = event.relatedTarget as HTMLElement;
|
|
331
|
+
if (!relatedTarget?.contains(headerCell.firstChild)) {
|
|
332
|
+
headerCell.classList.remove('drag-over');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Handle drop on column header
|
|
338
|
+
*/
|
|
339
|
+
export function onDropColumnHeader(table: DGTableInterface, event: DragEvent): void {
|
|
340
|
+
event.preventDefault();
|
|
341
|
+
|
|
342
|
+
const o = table._o;
|
|
343
|
+
const p = table._p;
|
|
344
|
+
|
|
345
|
+
const dataStr = event.dataTransfer?.getData('text');
|
|
346
|
+
if (!dataStr) return;
|
|
347
|
+
|
|
348
|
+
let dataTransferred: DragData;
|
|
349
|
+
try {
|
|
350
|
+
dataTransferred = JSON.parse(dataStr);
|
|
351
|
+
} catch {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const headerCell = (event.target as HTMLElement).closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`) as HeaderCellElement;
|
|
356
|
+
if (!headerCell) return;
|
|
357
|
+
|
|
358
|
+
if (o.movableColumns && dataTransferred.dragId === p.dragId) {
|
|
359
|
+
const srcColName = dataTransferred.column;
|
|
360
|
+
const destColName = headerCell.columnName!;
|
|
361
|
+
const srcCol = p.columns.get(srcColName);
|
|
362
|
+
const destCol = p.columns.get(destColName);
|
|
363
|
+
if (srcCol && destCol && srcCol.movable && (destCol.movable || destCol !== p.visibleColumns[0])) {
|
|
364
|
+
(table as unknown as TableWithSort).moveColumn(srcColName, destColName);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
headerCell.classList.remove('drag-over');
|
|
368
|
+
}
|
|
369
|
+
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for DGTable
|
|
3
|
+
* These are extracted to keep the main class focused on public API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
7
|
+
// @ts-ignore - No type declarations available for this module
|
|
8
|
+
import { getElementWidth, setCssProps } from '@danielgindi/dom-utils/lib/Css.js';
|
|
9
|
+
|
|
10
|
+
import { ColumnWidthMode } from './constants';
|
|
11
|
+
import type { InternalColumn, DGTableInterface } from './private_types';
|
|
12
|
+
|
|
13
|
+
const createElement = document.createElement.bind(document);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* BUGFIX: WebKit has a bug where it does not relayout
|
|
17
|
+
*/
|
|
18
|
+
export function webkitRenderBugfix(el: HTMLElement): HTMLElement {
|
|
19
|
+
const oldDisplay = el.style.display;
|
|
20
|
+
el.style.display = 'none';
|
|
21
|
+
// No need to store this anywhere, the reference is enough
|
|
22
|
+
void el.offsetHeight;
|
|
23
|
+
el.style.display = oldDisplay;
|
|
24
|
+
return el;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Make element relative if not already positioned
|
|
29
|
+
*/
|
|
30
|
+
export function relativizeElement(el: HTMLElement): void {
|
|
31
|
+
if (!['relative', 'absolute', 'fixed'].includes(getComputedStyle(el).position)) {
|
|
32
|
+
el.style.position = 'relative';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if event target is an input element
|
|
38
|
+
*/
|
|
39
|
+
export function isInputElementEvent(event: Event): boolean {
|
|
40
|
+
const target = event.target as HTMLElement;
|
|
41
|
+
return /^(?:INPUT|TEXTAREA|BUTTON|SELECT)$/.test(target.tagName);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Calculate horizontal padding of an element
|
|
46
|
+
*/
|
|
47
|
+
export function horizontalPadding(el: Element): number {
|
|
48
|
+
const style = getComputedStyle(el);
|
|
49
|
+
return ((parseFloat(style.paddingLeft) || 0) +
|
|
50
|
+
(parseFloat(style.paddingRight) || 0));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Calculate horizontal border width of an element
|
|
55
|
+
*/
|
|
56
|
+
export function horizontalBorderWidth(el: Element): number {
|
|
57
|
+
const style = getComputedStyle(el);
|
|
58
|
+
return ((parseFloat(style.borderLeftWidth) || 0) +
|
|
59
|
+
(parseFloat(style.borderRightWidth) || 0));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Disable CSS text selection on an element
|
|
64
|
+
*/
|
|
65
|
+
export function disableCssSelect(el: HTMLElement): void {
|
|
66
|
+
const style = el.style as CSSStyleDeclaration & Record<string, string>;
|
|
67
|
+
style['-webkit-touch-callout'] = 'none';
|
|
68
|
+
style['-webkit-user-select'] = 'none';
|
|
69
|
+
style['-moz-user-select'] = 'none';
|
|
70
|
+
style['-ms-user-select'] = 'none';
|
|
71
|
+
style['-o-user-select'] = 'none';
|
|
72
|
+
style['user-select'] = 'none';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get text width by measuring in a temporary element
|
|
77
|
+
*/
|
|
78
|
+
export function getTextWidth(table: DGTableInterface, text: string): number {
|
|
79
|
+
const tableClassName = table._o.tableClassName;
|
|
80
|
+
|
|
81
|
+
const tableWrapper = createElement('div');
|
|
82
|
+
tableWrapper.className = table.el.className;
|
|
83
|
+
const header = createElement('div');
|
|
84
|
+
header.className = tableClassName + '-header';
|
|
85
|
+
const headerRow = createElement('div');
|
|
86
|
+
headerRow.className = tableClassName + '-header-row';
|
|
87
|
+
const cell = createElement('div');
|
|
88
|
+
cell.className = tableClassName + '-header-cell';
|
|
89
|
+
const cellContent = createElement('div');
|
|
90
|
+
cellContent.textContent = text;
|
|
91
|
+
|
|
92
|
+
cell.appendChild(cellContent);
|
|
93
|
+
headerRow.appendChild(cell);
|
|
94
|
+
header.appendChild(headerRow);
|
|
95
|
+
tableWrapper.appendChild(header);
|
|
96
|
+
setCssProps(tableWrapper, {
|
|
97
|
+
position: 'absolute',
|
|
98
|
+
top: '-9999px',
|
|
99
|
+
visibility: 'hidden',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
document.body.appendChild(tableWrapper);
|
|
103
|
+
|
|
104
|
+
const width = getElementWidth(cell);
|
|
105
|
+
|
|
106
|
+
tableWrapper.remove();
|
|
107
|
+
|
|
108
|
+
return width;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Calculate width available for columns
|
|
113
|
+
*/
|
|
114
|
+
export function calculateWidthAvailableForColumns(table: DGTableInterface): number {
|
|
115
|
+
const o = table._o, p = table._p;
|
|
116
|
+
|
|
117
|
+
// Changing display mode briefly, to prevent taking in account the parent's scrollbar width when we are the cause for it
|
|
118
|
+
let oldDisplay: string | undefined;
|
|
119
|
+
let lastScrollTop = 0;
|
|
120
|
+
let lastScrollLeft = 0;
|
|
121
|
+
if (p.table) {
|
|
122
|
+
lastScrollTop = p.table ? p.table.scrollTop : 0;
|
|
123
|
+
lastScrollLeft = p.table ? p.table.scrollLeft : 0;
|
|
124
|
+
|
|
125
|
+
if (o.virtualTable) {
|
|
126
|
+
oldDisplay = p.table.style.display;
|
|
127
|
+
p.table.style.display = 'none';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let detectedWidth = getElementWidth(table.el);
|
|
132
|
+
|
|
133
|
+
if (p.table) {
|
|
134
|
+
if (o.virtualTable && oldDisplay !== undefined) {
|
|
135
|
+
p.table.style.display = oldDisplay;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
p.table.scrollTop = lastScrollTop;
|
|
139
|
+
p.table.scrollLeft = lastScrollLeft;
|
|
140
|
+
if (p.header) {
|
|
141
|
+
p.header.scrollLeft = lastScrollLeft;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const tableClassName = o.tableClassName;
|
|
146
|
+
|
|
147
|
+
const thisWrapper = createElement('div');
|
|
148
|
+
thisWrapper.className = table.el.className;
|
|
149
|
+
setCssProps(thisWrapper, {
|
|
150
|
+
'z-index': '-1',
|
|
151
|
+
'position': 'absolute',
|
|
152
|
+
left: '0',
|
|
153
|
+
top: '-9999px',
|
|
154
|
+
});
|
|
155
|
+
const header = createElement('div');
|
|
156
|
+
header.className = `${tableClassName}-header`;
|
|
157
|
+
thisWrapper.appendChild(header);
|
|
158
|
+
const headerRow = createElement('div') as HTMLDivElement & { index: null; vIndex: null };
|
|
159
|
+
headerRow.index = null;
|
|
160
|
+
headerRow.vIndex = null;
|
|
161
|
+
headerRow.className = `${tableClassName}-header-row`;
|
|
162
|
+
header.appendChild(headerRow);
|
|
163
|
+
for (let i = 0; i < p.visibleColumns.length; i++) {
|
|
164
|
+
const column = p.visibleColumns[i];
|
|
165
|
+
const cell = createElement('div') as HTMLDivElement & { columnName: string };
|
|
166
|
+
cell.className = `${tableClassName}-header-cell ${column.cellClasses || ''}`;
|
|
167
|
+
cell.columnName = column.name;
|
|
168
|
+
cell.appendChild(createElement('div'));
|
|
169
|
+
headerRow.appendChild(cell);
|
|
170
|
+
}
|
|
171
|
+
document.body.appendChild(thisWrapper);
|
|
172
|
+
|
|
173
|
+
detectedWidth -= horizontalBorderWidth(headerRow);
|
|
174
|
+
|
|
175
|
+
const cells = headerRow.querySelectorAll(`div.${tableClassName}-header-cell`) as NodeListOf<HTMLDivElement & { columnName: string }>;
|
|
176
|
+
for (const cell of cells) {
|
|
177
|
+
const cellStyle = getComputedStyle(cell);
|
|
178
|
+
const isBoxing = cellStyle.boxSizing === 'border-box';
|
|
179
|
+
if (!isBoxing) {
|
|
180
|
+
detectedWidth -=
|
|
181
|
+
(parseFloat(cellStyle.borderRightWidth) || 0) +
|
|
182
|
+
(parseFloat(cellStyle.borderLeftWidth) || 0) +
|
|
183
|
+
(horizontalPadding(cell)); // CELL's padding
|
|
184
|
+
|
|
185
|
+
const colName = cell.columnName;
|
|
186
|
+
const column = p.columns.get(colName);
|
|
187
|
+
if (column)
|
|
188
|
+
detectedWidth -= column.arrowProposedWidth || 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
thisWrapper.remove();
|
|
193
|
+
|
|
194
|
+
return Math.max(0, detectedWidth);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Calculate the size required for the table body width
|
|
199
|
+
*/
|
|
200
|
+
export function calculateTbodyWidth(table: DGTableInterface): number {
|
|
201
|
+
const p = table._p;
|
|
202
|
+
const o = table._o;
|
|
203
|
+
|
|
204
|
+
const tableClassName = o.tableClassName;
|
|
205
|
+
const rowClassName = tableClassName + '-row';
|
|
206
|
+
const cellClassName = tableClassName + '-cell';
|
|
207
|
+
const visibleColumns = p.visibleColumns;
|
|
208
|
+
const colCount = visibleColumns.length;
|
|
209
|
+
|
|
210
|
+
const row = createElement('div');
|
|
211
|
+
row.className = rowClassName;
|
|
212
|
+
row.style.float = 'left';
|
|
213
|
+
|
|
214
|
+
let sumActualWidth = 0;
|
|
215
|
+
|
|
216
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
217
|
+
const column = visibleColumns[colIndex];
|
|
218
|
+
const cell = createElement('div');
|
|
219
|
+
cell.className = cellClassName;
|
|
220
|
+
cell.style.width = (column.actualWidth ?? 0) + 'px';
|
|
221
|
+
if (column.cellClasses) cell.className += ' ' + column.cellClasses;
|
|
222
|
+
cell.appendChild(createElement('div'));
|
|
223
|
+
row.appendChild(cell);
|
|
224
|
+
sumActualWidth += column.actualWidth ?? 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const thisWrapper = createElement('div');
|
|
228
|
+
thisWrapper.className = table.el.className;
|
|
229
|
+
setCssProps(thisWrapper, {
|
|
230
|
+
'z-index': '-1',
|
|
231
|
+
'position': 'absolute',
|
|
232
|
+
'left': '0',
|
|
233
|
+
'top': '-9999px',
|
|
234
|
+
'float': 'left',
|
|
235
|
+
'width': '1px',
|
|
236
|
+
'overflow': 'hidden',
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const tableDiv = createElement('div');
|
|
240
|
+
tableDiv.className = tableClassName;
|
|
241
|
+
thisWrapper.appendChild(tableDiv);
|
|
242
|
+
const tableBodyDiv = createElement('div');
|
|
243
|
+
tableBodyDiv.className = tableClassName + '-body';
|
|
244
|
+
tableBodyDiv.style.width = (sumActualWidth + 10000) + 'px';
|
|
245
|
+
tableDiv.appendChild(tableBodyDiv);
|
|
246
|
+
tableBodyDiv.appendChild(row);
|
|
247
|
+
|
|
248
|
+
document.body.appendChild(thisWrapper);
|
|
249
|
+
|
|
250
|
+
const fractionTest = createElement('div');
|
|
251
|
+
setCssProps(fractionTest, {
|
|
252
|
+
border: '1.5px solid #000',
|
|
253
|
+
width: '0',
|
|
254
|
+
height: '0',
|
|
255
|
+
position: 'absolute',
|
|
256
|
+
left: '0',
|
|
257
|
+
top: '-9999px',
|
|
258
|
+
});
|
|
259
|
+
document.body.appendChild(fractionTest);
|
|
260
|
+
const fractionValue = parseFloat(getComputedStyle(fractionTest).borderWidth);
|
|
261
|
+
const hasFractions = Math.round(fractionValue) !== fractionValue;
|
|
262
|
+
fractionTest.remove();
|
|
263
|
+
|
|
264
|
+
let width = getElementWidth(row, true, true, true);
|
|
265
|
+
width -= p.scrollbarWidth || 0;
|
|
266
|
+
|
|
267
|
+
if (hasFractions) {
|
|
268
|
+
width++;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
thisWrapper.remove();
|
|
272
|
+
return width;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Check if table is RTL
|
|
277
|
+
*/
|
|
278
|
+
export function isTableRtl(table: DGTableInterface): boolean {
|
|
279
|
+
const p = table._p;
|
|
280
|
+
return p.table ? getComputedStyle(p.table).direction === 'rtl' : false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Serialize column width to string
|
|
285
|
+
*/
|
|
286
|
+
export function serializeColumnWidth(column: InternalColumn): string | number {
|
|
287
|
+
return column.widthMode === ColumnWidthMode.AUTO ? 'auto' :
|
|
288
|
+
column.widthMode === ColumnWidthMode.RELATIVE ? column.width * 100 + '%' :
|
|
289
|
+
column.width;
|
|
290
|
+
}
|
|
291
|
+
|