canvas-datagrid 0.25.3 → 0.26.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.
@@ -0,0 +1,2640 @@
1
+ /*jslint browser: true, unparam: true, todo: true, plusplus: true*/
2
+ /*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false*/
3
+ 'use strict';
4
+
5
+ import isPrintableKeyEvent from 'is-printable-key-event';
6
+ import { isSupportedHtml, parseData, deduceColTypeFromData } from './util';
7
+
8
+ export default function (self) {
9
+ var wheeling;
10
+ self.stopPropagation = function (e) {
11
+ e.stopPropagation();
12
+ };
13
+ /**
14
+ * Adds an event listener to the given event.
15
+ * @memberof canvasDatagrid
16
+ * @name addEventListener
17
+ * @method
18
+ * @param {string} ev The name of the event to subscribe to.
19
+ * @param {function} fn The event procedure to execute when the event is raised.
20
+ */
21
+ self.addEventListener = function (ev, fn) {
22
+ self.events[ev] = self.events[ev] || [];
23
+ self.events[ev].unshift(fn);
24
+ };
25
+ /**
26
+ * Removes the given listener function from the given event. Must be an actual reference to the function that was bound.
27
+ * @memberof canvasDatagrid
28
+ * @name removeEventListener
29
+ * @method
30
+ * @param {string} ev The name of the event to unsubscribe from.
31
+ * @param {function} fn The event procedure to execute when the event is raised.
32
+ */
33
+ self.removeEventListener = function (ev, fn) {
34
+ (self.events[ev] || []).forEach(function removeEachListener(sfn, idx) {
35
+ if (fn === sfn) {
36
+ self.events[ev].splice(idx, 1);
37
+ }
38
+ });
39
+ };
40
+ /**
41
+ * Fires the given event, passing an event object to the event subscribers.
42
+ * @memberof canvasDatagrid
43
+ * @name dispatchEvent
44
+ * @method
45
+ * @param {number} ev The name of the event to dispatch.
46
+ * @param {number} e The event object.
47
+ */
48
+ self.dispatchEvent = function (ev, e) {
49
+ e = ev.type ? ev : e || {};
50
+ ev = ev.type || ev;
51
+ var defaultPrevented;
52
+ function preventDefault() {
53
+ defaultPrevented = true;
54
+ }
55
+ if (!self.events[ev]) {
56
+ return;
57
+ }
58
+ self.events[ev].forEach(function dispatchEachEvent(fn) {
59
+ e.ctx = self.ctx;
60
+ e.preventDefault = preventDefault;
61
+ fn.apply(self.intf, [e]);
62
+ });
63
+ return defaultPrevented;
64
+ };
65
+ self.getRatio = function () {
66
+ return Math.min(
67
+ self.attributes.maxPixelRatio,
68
+ (window.devicePixelRatio || 1) /
69
+ (self.ctx.webkitBackingStorePixelRatio ||
70
+ self.ctx.mozBackingStorePixelRatio ||
71
+ self.ctx.msBackingStorePixelRatio ||
72
+ self.ctx.oBackingStorePixelRatio ||
73
+ self.ctx.backingStorePixelRatio ||
74
+ 1),
75
+ );
76
+ };
77
+ /**
78
+ * @returns {number} dataWidth
79
+ */
80
+ self.refreshScrollCacheX = function () {
81
+ const s = self.getSchema();
82
+ self.scrollCache.x = [];
83
+
84
+ /** @type {number} it will be used in `reduceSchema` only */
85
+ let frozenWidth = 0;
86
+
87
+ const collapsedColumnGroups = self.getCollapsedColumnGroups();
88
+ const isColumnCollapsed = (columnIndex) =>
89
+ collapsedColumnGroups.findIndex(
90
+ (group) => columnIndex >= group.from && columnIndex <= group.to,
91
+ ) >= 0;
92
+
93
+ const dataWidth =
94
+ s.reduce(function reduceSchema(accumulator, column, columnIndex) {
95
+ // intentional redefintion of column. This causes scrollCache to be in the correct order
96
+ const schemaIndex = self.orders.columns[columnIndex];
97
+ const columnWidth = self.getColumnWidth(schemaIndex);
98
+ column = s[schemaIndex];
99
+ if (!column.hidden && !isColumnCollapsed(columnIndex))
100
+ accumulator += columnWidth;
101
+ if (columnIndex < self.frozenColumn) {
102
+ self.scrollCache.x[columnIndex] = accumulator;
103
+ frozenWidth = accumulator;
104
+ } else {
105
+ self.scrollCache.x[columnIndex] = Math.max(
106
+ frozenWidth + columnWidth,
107
+ accumulator,
108
+ );
109
+ }
110
+ return accumulator;
111
+ }, 0) || 0;
112
+ return dataWidth;
113
+ };
114
+ self.resize = function (drawAfterResize) {
115
+ if (!self.canvas) {
116
+ return;
117
+ }
118
+ var x,
119
+ v = {
120
+ x: 0,
121
+ y: 0,
122
+ height: 0,
123
+ width: 0,
124
+ style: 'vertical-scroll-bar',
125
+ },
126
+ n = {
127
+ x: 0,
128
+ y: 0,
129
+ height: 0,
130
+ width: 0,
131
+ style: 'horizontal-scroll-bar',
132
+ },
133
+ vb = {
134
+ x: 0,
135
+ y: 0,
136
+ height: 0,
137
+ width: 0,
138
+ style: 'vertical-scroll-box',
139
+ },
140
+ nb = {
141
+ x: 0,
142
+ y: 0,
143
+ height: 0,
144
+ width: 0,
145
+ style: 'horizontal-scroll-box',
146
+ },
147
+ co = {
148
+ x: 0,
149
+ y: 0,
150
+ height: 0,
151
+ width: 0,
152
+ isCorner: true,
153
+ isScrollBoxCorner: true,
154
+ style: 'scroll-box-corner',
155
+ },
156
+ m = self.style.scrollBarBoxMargin * 2,
157
+ b = self.style.scrollBarBorderWidth * 2,
158
+ d = self.style.scrollBarBoxMargin * 0.5,
159
+ sbw = self.style.scrollBarWidth + self.style.scrollBarBorderWidth * 2,
160
+ ratio = self.getRatio(),
161
+ bm = self.style.gridBorderCollapse === 'collapse' ? 1 : 2,
162
+ cellBorder = self.style.cellBorderWidth * bm,
163
+ columnHeaderCellBorder = self.style.columnHeaderCellBorderWidth * bm,
164
+ dataHeight = 0,
165
+ dataWidth = 0,
166
+ dims,
167
+ l = (self.viewData || []).length,
168
+ columnHeaderCellHeight = self.getColumnHeaderCellHeight(),
169
+ rowHeaderCellWidth = self.getRowHeaderCellWidth(),
170
+ topGroupAreaHeight = self.getColumnGroupAreaHeight(),
171
+ leftGroupAreaWidth = self.getRowGroupAreaWidth(),
172
+ ch = self.style.cellHeight;
173
+ // sets actual DOM canvas element
174
+ function checkScrollBoxVisibility() {
175
+ var capped = self.autoSizeCapped || {};
176
+ self.scrollBox.horizontalBarVisible =
177
+ ((self.style.width !== 'auto' || capped.width) &&
178
+ dataWidth > self.scrollBox.width &&
179
+ self.style.overflowX !== 'hidden') ||
180
+ self.style.overflowX === 'scroll';
181
+ self.scrollBox.horizontalBoxVisible = dataWidth > self.scrollBox.width;
182
+ self.scrollBox.verticalBarVisible =
183
+ ((self.style.height !== 'auto' || capped.height) &&
184
+ dataHeight > self.scrollBox.height &&
185
+ self.style.overflowY !== 'hidden') ||
186
+ self.style.overflowY === 'scroll';
187
+ self.scrollBox.verticalBoxVisible = dataHeight > self.scrollBox.height;
188
+ }
189
+ function setScrollBoxSize() {
190
+ self.scrollBox.width =
191
+ self.width - rowHeaderCellWidth - leftGroupAreaWidth;
192
+ self.scrollBox.height =
193
+ self.height - columnHeaderCellHeight - topGroupAreaHeight;
194
+ }
195
+ function setCanvasSize() {
196
+ if (self.isChildGrid) {
197
+ return;
198
+ }
199
+ dims = {
200
+ // HACK +1 ? maybe it's a magic cell border? Required to line up properly in auto height mode.
201
+ height:
202
+ columnHeaderCellHeight +
203
+ topGroupAreaHeight +
204
+ dataHeight +
205
+ cellBorder +
206
+ 1,
207
+ width: dataWidth + rowHeaderCellWidth + cellBorder + leftGroupAreaWidth,
208
+ };
209
+ // Browsers refuse to paint canvases beyond roughly 16k px per side, so
210
+ // an auto-sized grid with enough rows went blank past that point
211
+ // (#254). Cap the element and let it scroll for the remainder.
212
+ var maxCanvasSize = self.attributes.maxCanvasSize;
213
+ self.autoSizeCapped = { width: false, height: false };
214
+ ['width', 'height'].forEach(function (dim) {
215
+ if (dims[dim] > maxCanvasSize) {
216
+ dims[dim] = maxCanvasSize;
217
+ self.autoSizeCapped[dim] = true;
218
+ }
219
+ });
220
+ ['width', 'height'].forEach(function (dim) {
221
+ //TODO: support inherit
222
+ if (
223
+ ['auto', undefined].indexOf(self.style[dim]) !== -1 &&
224
+ ['auto', undefined].indexOf(self.appliedInlineStyles[dim]) !== -1
225
+ ) {
226
+ self.parentNodeStyle[dim] = dims[dim] + 'px';
227
+ } else if (
228
+ ['auto', undefined].indexOf(self.style[dim]) == -1 &&
229
+ ['auto', undefined].indexOf(self.appliedInlineStyles[dim]) == -1
230
+ ) {
231
+ self.parentNodeStyle[dim] = self.style[dim];
232
+ if (self.isComponent) {
233
+ self.canvas.style[dim] = self.style[dim];
234
+ }
235
+ }
236
+ });
237
+ }
238
+ self.autoLastColumnWidth = undefined;
239
+ dataWidth = self.refreshScrollCacheX();
240
+ self.scrollCache.y = [];
241
+ for (x = 0; x < l; x += 1) {
242
+ self.scrollCache.y[x] = dataHeight;
243
+ dataHeight +=
244
+ ((self.sizes.rows[x] || ch) + (self.sizes.trees[x] || 0)) * self.scale +
245
+ // HACK? if an expanded tree row is frozen it is necessary to add the tree row's height a second time.
246
+ (self.frozenRow > x ? self.sizes.trees[x] || 0 : 0);
247
+ }
248
+ if (l > 1) {
249
+ self.scrollCache.y[x] = dataHeight;
250
+ }
251
+ if (self.attributes.showNewRow) {
252
+ dataHeight += ch;
253
+ }
254
+ if (self.attributes.snapToRow) {
255
+ dataHeight += self.style.cellHeight;
256
+ }
257
+ setCanvasSize();
258
+ if (self.isChildGrid) {
259
+ self.width = self.parentNode.offsetWidth;
260
+ self.height = self.parentNode.offsetHeight;
261
+ } else if (
262
+ self.height !== self.canvas.offsetHeight ||
263
+ self.width !== self.canvas.offsetWidth
264
+ ) {
265
+ self.height = self.canvas.offsetHeight;
266
+ self.width = self.canvas.offsetWidth;
267
+ self.canvasOffsetLeft = self.args.canvasOffsetLeft || 0;
268
+ self.canvasOffsetTop = self.args.canvasOffsetTop || 0;
269
+ }
270
+ /// calculate scroll bar dimensions
271
+ // non-controversial
272
+ self.scrollBox.top =
273
+ columnHeaderCellHeight + topGroupAreaHeight + columnHeaderCellBorder;
274
+ self.scrollBox.left = rowHeaderCellWidth + leftGroupAreaWidth;
275
+ // width and height of scroll box
276
+ setScrollBoxSize();
277
+ // is the data larger than the scroll box
278
+ checkScrollBoxVisibility();
279
+ // if the scroll box is visible, make room for it by expanding the size of the element
280
+ // if the other dimension is set to auto
281
+ if (self.scrollBox.horizontalBarVisible) {
282
+ if (self.style.height === 'auto' && !self.isChildGrid) {
283
+ self.height += sbw;
284
+ }
285
+ dataHeight += sbw;
286
+ setCanvasSize();
287
+ setScrollBoxSize();
288
+ checkScrollBoxVisibility();
289
+ }
290
+ if (self.scrollBox.verticalBarVisible) {
291
+ if (self.style.width === 'auto' && !self.isChildGrid) {
292
+ self.width += sbw;
293
+ }
294
+ dataWidth += sbw;
295
+ setCanvasSize();
296
+ setScrollBoxSize();
297
+ checkScrollBoxVisibility();
298
+ }
299
+ // set again after bar visibility checks
300
+ setScrollBoxSize();
301
+ if (self.attributes.fillLastColumn) {
302
+ const lastColumnIndex = self.getLastVisibleColumnIndex();
303
+ const availableWidth =
304
+ self.scrollBox.width -
305
+ (self.scrollBox.verticalBarVisible ? sbw : 0) -
306
+ cellBorder;
307
+ if (lastColumnIndex >= 0 && dataWidth < availableWidth) {
308
+ self.autoLastColumnWidth = {
309
+ index: lastColumnIndex,
310
+ width:
311
+ self.getColumnWidth(lastColumnIndex) + availableWidth - dataWidth,
312
+ };
313
+ dataWidth = self.refreshScrollCacheX();
314
+ checkScrollBoxVisibility();
315
+ }
316
+ }
317
+ self.scrollBox.scrollWidth = dataWidth - self.scrollBox.width;
318
+ self.scrollBox.scrollHeight = dataHeight - self.scrollBox.height;
319
+ if (self.frozenColumn > 0) {
320
+ self.scrollBox.widthBoxRatio =
321
+ (self.scrollBox.width - self.scrollCache.x[self.frozenColumn - 1]) /
322
+ dataWidth;
323
+ } else {
324
+ self.scrollBox.widthBoxRatio = self.scrollBox.width / dataWidth;
325
+ }
326
+ self.scrollBox.scrollBoxWidth =
327
+ self.scrollBox.width * self.scrollBox.widthBoxRatio -
328
+ self.style.scrollBarWidth -
329
+ b -
330
+ d;
331
+ // TODO: This heightBoxRatio number is terribly wrong.
332
+ // They should be a result of the size of the grid/canvas?
333
+ // it being off causes the scroll bar to "slide" under
334
+ // the dragged mouse.
335
+ // https://github.com/TonyGermaneri/canvas-datagrid/issues/97
336
+ self.scrollBox.heightBoxRatio =
337
+ (self.scrollBox.height -
338
+ columnHeaderCellHeight -
339
+ topGroupAreaHeight -
340
+ self.scrollCache.y[self.frozenRow]) /
341
+ dataHeight;
342
+ self.scrollBox.scrollBoxHeight =
343
+ self.scrollBox.height * self.scrollBox.heightBoxRatio -
344
+ self.style.scrollBarWidth -
345
+ b -
346
+ d;
347
+ self.scrollBox.scrollBoxWidth = Math.max(
348
+ self.scrollBox.scrollBoxWidth,
349
+ self.style.scrollBarBoxMinSize,
350
+ );
351
+ self.scrollBox.scrollBoxHeight = Math.max(
352
+ self.scrollBox.scrollBoxHeight,
353
+ self.style.scrollBarBoxMinSize,
354
+ );
355
+ // horizontal
356
+ n.x += rowHeaderCellWidth;
357
+ n.y += self.height - self.style.scrollBarWidth - d - topGroupAreaHeight;
358
+ n.width =
359
+ self.width -
360
+ self.style.scrollBarWidth -
361
+ rowHeaderCellWidth -
362
+ leftGroupAreaWidth -
363
+ d -
364
+ m;
365
+ n.height = self.style.scrollBarWidth + self.style.scrollBarBorderWidth + d;
366
+ // horizontal box
367
+ nb.y = n.y + self.style.scrollBarBoxMargin;
368
+ nb.width = self.scrollBox.scrollBoxWidth;
369
+ nb.height = self.style.scrollBarBoxWidth;
370
+ // vertical
371
+ v.x +=
372
+ self.width -
373
+ leftGroupAreaWidth -
374
+ self.style.scrollBarWidth -
375
+ self.style.scrollBarBorderWidth -
376
+ d;
377
+ v.y += columnHeaderCellHeight + self.scrollCache.y[self.frozenRow];
378
+ v.width = self.style.scrollBarWidth + self.style.scrollBarBorderWidth + d;
379
+ v.height =
380
+ self.height -
381
+ columnHeaderCellHeight -
382
+ topGroupAreaHeight -
383
+ self.style.scrollBarWidth -
384
+ d -
385
+ m;
386
+ // vertical box
387
+ vb.x = v.x + self.style.scrollBarBoxMargin;
388
+ vb.y += self.scrollCache.y[self.frozenRow];
389
+ vb.width = self.style.scrollBarBoxWidth;
390
+ vb.height = self.scrollBox.scrollBoxHeight;
391
+ // corner
392
+ co.x = n.x + n.width + m;
393
+ co.y = v.y + v.height + m;
394
+ co.width = self.style.scrollBarWidth + self.style.scrollBarBorderWidth;
395
+ co.height = self.style.scrollBarWidth + self.style.scrollBarBorderWidth;
396
+ self.scrollBox.entities = {
397
+ horizontalBar: n,
398
+ horizontalBox: nb,
399
+ verticalBar: v,
400
+ verticalBox: vb,
401
+ corner: co,
402
+ };
403
+ self.scrollBox.bar = {
404
+ v: v,
405
+ h: n,
406
+ };
407
+ self.scrollBox.box = {
408
+ v: vb,
409
+ h: nb,
410
+ };
411
+ /// calculate page and dom elements
412
+ self.page = Math.max(
413
+ 1,
414
+ self.visibleRows.length - 3 - self.attributes.pageUpDownOverlap,
415
+ );
416
+ // set canvas drawing related items
417
+ if (!self.isChildGrid) {
418
+ const newWidth = self.width * ratio;
419
+ const newHeight = self.height * ratio;
420
+ // We need to check is settings size to canvas necessary,
421
+ // because settings the canvas'size will cause the canvas and its state be cleared
422
+ // even if the size is the same.
423
+ // Notes: Please don't call `self.resize()` without a subsequent call to `self.draw()`
424
+ if (self.canvas.width !== newWidth || self.canvas.height !== newHeight) {
425
+ self.canvas.width = newWidth;
426
+ self.canvas.height = newHeight;
427
+ self.ctx.scale(ratio, ratio);
428
+ }
429
+ }
430
+ // resize any open dom elements (input/textarea)
431
+ self.resizeEditInput();
432
+ self.scroll(true);
433
+ if (drawAfterResize) {
434
+ self.draw(true);
435
+ }
436
+ self.dispatchEvent('resize', {});
437
+ return true;
438
+ };
439
+ self.scroll = function (dontDraw) {
440
+ var s = self.getSchema(),
441
+ l = (self.viewData || []).length,
442
+ ch = self.style.cellHeight;
443
+ // go too far in leaps, then get focused
444
+ self.scrollIndexTop = Math.floor(
445
+ l * (self.scrollBox.scrollTop / self.scrollBox.scrollHeight) - 100,
446
+ );
447
+ self.scrollIndexTop = Math.max(self.scrollIndexTop, 0);
448
+ self.scrollPixelTop = self.scrollCache.y[self.scrollIndexTop];
449
+ // sometimes the grid is rendered but the height is zero
450
+ if (self.scrollBox.scrollHeight === 0) {
451
+ self.scrollIndexTop = 0;
452
+ }
453
+ self.scrollPixelTop = 0;
454
+ self.scrollIndexLeft = self.frozenColumn;
455
+ self.scrollPixelLeft = 0;
456
+ while (
457
+ self.scrollPixelTop < self.scrollBox.scrollTop &&
458
+ self.scrollIndexTop < self.viewData.length
459
+ ) {
460
+ // start on index +1 since index 0 was used in "go too far" section above
461
+ self.scrollIndexTop += 1;
462
+ self.scrollPixelTop = self.scrollCache.y[self.scrollIndexTop];
463
+ }
464
+ while (
465
+ self.scrollPixelLeft < self.scrollBox.scrollLeft + 1 &&
466
+ self.scrollIndexLeft < s.length
467
+ ) {
468
+ self.scrollPixelLeft = self.scrollCache.x[self.scrollIndexLeft];
469
+ self.scrollIndexLeft += 1;
470
+ }
471
+ if (s.length > 0) {
472
+ self.scrollIndexLeft = Math.max(self.scrollIndexLeft - 1, 0);
473
+ self.scrollPixelLeft -= self.getColumnWidth(
474
+ self.orders.columns[self.scrollIndexLeft],
475
+ );
476
+ }
477
+ if ((self.viewData || []).length > 0) {
478
+ self.scrollIndexTop = Math.max(self.scrollIndexTop - 1, 0);
479
+ self.scrollPixelTop = Math.max(
480
+ self.scrollPixelTop -
481
+ (self.viewData[self.scrollIndexTop]
482
+ ? (self.sizes.rows[self.scrollIndexTop] || ch) +
483
+ (self.sizes.trees[self.scrollIndexTop] || 0)
484
+ : ch) *
485
+ self.scale,
486
+ 0,
487
+ );
488
+ }
489
+ self.ellipsisCache = {};
490
+ if (!dontDraw) {
491
+ self.draw(true);
492
+ }
493
+ //TODO: figure out why this has to be delayed for child grids
494
+ //BUG: wheeling event on 3rd level hierarchy fails to move input box
495
+ requestAnimationFrame(self.resizeEditInput);
496
+ self.dispatchEvent('scroll', {
497
+ top: self.scrollBox.scrollTop,
498
+ left: self.scrollBox.scrollLeft,
499
+ });
500
+ };
501
+ self.mousemove = function (e, overridePos) {
502
+ if (self.contextMenu || self.input) {
503
+ return;
504
+ }
505
+
506
+ // Cancel dragging action if user ventures outside grid
507
+ if (self.draggingItem && e.which === 0) {
508
+ self.stopFreezeMove(e);
509
+ self.mouseup(e);
510
+ return;
511
+ }
512
+
513
+ self.mouse = overridePos || self.getLayerPos(e);
514
+ var ctrl =
515
+ (e.ctrlKey || e.metaKey || self.attributes.persistantSelectionMode) &&
516
+ !self.attributes.singleSelectionMode,
517
+ i,
518
+ s = self.getSchema(),
519
+ dragBounds,
520
+ sBounds,
521
+ x = self.mouse.x,
522
+ y = self.mouse.y,
523
+ cell = self.getCellAt(x, y),
524
+ delta,
525
+ disallowVerticalAutoScroll = false,
526
+ disallowHorizontalAutoScroll = false,
527
+ ev = { NativeEvent: e, cell: cell, x: x, y: y },
528
+ previousCell = self.currentCell;
529
+ clearTimeout(self.scrollTimer);
530
+ if (!self.isInGrid({ x: x, y: y })) {
531
+ self.hasFocus = false;
532
+ }
533
+ if (self.dispatchEvent('mousemove', ev)) {
534
+ return;
535
+ }
536
+ if (cell && self.currentCell) {
537
+ self.rowBoundaryCrossed = self.currentCell.rowIndex !== cell.rowIndex;
538
+ self.columnBoundaryCrossed =
539
+ self.currentCell.columnIndex !== cell.columnIndex;
540
+ self.cellBoundaryCrossed =
541
+ self.rowBoundaryCrossed || self.columnBoundaryCrossed;
542
+ ['row', 'column', 'cell'].forEach(function (prefix) {
543
+ if (self[prefix + 'BoundaryCrossed']) {
544
+ ev.cell = previousCell;
545
+ self.dispatchEvent(prefix + 'mouseout', ev);
546
+ ev.cell = cell;
547
+ self.dispatchEvent(prefix + 'mouseover', ev);
548
+ }
549
+ });
550
+ }
551
+ self.currentCell = cell;
552
+
553
+ if (
554
+ !self.draggingItem && // It is not in dragging mode (avoid changing hovers frequent)
555
+ cell &&
556
+ (cell.context === 'cell' || cell.context === self.cursorGrab)
557
+ ) {
558
+ const indicator = self.getUnhideIndicator(self.mouse.x, self.mouse.y);
559
+ if (indicator) {
560
+ self.cursor = 'pointer';
561
+ self.hovers = { unhideIndicator: indicator };
562
+ self.draw();
563
+ return;
564
+ }
565
+ }
566
+
567
+ self.hovers = {};
568
+ if (!self.draggingItem && cell) {
569
+ self.dragItem = cell;
570
+ self.dragMode = cell.dragContext;
571
+ self.cursor = cell.context;
572
+ if (cell.context === 'cell') {
573
+ self.cursor = 'default';
574
+ self.hovers = {
575
+ rowIndex: cell.rowIndex,
576
+ columnIndex: cell.columnIndex,
577
+ onFilterButton: false,
578
+ onCellTreeIcon: false,
579
+ };
580
+ if (
581
+ cell.isFilterable &&
582
+ x >
583
+ cell.x +
584
+ cell.width +
585
+ self.canvasOffsetLeft -
586
+ self.style.filterButtonWidth &&
587
+ x < cell.x + cell.width + self.canvasOffsetLeft &&
588
+ y >
589
+ cell.y +
590
+ cell.height +
591
+ self.canvasOffsetTop -
592
+ self.style.filterButtonHeight &&
593
+ y < cell.y + cell.height + self.canvasOffsetTop
594
+ ) {
595
+ self.hovers.onFilterButton = true;
596
+ self.draw();
597
+ }
598
+ if (cell.isRowTree || cell.isColumnTree) {
599
+ const pc = cell.isRowTree
600
+ ? self.cellTree.rows[cell.rowIndex].parentCount
601
+ : 0;
602
+ const rc = self.style.cellTreeIconWidth * self.scale,
603
+ rx =
604
+ cell.x +
605
+ cell.paddingLeft +
606
+ self.canvasOffsetLeft +
607
+ self.style.cellTreeIconMarginLeft +
608
+ pc * (rc + cell.paddingLeft),
609
+ ry =
610
+ cell.y +
611
+ self.canvasOffsetTop +
612
+ self.style.cellTreeIconMarginTop * self.scale;
613
+ if (x >= rx && x <= rx + rc && y >= ry && y < ry + rc) {
614
+ self.hovers.onCellTreeIcon = true;
615
+ self.draw();
616
+ }
617
+ }
618
+ }
619
+ if (self.selecting || self.reorderObject) {
620
+ delta = {
621
+ x: Math.abs(self.dragStart.x - x),
622
+ y: Math.abs(self.dragStart.y - y),
623
+ };
624
+ if (
625
+ self.dragStartObject.columnIndex !== -1 &&
626
+ e.shiftKey &&
627
+ !self.attributes.singleSelectionMode
628
+ ) {
629
+ self.dragStartObject = {
630
+ rowIndex: self.activeCell.rowIndex,
631
+ columnIndex: self.activeCell.columnIndex,
632
+ };
633
+ }
634
+ dragBounds = {
635
+ top: Math.min(self.dragStartObject.rowIndex, cell.rowIndex),
636
+ left: Math.min(self.dragStartObject.columnIndex, cell.columnIndex),
637
+ bottom: Math.max(self.dragStartObject.rowIndex, cell.rowIndex),
638
+ right: Math.max(self.dragStartObject.columnIndex, cell.columnIndex),
639
+ };
640
+ if (self.dragStartObject.columnIndex === -1) {
641
+ sBounds = self.getSelectionBounds();
642
+ dragBounds.left = -1;
643
+ dragBounds.right = s.length - 1;
644
+ dragBounds.top = Math.min(sBounds.top, cell.rowIndex);
645
+ dragBounds.bottom = Math.max(sBounds.bottom, cell.rowIndex);
646
+ if (dragBounds.top < 0) dragBounds.top = 0;
647
+ }
648
+
649
+ if (self.dragStartObject.rowIndex === -1) {
650
+ sBounds = self.getSelectionBounds();
651
+ dragBounds.left =
652
+ cell.columnIndex === undefined
653
+ ? sBounds.left
654
+ : Math.min(sBounds.left, cell.columnIndex);
655
+ dragBounds.right =
656
+ cell.columnIndex === undefined
657
+ ? sBounds.right
658
+ : Math.max(sBounds.right, cell.columnIndex);
659
+ dragBounds.top = -1;
660
+ dragBounds.bottom = self.viewData.length - 1;
661
+ if (dragBounds.left < 0) dragBounds.left = 0;
662
+ if (dragBounds.left != dragBounds.right)
663
+ self.isMultiColumnsSelected = true;
664
+ else self.isMultiRowsSelected = false;
665
+ }
666
+
667
+ if (
668
+ self.dragStartObject.rowIndex !== cell.rowIndex ||
669
+ self.dragStartObject.columnIndex !== cell.columnIndex
670
+ ) {
671
+ self.ignoreNextClick = true;
672
+ }
673
+ if (
674
+ self.cellBoundaryCrossed ||
675
+ (delta.x === 0 && delta.y === 0) ||
676
+ self.attributes.selectionMode === 'row'
677
+ ) {
678
+ if (
679
+ cell.rowIndex !== undefined &&
680
+ (self.attributes.selectionMode === 'row' ||
681
+ self.dragStartObject.columnIndex === -1) &&
682
+ self.rowBoundaryCrossed
683
+ ) {
684
+ if (self.dragStartObject.rowIndex < cell.rowIndex) {
685
+ dragBounds.top = self.dragStartObject.rowIndex;
686
+ dragBounds.bottom = cell.rowIndex;
687
+ } else {
688
+ dragBounds.top = cell.rowIndex;
689
+ dragBounds.bottom = self.dragStartObject.rowIndex;
690
+ }
691
+ } else if (
692
+ cell.rowIndex !== undefined &&
693
+ (self.attributes.selectionMode === 'column' ||
694
+ self.dragStartObject.rowIndex === -1) &&
695
+ self.columnBoundaryCrossed
696
+ ) {
697
+ if (self.dragStartObject.columnIndex < cell.columnIndex) {
698
+ dragBounds.left = self.dragStartObject.columnIndex;
699
+ dragBounds.right = cell.columnIndex;
700
+ } else {
701
+ dragBounds.left = cell.columnIndex;
702
+ dragBounds.right = self.dragStartObject.columnIndex;
703
+ }
704
+ } else if (self.attributes.selectionMode !== 'row') {
705
+ if (cell.hovered && self.hovers.onFilterButton) {
706
+ if (cell.openedFilter) {
707
+ cell.openedFilter = false;
708
+ self.selectedFilterButton = {
709
+ columnIndex: -1,
710
+ rowIndex: -1,
711
+ };
712
+ } else {
713
+ self.selectedFilterButton.rowIndex = cell.rowIndex;
714
+ self.selectedFilterButton.columnIndex = cell.columnIndex;
715
+ self.contextmenuEvent(e, {
716
+ x: cell.x + cell.width - self.style.filterButtonWidth,
717
+ y: cell.y + cell.height,
718
+ rect: { left: 0, top: 0 },
719
+ });
720
+ }
721
+ self.draw();
722
+ return;
723
+ } else if (
724
+ cell.hovered &&
725
+ self.hovers.onCellTreeIcon &&
726
+ e.type == 'mousedown'
727
+ ) {
728
+ self.toggleCollapseTree(cell.rowIndex, cell.columnIndex);
729
+ return;
730
+ } else {
731
+ self.selectedFilterButton = {
732
+ columnIndex: -1,
733
+ rowIndex: -1,
734
+ };
735
+ if (self.hovers.onFilterButton) return;
736
+ if (self.hovers.onCellTreeIcon) return;
737
+ if (!self.dragAddToSelection && cell.rowIndex !== undefined) {
738
+ if (self.isCellSelected(cell)) self.unselectCell(cell);
739
+ } else {
740
+ if (!self.isCellSelected(cell)) self.selectCell(cell);
741
+ }
742
+ }
743
+ }
744
+ }
745
+ if (self.attributes.singleSelectionMode) {
746
+ // only one row (row mode) or one cell may be selected at a time, so
747
+ // a drag follows the pointer instead of growing a range (#472)
748
+ if (
749
+ self.dragStartObject.rowIndex !== -1 &&
750
+ cell.rowIndex !== undefined &&
751
+ cell.rowIndex > -1
752
+ ) {
753
+ dragBounds.top = cell.rowIndex;
754
+ dragBounds.bottom = cell.rowIndex;
755
+ }
756
+ if (
757
+ self.attributes.selectionMode !== 'row' &&
758
+ self.dragStartObject.columnIndex !== -1 &&
759
+ cell.columnIndex !== undefined &&
760
+ cell.columnIndex > -1
761
+ ) {
762
+ dragBounds.left = cell.columnIndex;
763
+ dragBounds.right = cell.columnIndex;
764
+ }
765
+ }
766
+ if (
767
+ (!self.selectionBounds ||
768
+ dragBounds.top !== self.selectionBounds.top ||
769
+ dragBounds.left !== self.selectionBounds.left ||
770
+ dragBounds.bottom !== self.selectionBounds.bottom ||
771
+ dragBounds.right !== self.selectionBounds.right) &&
772
+ !ctrl
773
+ ) {
774
+ if (!(cell.hovered && self.hovers.onFilterButton))
775
+ self.clearSelections();
776
+ if (dragBounds.top === -1) {
777
+ dragBounds.top = 0;
778
+ }
779
+ sBounds = dragBounds;
780
+ if (self.attributes.selectionMode === 'row') {
781
+ for (i = sBounds.top; i <= sBounds.bottom; i += 1) {
782
+ self.selectRow(i, true, null, true);
783
+ }
784
+ } else {
785
+ self.selectArea(sBounds, true);
786
+ self.activeCell.rowIndex = sBounds.top;
787
+ self.activeCell.columnIndex = sBounds.left;
788
+ if (sBounds.left == -1 && sBounds.top !== sBounds.bottom) {
789
+ self.activeCell.columnIndex = 0;
790
+ self.isMultiRowsSelected = true;
791
+ self.isMultiColumnsSelected = false;
792
+ }
793
+ }
794
+ }
795
+ } else if (self.movingSelectionHandle) {
796
+ delta = {
797
+ x: Math.abs(self.dragStart.x - x),
798
+ y: Math.abs(self.dragStart.y - y),
799
+ };
800
+
801
+ // Disallow auto-scroll to the direction that overlay is not
802
+ // moving towards.
803
+ if (self.fillOverlay.direction === 'y') {
804
+ disallowHorizontalAutoScroll = true;
805
+ } else if (self.fillOverlay.direction === 'x') {
806
+ disallowVerticalAutoScroll = true;
807
+ }
808
+ }
809
+
810
+ if (delta) {
811
+ if (
812
+ self.attributes.autoScrollOnMousemove ||
813
+ disallowVerticalAutoScroll ||
814
+ disallowHorizontalAutoScroll
815
+ ) {
816
+ var movedVertically =
817
+ !disallowVerticalAutoScroll &&
818
+ delta.y > self.attributes.autoScrollMargin;
819
+ var movedHorizontally =
820
+ !disallowHorizontalAutoScroll &&
821
+ delta.x > self.attributes.autoScrollMargin;
822
+
823
+ if (movedVertically && !movedHorizontally)
824
+ self.autoScrollZone(e, -1, y, ctrl);
825
+ else if (movedHorizontally && !movedVertically)
826
+ self.autoScrollZone(e, x, -1, ctrl);
827
+ else if (movedHorizontally && movedVertically)
828
+ self.autoScrollZone(e, x, y, ctrl);
829
+ } else {
830
+ self.autoScrollZone(e, x, y, ctrl);
831
+ }
832
+ }
833
+ }
834
+ const columnGroup = self.getColumnGroupAt(self.mouse.x, self.mouse.y);
835
+ if (columnGroup) self.cursor = 'pointer';
836
+
837
+ const rowGroup = self.getRowGroupAt(self.mouse.x, self.mouse.y);
838
+ if (rowGroup) self.cursor = 'pointer';
839
+
840
+ self.cellBoundaryCrossed = false;
841
+ self.rowBoundaryCrossed = false;
842
+ self.columnBoundaryCrossed = false;
843
+ self.draw(true);
844
+ };
845
+ self.click = function (e, overridePos) {
846
+ var i,
847
+ startingBounds = JSON.stringify(self.getSelectionBounds()),
848
+ ctrl =
849
+ (e.ctrlKey || e.metaKey || self.attributes.persistantSelectionMode) &&
850
+ !self.attributes.singleSelectionMode,
851
+ pos = overridePos || self.getLayerPos(e);
852
+ self.currentCell = self.getCellAt(pos.x, pos.y);
853
+ if (self.currentCell.grid !== undefined) {
854
+ return;
855
+ }
856
+ function checkSelectionChange() {
857
+ var ev,
858
+ sb = self.getSelectionBounds();
859
+
860
+ if (startingBounds === JSON.stringify(sb)) {
861
+ return;
862
+ }
863
+ self.dispatchEvent('selectionchanged', self.getContextOfSelectionEvent());
864
+ }
865
+ if (self.input) {
866
+ self.endEdit();
867
+ }
868
+ if (self.ignoreNextClick) {
869
+ self.ignoreNextClick = false;
870
+ return;
871
+ }
872
+ i = self.currentCell;
873
+ if (
874
+ self.dispatchEvent('click', { NativeEvent: e, cell: self.currentCell })
875
+ ) {
876
+ return;
877
+ }
878
+
879
+ const unhideIndicator = self.getUnhideIndicator(pos.x, pos.y);
880
+ if (unhideIndicator) {
881
+ const { dir, orderIndex0, orderIndex1 } = unhideIndicator;
882
+ if (dir === 'l' || dir === 'r')
883
+ self.unhideColumns(orderIndex0, orderIndex1);
884
+ else self.unhideRows(orderIndex0, orderIndex1);
885
+ return;
886
+ }
887
+
888
+ let group = self.getColumnGroupAt(pos.x, pos.y);
889
+ if (!group) group = self.getRowGroupAt(pos.x, pos.y);
890
+ if (group) {
891
+ if (self.toggleGroup(group)) {
892
+ self.setStorageData();
893
+ self.refresh();
894
+ return;
895
+ }
896
+ }
897
+
898
+ if (!self.hasFocus) {
899
+ return;
900
+ }
901
+
902
+ const leftOffset = self.getRowGroupAreaWidth();
903
+ const topOffset = self.getColumnGroupAreaHeight();
904
+ const xInGrid = pos.x - leftOffset;
905
+ const yInGrid = pos.y - topOffset;
906
+ if (
907
+ ['rowHeaderCell', 'columnHeaderCell'].indexOf(self.currentCell.style) ===
908
+ -1 &&
909
+ !ctrl
910
+ ) {
911
+ if (!self.hovers.onFilterButton) {
912
+ self.setActiveCell(i.columnIndex, i.rowIndex);
913
+ }
914
+ }
915
+ if (
916
+ self.currentCell.style === 'columnHeaderCell' &&
917
+ self.attributes.columnHeaderClickBehavior === 'sort' &&
918
+ // a header is also a reorder grab zone when column reordering is on;
919
+ // a plain click (no drag, which sets ignoreNextClick) still sorts (#514)
920
+ (self.currentCell.context === 'cell' ||
921
+ self.currentCell.dragContext === 'column-reorder')
922
+ ) {
923
+ if (self.orderBy === i.header.name) {
924
+ self.orderDirection = self.orderDirection === 'asc' ? 'desc' : 'asc';
925
+ } else {
926
+ self.orderDirection = 'asc';
927
+ }
928
+ self.order(i.header.name, self.orderDirection);
929
+ checkSelectionChange();
930
+ return;
931
+ }
932
+ if (self.currentCell.context === 'cell') {
933
+ if (self.currentCell.style === 'cornerCell') {
934
+ self.selectAll();
935
+ self.draw();
936
+ checkSelectionChange();
937
+ return;
938
+ }
939
+ if (
940
+ self.attributes.selectionMode === 'row' ||
941
+ self.currentCell.style === 'rowHeaderCell'
942
+ ) {
943
+ if (
944
+ self.currentCell.style === 'rowHeaderCell' &&
945
+ self.attributes.tree &&
946
+ xInGrid > 0 &&
947
+ xInGrid - self.currentCell.x <
948
+ self.style.treeArrowWidth +
949
+ self.style.treeArrowMarginLeft +
950
+ self.style.treeArrowMarginRight +
951
+ self.style.treeArrowClickRadius &&
952
+ yInGrid - self.currentCell.y <
953
+ self.style.treeArrowHeight +
954
+ self.style.treeArrowMarginTop +
955
+ self.style.treeArrowClickRadius &&
956
+ yInGrid > 0
957
+ ) {
958
+ self.toggleTree(i.rowIndex);
959
+ return;
960
+ }
961
+ }
962
+ if (e.shiftKey && !ctrl && !self.attributes.singleSelectionMode) {
963
+ self.selectionBounds = self.getSelectionBounds();
964
+ self.selectArea(undefined, false);
965
+ }
966
+ }
967
+ checkSelectionChange();
968
+ self.draw(true);
969
+ };
970
+ self.dragResizeColumn = function (e) {
971
+ const resizingColumn = self.dragMode === 'ew-resize';
972
+ var pos, x, y;
973
+ pos = self.getLayerPos(e);
974
+ x = self.resizingStartingWidth + pos.x - self.dragStart.x;
975
+ y = self.resizingStartingHeight + pos.y - self.dragStart.y;
976
+ if (x < self.style.minColumnWidth) {
977
+ x = self.style.minColumnWidth;
978
+ }
979
+ if (y < self.style.minRowHeight) {
980
+ y = self.style.minRowHeight;
981
+ }
982
+ const resizeEventDetails = {
983
+ x: x,
984
+ y: y,
985
+ width: x,
986
+ height: y,
987
+ columnIndex: resizingColumn ? self.draggingItem.columnIndex : undefined,
988
+ rowIndex: resizingColumn ? undefined : self.draggingItem.rowIndex,
989
+ draggingItem: self.draggingItem,
990
+ NativeEvent: e,
991
+ };
992
+ if (
993
+ resizingColumn
994
+ ? self.dispatchEvent('resizecolumn', resizeEventDetails)
995
+ : self.dispatchResizeRow(resizeEventDetails)
996
+ ) {
997
+ return false;
998
+ }
999
+ if (self.attributes.resizeAfterDragged) {
1000
+ self.pendingDragResize = {
1001
+ item: self.draggingItem,
1002
+ width: x,
1003
+ height: y,
1004
+ x: e.clientX,
1005
+ y: e.clientY,
1006
+ };
1007
+ } else {
1008
+ self.dragResizeApply(self.draggingItem, x, y);
1009
+ }
1010
+ };
1011
+ self.dragResizeApply = function (draggingItem, width, height) {
1012
+ if (
1013
+ self.scrollBox.scrollLeft >
1014
+ self.scrollBox.scrollWidth - self.attributes.resizeScrollZone &&
1015
+ self.dragMode === 'ew-resize'
1016
+ ) {
1017
+ self.resize(true);
1018
+ }
1019
+ if (self.dragMode === 'ew-resize') {
1020
+ self.sizes.columns[
1021
+ draggingItem.header.style === 'rowHeaderCell'
1022
+ ? 'cornerCell'
1023
+ : draggingItem.sortColumnIndex
1024
+ ] = width;
1025
+ if (
1026
+ ['rowHeaderCell', 'cornerCell'].indexOf(draggingItem.header.style) !==
1027
+ -1
1028
+ ) {
1029
+ self.resize(true);
1030
+ }
1031
+ self.resizeChildGrids();
1032
+ return;
1033
+ } else if (self.dragMode === 'ns-resize') {
1034
+ if (draggingItem.rowOpen) {
1035
+ self.sizes.trees[draggingItem.rowIndex] = height;
1036
+ } else if (self.attributes.globalRowResize) {
1037
+ self.style.cellHeight = height;
1038
+ } else {
1039
+ self.sizes.rows[draggingItem.rowIndex] = height;
1040
+ }
1041
+ self.dispatchResizeRow({
1042
+ height: height,
1043
+ rowIndex: draggingItem.rowIndex,
1044
+ draggingItem: draggingItem,
1045
+ });
1046
+ self.resizeChildGrids();
1047
+ return;
1048
+ }
1049
+ self.ellipsisCache = {};
1050
+ };
1051
+ self.stopDragResize = function (event) {
1052
+ const pos = self.getLayerPos(event);
1053
+
1054
+ if (self.attributes.resizeAfterDragged) {
1055
+ self.dragResizeApply(
1056
+ self.pendingDragResize.item,
1057
+ self.pendingDragResize.width,
1058
+ self.pendingDragResize.height,
1059
+ );
1060
+ self.pendingDragResize = undefined;
1061
+ }
1062
+
1063
+ if (self.dragMode === 'ew-resize') {
1064
+ const hasMoved = !!(pos.x - self.dragStart.x);
1065
+ // Check that dragItem is selected or part of selection.
1066
+ const dragItemIsSelected = self.isColumnSelected(
1067
+ self.dragItem.columnIndex,
1068
+ );
1069
+
1070
+ if (hasMoved && dragItemIsSelected) {
1071
+ const width = Math.max(
1072
+ self.resizingStartingWidth + pos.x - self.dragStart.x,
1073
+ self.style.minColumnWidth,
1074
+ );
1075
+ // If the column is selected, resize it to width plus any other selected columns.
1076
+ self.fitSelectedColumns(width);
1077
+ }
1078
+ } else if (self.dragMode === 'ns-resize') {
1079
+ // Do the above for rows.
1080
+ const hasMoved = !!(pos.y - self.dragStart.y);
1081
+ const dragItemIsSelected = self.isRowSelected(self.dragItem.rowIndex);
1082
+
1083
+ if (hasMoved && dragItemIsSelected) {
1084
+ const height = Math.max(
1085
+ self.resizingStartingHeight + pos.y - self.dragStart.y,
1086
+ self.style.minRowHeight,
1087
+ );
1088
+ self.fitSelectedRows(height);
1089
+ }
1090
+ }
1091
+
1092
+ self.resize();
1093
+ window.removeEventListener('mousemove', self.dragResizeColumn, false);
1094
+ window.removeEventListener('mouseup', self.stopDragResize, false);
1095
+ self.setStorageData();
1096
+ self.draw(true);
1097
+ self.ignoreNextClick = true;
1098
+ };
1099
+ self.scrollGrid = function (e) {
1100
+ var pos = self.getLayerPos(e);
1101
+ if (
1102
+ self.attributes.scrollPointerLock &&
1103
+ self.pointerLockPosition &&
1104
+ ['horizontal-scroll-box', 'vertical-scroll-box'].indexOf(
1105
+ self.scrollStartMode,
1106
+ ) !== -1
1107
+ ) {
1108
+ self.pointerLockPosition.x += e.movementX;
1109
+ self.pointerLockPosition.y += e.movementY;
1110
+ self.pointerLockPosition.x = Math.min(
1111
+ self.width - self.style.scrollBarWidth,
1112
+ Math.max(0, self.pointerLockPosition.x),
1113
+ );
1114
+ self.pointerLockPosition.y = Math.min(
1115
+ self.height - self.style.scrollBarWidth,
1116
+ Math.max(0, self.pointerLockPosition.y),
1117
+ );
1118
+ pos = self.pointerLockPosition;
1119
+ }
1120
+ self.scrollMode = self.getCellAt(pos.x, pos.y).context;
1121
+ if (
1122
+ self.scrollMode === 'horizontal-scroll-box' &&
1123
+ self.scrollStartMode !== 'horizontal-scroll-box'
1124
+ ) {
1125
+ self.scrollStartMode = 'horizontal-scroll-box';
1126
+ self.dragStart = pos;
1127
+ self.scrollStart.left = self.scrollBox.scrollLeft;
1128
+ clearTimeout(self.scrollTimer);
1129
+ return;
1130
+ }
1131
+ if (
1132
+ self.scrollMode === 'vertical-scroll-box' &&
1133
+ self.scrollStartMode !== 'vertical-scroll-box'
1134
+ ) {
1135
+ self.scrollStartMode = 'vertical-scroll-box';
1136
+ self.dragStart = pos;
1137
+ self.scrollStart.top = self.scrollBox.scrollTop;
1138
+ clearTimeout(self.scrollTimer);
1139
+ return;
1140
+ }
1141
+ if (
1142
+ self.scrollStartMode === 'vertical-scroll-box' &&
1143
+ self.scrollMode !== 'vertical-scroll-box'
1144
+ ) {
1145
+ self.scrollMode = 'vertical-scroll-box';
1146
+ }
1147
+ if (
1148
+ self.scrollStartMode === 'horizontal-scroll-box' &&
1149
+ self.scrollMode !== 'horizontal-scroll-box'
1150
+ ) {
1151
+ self.scrollMode = 'horizontal-scroll-box';
1152
+ }
1153
+ clearTimeout(self.scrollTimer);
1154
+ if (self.scrollModes.indexOf(self.scrollMode) === -1) {
1155
+ return;
1156
+ }
1157
+ if (self.scrollMode === 'vertical-scroll-box') {
1158
+ self.scrollBox.scrollTop =
1159
+ self.scrollStart.top +
1160
+ (pos.y - self.dragStart.y) / self.scrollBox.heightBoxRatio;
1161
+ } else if (self.scrollMode === 'vertical-scroll-top') {
1162
+ self.scrollBox.scrollTop -= self.page * self.style.cellHeight;
1163
+ self.scrollTimer = setTimeout(
1164
+ self.scrollGrid,
1165
+ self.attributes.scrollRepeatRate,
1166
+ e,
1167
+ );
1168
+ } else if (self.scrollMode === 'vertical-scroll-bottom') {
1169
+ self.scrollBox.scrollTop += self.page * self.style.cellHeight;
1170
+ self.scrollTimer = setTimeout(
1171
+ self.scrollGrid,
1172
+ self.attributes.scrollRepeatRate,
1173
+ e,
1174
+ );
1175
+ }
1176
+ if (self.scrollMode === 'horizontal-scroll-box') {
1177
+ self.scrollBox.scrollLeft =
1178
+ self.scrollStart.left +
1179
+ (pos.x - self.dragStart.x) / self.scrollBox.widthBoxRatio;
1180
+ } else if (self.scrollMode === 'horizontal-scroll-right') {
1181
+ self.scrollBox.scrollLeft += self.attributes.selectionScrollIncrement;
1182
+ self.scrollTimer = setTimeout(
1183
+ self.scrollGrid,
1184
+ self.attributes.scrollRepeatRate,
1185
+ e,
1186
+ );
1187
+ } else if (self.scrollMode === 'horizontal-scroll-left') {
1188
+ self.scrollBox.scrollLeft -= self.attributes.selectionScrollIncrement;
1189
+ self.scrollTimer = setTimeout(
1190
+ self.scrollGrid,
1191
+ self.attributes.scrollRepeatRate,
1192
+ e,
1193
+ );
1194
+ }
1195
+ };
1196
+ self.stopScrollGrid = function () {
1197
+ clearTimeout(self.scrollTimer);
1198
+ if (document.exitPointerLock) {
1199
+ document.exitPointerLock();
1200
+ }
1201
+ window.removeEventListener('mousemove', self.scrollGrid, false);
1202
+ };
1203
+ self.dragReorder = function (e) {
1204
+ var pos,
1205
+ x,
1206
+ y,
1207
+ columReorder = self.dragMode === 'column-reorder',
1208
+ rowReorder = self.dragMode === 'row-reorder';
1209
+ pos = self.getLayerPos(e);
1210
+ x = pos.x - self.dragStart.x;
1211
+ y = pos.y - self.dragStart.y;
1212
+ if (!self.attributes.allowColumnReordering && columReorder) {
1213
+ return;
1214
+ }
1215
+ if (!self.attributes.allowRowReordering && rowReorder) {
1216
+ return;
1217
+ }
1218
+ if ((e.ctrlKey || e.metaKey || e.shiftKey) && self.reorderObject) {
1219
+ if (self.dragMode === 'column-reorder' && !self.isMultiColumnsSelected) {
1220
+ self.selectColumn(self.draggingItem.header.index, false, false);
1221
+ }
1222
+ }
1223
+ if (
1224
+ self.dispatchEvent('reordering', {
1225
+ NativeEvent: e,
1226
+ source: self.dragStartObject,
1227
+ target: self.currentCell,
1228
+ dragMode: self.dragMode,
1229
+ })
1230
+ ) {
1231
+ return;
1232
+ }
1233
+ if (
1234
+ Math.abs(x) > self.attributes.reorderDeadZone ||
1235
+ Math.abs(y) > self.attributes.reorderDeadZone
1236
+ ) {
1237
+ self.reorderObject = self.draggingItem;
1238
+ if (self.isMultiRowsSelected)
1239
+ self.reorderObject = self.getVisibleCellByIndex(
1240
+ -1,
1241
+ self.activeCell.rowIndex,
1242
+ );
1243
+ if (self.isMultiColumnsSelected)
1244
+ self.reorderObject = self.getVisibleCellByIndex(
1245
+ self.activeCell.columnIndex,
1246
+ -1,
1247
+ );
1248
+ if (!self.reorderObject) return;
1249
+ self.reorderTarget = self.currentCell;
1250
+ self.reorderObject.dragOffset = {
1251
+ x: x,
1252
+ y: y,
1253
+ };
1254
+ self.autoScrollZone(
1255
+ e,
1256
+ columReorder ? pos.x : -1,
1257
+ rowReorder ? pos.y : -1,
1258
+ false,
1259
+ );
1260
+ }
1261
+ };
1262
+ self.stopDragReorder = function (e) {
1263
+ let originalIndex;
1264
+ let targetIndex;
1265
+ const orderLists = {
1266
+ 'row-reorder': self.orders.rows,
1267
+ 'column-reorder': self.orders.columns,
1268
+ };
1269
+ const indexName = {
1270
+ 'row-reorder': 'rowIndex',
1271
+ 'column-reorder': 'sortColumnIndex',
1272
+ }[self.dragMode];
1273
+ window.removeEventListener('mousemove', self.dragReorder, false);
1274
+ window.removeEventListener('mouseup', self.stopDragReorder, false);
1275
+ if (
1276
+ self.reorderObject &&
1277
+ self.reorderTarget &&
1278
+ ((self.dragMode === 'column-reorder' &&
1279
+ self.reorderTarget.sortColumnIndex > -1 &&
1280
+ self.reorderTarget.sortColumnIndex < self.getSchema().length) ||
1281
+ (self.dragMode === 'row-reorder' &&
1282
+ self.reorderTarget.rowIndex > -1 &&
1283
+ self.reorderTarget.rowIndex < self.viewData.length)) &&
1284
+ self.reorderObject[indexName] !== self.reorderTarget[indexName] &&
1285
+ !self.dispatchEvent('reorder', {
1286
+ NativeEvent: e,
1287
+ source: self.reorderObject,
1288
+ target: self.reorderTarget,
1289
+ dragMode: self.dragMode,
1290
+ })
1291
+ ) {
1292
+ self.ignoreNextClick = true;
1293
+ originalIndex = orderLists[self.dragMode].indexOf(
1294
+ self.reorderObject[indexName],
1295
+ );
1296
+ targetIndex = orderLists[self.dragMode].indexOf(
1297
+ self.reorderTarget[indexName],
1298
+ );
1299
+ if (self.dragMode === 'column-reorder') {
1300
+ /** Select column view indexes in the first row */
1301
+ const selectedIndexes = self.getRowSelectionStates(0);
1302
+ const sortColumnIndices = [];
1303
+ const selectedColumnIndices = [];
1304
+ if (selectedIndexes) {
1305
+ originalIndex = selectedIndexes[0];
1306
+ selectedIndexes.forEach(function (value) {
1307
+ sortColumnIndices.push(self.orders.columns[value]);
1308
+ });
1309
+ }
1310
+ const deleteCount = sortColumnIndices.length;
1311
+ if (
1312
+ targetIndex < originalIndex ||
1313
+ (targetIndex > originalIndex &&
1314
+ Math.abs(targetIndex - originalIndex) >= deleteCount)
1315
+ ) {
1316
+ orderLists[self.dragMode].splice(originalIndex, deleteCount);
1317
+ if (targetIndex > originalIndex)
1318
+ targetIndex = targetIndex - (deleteCount - 1);
1319
+
1320
+ for (let i = 0; i < sortColumnIndices.length; i++) {
1321
+ if (i === 0) self.activeCell.columnIndex = targetIndex;
1322
+ selectedColumnIndices.push(targetIndex + i);
1323
+ orderLists[self.dragMode].splice(
1324
+ targetIndex + i,
1325
+ 0,
1326
+ sortColumnIndices[i],
1327
+ );
1328
+ }
1329
+
1330
+ self.orders.columns = orderLists[self.dragMode];
1331
+ self.selectColumnViewIndexes(selectedColumnIndices);
1332
+ }
1333
+ } else {
1334
+ /** The original name of this variable is `odata` */
1335
+ const rowIndexes = self.getRowViewIndexesFromSelection();
1336
+ const originalData = rowIndexes.map(
1337
+ (rowIndex) => self.viewData[rowIndex],
1338
+ );
1339
+ if (
1340
+ targetIndex < originalIndex ||
1341
+ (targetIndex > originalIndex &&
1342
+ Math.abs(targetIndex - originalIndex) >= originalData.length)
1343
+ ) {
1344
+ self.viewData.splice(originalIndex, originalData.length);
1345
+ if (targetIndex > originalIndex)
1346
+ targetIndex = targetIndex - (originalData.length - 1);
1347
+ self.activeCell.rowIndex = targetIndex;
1348
+ for (let i = 0; i < originalData.length; i++)
1349
+ self.viewData.splice(targetIndex + i, 0, originalData[i]);
1350
+ self.moveSelection(0, targetIndex);
1351
+ }
1352
+ }
1353
+ self.resize();
1354
+ self.setStorageData();
1355
+ }
1356
+ self.reorderObject = undefined;
1357
+ self.reorderTarget = undefined;
1358
+ self.draw(true);
1359
+ };
1360
+ self.dragMove = function (e) {
1361
+ if (
1362
+ self.dispatchEvent('moving', { NativeEvent: e, cell: self.currentCell })
1363
+ ) {
1364
+ return;
1365
+ }
1366
+ var pos = self.getLayerPos(e);
1367
+ self.moveOffset = {
1368
+ x: self.currentCell.columnIndex - self.dragStartObject.columnIndex,
1369
+ y: self.currentCell.rowIndex - self.dragStartObject.rowIndex,
1370
+ };
1371
+ if (
1372
+ Math.abs(pos.x) > self.attributes.reorderDeadZone ||
1373
+ Math.abs(pos.y) > self.attributes.reorderDeadZone
1374
+ ) {
1375
+ setTimeout(function () {
1376
+ self.autoScrollZone(e, pos.x, pos.y, false);
1377
+ }, 1);
1378
+ }
1379
+ };
1380
+ self.stopDragMove = function (e) {
1381
+ window.removeEventListener('mousemove', self.dragMove, false);
1382
+ window.removeEventListener('mouseup', self.stopDragMove, false);
1383
+ var b = self.getSelectionBounds();
1384
+ if (
1385
+ self.dispatchEvent('endmove', { NativeEvent: e, cell: self.currentCell })
1386
+ ) {
1387
+ self.movingSelection = undefined;
1388
+ self.moveOffset = undefined;
1389
+ self.draw(true);
1390
+ return;
1391
+ }
1392
+ if (self.moveOffset) {
1393
+ self.moveTo(
1394
+ self.movingSelection,
1395
+ b.left + self.moveOffset.x,
1396
+ b.top + self.moveOffset.y,
1397
+ );
1398
+ self.moveSelection(self.moveOffset.x, self.moveOffset.y);
1399
+ }
1400
+ self.movingSelection = undefined;
1401
+ self.moveOffset = undefined;
1402
+ self.draw(true);
1403
+ };
1404
+ self.freezeMove = function (e) {
1405
+ if (
1406
+ self.dispatchEvent('freezemoving', {
1407
+ NativeEvent: e,
1408
+ cell: self.currentCell,
1409
+ })
1410
+ ) {
1411
+ return;
1412
+ }
1413
+ var pos = self.getLayerPos(e);
1414
+
1415
+ self.ignoreNextClick = true;
1416
+ self.freezeMarkerPosition = pos;
1417
+ pos.x -= self.getRowGroupAreaWidth();
1418
+ pos.y -= self.getColumnGroupAreaHeight();
1419
+ if (
1420
+ Math.abs(pos.x) > self.attributes.reorderDeadZone ||
1421
+ Math.abs(pos.y) > self.attributes.reorderDeadZone
1422
+ ) {
1423
+ setTimeout(function () {
1424
+ self.autoScrollZone(e, pos.x, pos.y, false);
1425
+ }, 1);
1426
+ }
1427
+ };
1428
+ self.stopFreezeMove = function (e) {
1429
+ let pos = self.getLayerPos(e),
1430
+ bm = self.style.gridBorderCollapse === 'collapse' ? 1 : 2,
1431
+ columnHeaderCellBorder = self.style.columnHeaderCellBorderWidth * bm,
1432
+ columnHeaderCellHeight = self.getColumnHeaderCellHeight(),
1433
+ rowHeaderCellWidth = self.getRowHeaderCellWidth();
1434
+ pos.x -= self.getRowGroupAreaWidth();
1435
+ pos.y -= self.getColumnGroupAreaHeight();
1436
+
1437
+ if (
1438
+ self.currentCell &&
1439
+ self.currentCell.rowIndex !== undefined &&
1440
+ self.dragMode === 'frozen-row-marker'
1441
+ ) {
1442
+ self.scrollBox.scrollTop = 0;
1443
+ self.frozenRow = self.currentCell.rowIndex + 1;
1444
+ self.scrollBox.bar.v.y =
1445
+ columnHeaderCellHeight +
1446
+ columnHeaderCellBorder +
1447
+ self.scrollCache.y[self.frozenRow];
1448
+ self.scrollBox.box.v.y =
1449
+ columnHeaderCellHeight +
1450
+ columnHeaderCellBorder +
1451
+ self.scrollCache.y[self.frozenRow];
1452
+ var dataHeight = self.scrollCache.y[self.scrollCache.y.length - 1];
1453
+ self.scrollBox.heightBoxRatio =
1454
+ (self.scrollBox.height -
1455
+ columnHeaderCellHeight -
1456
+ self.scrollCache.y[self.frozenRow]) /
1457
+ dataHeight;
1458
+ self.scrollBox.scrollBoxHeight =
1459
+ self.scrollBox.height * self.scrollBox.heightBoxRatio -
1460
+ self.style.scrollBarWidth;
1461
+ self.scrollBox.scrollBoxHeight = Math.max(
1462
+ self.scrollBox.scrollBoxHeight,
1463
+ self.style.scrollBarBoxMinSize,
1464
+ );
1465
+ self.scrollBox.box.v.height = self.scrollBox.scrollBoxHeight;
1466
+ }
1467
+ if (
1468
+ self.currentCell &&
1469
+ self.currentCell.columnIndex !== undefined &&
1470
+ self.dragMode === 'frozen-column-marker'
1471
+ ) {
1472
+ const dataWidth = self.refreshScrollCacheX();
1473
+ self.scrollBox.scrollLeft = 0;
1474
+ let x =
1475
+ self.currentCell.x -
1476
+ self.style.frozenMarkerWidth -
1477
+ self.style.frozenMarkerBorderWidth;
1478
+ if (pos.x > x + self.currentCell.width / 2)
1479
+ self.frozenColumn = self.currentCell.columnIndex + 1;
1480
+ else if (self.currentCell.columnIndex >= 0)
1481
+ self.frozenColumn = self.currentCell.columnIndex;
1482
+ self.scrollBox.bar.h.x =
1483
+ rowHeaderCellWidth + self.scrollCache.x[self.frozenColumn];
1484
+ self.scrollBox.widthBoxRatio =
1485
+ (self.scrollBox.width - self.scrollCache.x[self.frozenColumn]) /
1486
+ dataWidth;
1487
+ self.scrollBox.scrollBoxWidth =
1488
+ self.scrollBox.width * self.scrollBox.widthBoxRatio -
1489
+ self.style.scrollBarWidth;
1490
+ self.scrollBox.scrollBoxWidth = Math.max(
1491
+ self.scrollBox.scrollBoxWidth,
1492
+ self.style.scrollBarBoxMinSize,
1493
+ );
1494
+ self.scrollBox.box.h.width = self.scrollBox.scrollBoxWidth;
1495
+ }
1496
+ if (
1497
+ Math.abs(pos.x) > self.attributes.reorderDeadZone ||
1498
+ Math.abs(pos.y) > self.attributes.reorderDeadZone
1499
+ ) {
1500
+ setTimeout(function () {
1501
+ self.autoScrollZone(e, pos.x, pos.y, false);
1502
+ }, 1);
1503
+ }
1504
+
1505
+ window.removeEventListener('mousemove', self.freezeMove, false);
1506
+ window.removeEventListener('mouseup', self.stopFreezeMove, false);
1507
+ self.freezeMarkerPosition = undefined;
1508
+ if (
1509
+ self.dispatchEvent('endfreezemove', {
1510
+ NativeEvent: e,
1511
+ cell: self.currentCell,
1512
+ })
1513
+ ) {
1514
+ self.frozenRow = self.startFreezeMove.x;
1515
+ self.frozenColumn = self.startFreezeMove.y;
1516
+ self.draw(true);
1517
+ return;
1518
+ }
1519
+ self.draw(true);
1520
+ self.resize();
1521
+ };
1522
+ self.mousedown = function (e, overridePos) {
1523
+ self.lastMouseDownTarget = e.target;
1524
+ if (
1525
+ self.dispatchEvent('mousedown', {
1526
+ NativeEvent: e,
1527
+ cell: self.currentCell,
1528
+ })
1529
+ ) {
1530
+ return;
1531
+ }
1532
+ if (!self.hasFocus) {
1533
+ return;
1534
+ }
1535
+ if (e.button === 2 || self.input) {
1536
+ return;
1537
+ }
1538
+ var ctrl = (e.ctrlKey || e.metaKey) && !self.attributes.singleSelectionMode,
1539
+ shift = e.shiftKey && !self.attributes.singleSelectionMode,
1540
+ move = /-move/.test(self.dragMode),
1541
+ freeze = /frozen-row-marker|frozen-column-marker/.test(self.dragMode),
1542
+ resize = /-resize/.test(self.dragMode),
1543
+ selectionHandleMove = /selection-handle-br/.test(self.dragMode);
1544
+ const onUnhideIndicator = self.hovers && self.hovers.unhideIndicator;
1545
+ self.dragStart = overridePos || self.getLayerPos(e);
1546
+ self.scrollStart = {
1547
+ left: self.scrollBox.scrollLeft,
1548
+ top: self.scrollBox.scrollTop,
1549
+ };
1550
+ self.dragStartObject = self.getCellAt(self.dragStart.x, self.dragStart.y);
1551
+ self.dragAddToSelection = !self.dragStartObject.selected;
1552
+ if (
1553
+ !ctrl &&
1554
+ !shift &&
1555
+ !/(vertical|horizontal)-scroll-(bar|box)/.test(
1556
+ self.dragStartObject.context,
1557
+ ) &&
1558
+ self.currentCell &&
1559
+ !self.currentCell.isColumnHeader &&
1560
+ !move &&
1561
+ !freeze &&
1562
+ !resize &&
1563
+ !selectionHandleMove
1564
+ ) {
1565
+ if (
1566
+ !(self.dragMode == 'row-reorder' && self.isMultiRowsSelected) &&
1567
+ !(self.currentCell.hovered && self.hovers.onFilterButton)
1568
+ ) {
1569
+ self.clearSelections();
1570
+ }
1571
+ }
1572
+ if (self.dragStartObject.isGrid) {
1573
+ return;
1574
+ }
1575
+ if (
1576
+ self.scrollModes.indexOf(self.dragStartObject.context) !== -1 &&
1577
+ !onUnhideIndicator
1578
+ ) {
1579
+ self.scrollMode = self.dragStartObject.context;
1580
+ self.scrollStartMode = self.dragStartObject.context;
1581
+ self.scrollGrid(e);
1582
+ if (
1583
+ self.attributes.scrollPointerLock &&
1584
+ ['horizontal-scroll-box', 'vertical-scroll-box'].indexOf(
1585
+ self.scrollStartMode,
1586
+ ) !== -1
1587
+ ) {
1588
+ self.pointerLockPosition = {
1589
+ x: self.dragStart.x,
1590
+ y: self.dragStart.y,
1591
+ };
1592
+ self.canvas.requestPointerLock();
1593
+ }
1594
+ window.addEventListener('mousemove', self.scrollGrid, false);
1595
+ window.addEventListener('mouseup', self.stopScrollGrid, false);
1596
+ self.ignoreNextClick = true;
1597
+ return;
1598
+ }
1599
+ if (self.dragMode === 'cell') {
1600
+ self.selecting = true;
1601
+ if (
1602
+ (self.attributes.selectionMode === 'row' ||
1603
+ self.dragStartObject.columnIndex === -1) &&
1604
+ self.dragStartObject.rowIndex > -1
1605
+ ) {
1606
+ self.selectRow(self.dragStartObject.rowIndex, ctrl, null);
1607
+ } else if (
1608
+ (self.attributes.selectionMode === 'column' ||
1609
+ self.dragStartObject.rowIndex === -1) &&
1610
+ self.dragStartObject.columnIndex > -1
1611
+ ) {
1612
+ if (self.attributes.allowColumnSelection) {
1613
+ self.selectColumn(self.currentCell.header.index, ctrl, shift);
1614
+ }
1615
+ return;
1616
+ }
1617
+ if (self.attributes.selectionMode !== 'row') {
1618
+ self.mousemove(e);
1619
+ }
1620
+ return;
1621
+ }
1622
+ if (selectionHandleMove && self.attributes.editable) {
1623
+ self.movingSelectionHandle = true;
1624
+ self.fillOverlay = {
1625
+ handle: {
1626
+ x: self.dragStartObject.x + self.dragStartObject.width / 2,
1627
+ y: self.dragStartObject.y + self.dragStartObject.height / 2,
1628
+ },
1629
+ snapTo: { x: -1, y: -1 },
1630
+ selection: self.getSelectionBounds(),
1631
+ };
1632
+ if (self.dispatchEvent('beginselectionhandlemove', { NativeEvent: e })) {
1633
+ return;
1634
+ }
1635
+ window.addEventListener('mousemove', self.selectionHandleMove, false);
1636
+ window.addEventListener('mouseup', self.stopSelectionHandleMove, false);
1637
+ return self.selectionHandleMove(e);
1638
+ }
1639
+ if (move) {
1640
+ self.draggingItem = self.dragItem;
1641
+ self.movingSelection = self.cloneSelections();
1642
+ self.dragging = self.dragStartObject;
1643
+ if (
1644
+ self.dispatchEvent('beginmove', {
1645
+ NativeEvent: e,
1646
+ cell: self.currentCell,
1647
+ })
1648
+ ) {
1649
+ return;
1650
+ }
1651
+ window.addEventListener('mousemove', self.dragMove, false);
1652
+ window.addEventListener('mouseup', self.stopDragMove, false);
1653
+ return self.mousemove(e);
1654
+ }
1655
+ if (freeze) {
1656
+ self.draggingItem = self.dragItem;
1657
+ self.startFreezeMove = {
1658
+ x: self.frozenRow,
1659
+ y: self.frozenColumn,
1660
+ };
1661
+ if (self.dispatchEvent('beginfreezemove', { NativeEvent: e })) {
1662
+ return;
1663
+ }
1664
+
1665
+ self.freezeMarkerPosition = self.getLayerPos(e);
1666
+ self.freezeMarkerPosition.isGrab = true;
1667
+
1668
+ window.addEventListener('mousemove', self.freezeMove, false);
1669
+ window.addEventListener('mouseup', self.stopFreezeMove, false);
1670
+ return self.mousemove(e);
1671
+ }
1672
+ if (resize && !onUnhideIndicator) {
1673
+ self.draggingItem = self.dragItem;
1674
+ if (self.draggingItem.rowOpen) {
1675
+ self.resizingStartingHeight =
1676
+ self.sizes.trees[self.draggingItem.rowIndex];
1677
+ } else {
1678
+ self.resizingStartingHeight =
1679
+ self.sizes.rows[self.draggingItem.rowIndex] || self.style.cellHeight;
1680
+ }
1681
+ self.resizingStartingWidth =
1682
+ self.sizes.columns[
1683
+ self.draggingItem.header.style === 'rowHeaderCell'
1684
+ ? 'cornerCell'
1685
+ : self.draggingItem.sortColumnIndex
1686
+ ] || self.draggingItem.width;
1687
+ window.addEventListener('mousemove', self.dragResizeColumn, false);
1688
+ window.addEventListener('mouseup', self.stopDragResize, false);
1689
+ return;
1690
+ }
1691
+ if (['row-reorder', 'column-reorder'].indexOf(self.dragMode) !== -1) {
1692
+ self.draggingItem = self.dragStartObject;
1693
+ if (
1694
+ self.dragMode === 'column-reorder' &&
1695
+ !self.isMultiColumnsSelected &&
1696
+ self.attributes.allowColumnSelection
1697
+ ) {
1698
+ self.selectColumn(self.currentCell.header.index, ctrl, shift);
1699
+ } else if (self.dragMode === 'row-reorder' && !self.isMultiRowsSelected) {
1700
+ self.selectRow(self.dragStartObject.rowIndex, ctrl, null);
1701
+ }
1702
+ window.addEventListener('mousemove', self.dragReorder, false);
1703
+ window.addEventListener('mouseup', self.stopDragReorder, false);
1704
+ return;
1705
+ }
1706
+ };
1707
+ /**
1708
+ * A mouseup anywhere ends a selection drag. Only the canvas receives the
1709
+ * full mouseup handler, so releasing the button outside the grid used to
1710
+ * leave it in "selecting" mode and the next mousemove kept extending the
1711
+ * selection.
1712
+ */
1713
+ self.mouseupOutsideGrid = function (e) {
1714
+ if (e.target === self.canvas) {
1715
+ return; // handled by mouseup
1716
+ }
1717
+ self.selecting = undefined;
1718
+ self.cellBoundaryCrossed = true;
1719
+ self.rowBoundaryCrossed = true;
1720
+ self.columnBoundaryCrossed = true;
1721
+ };
1722
+ self.mouseup = function (e) {
1723
+ clearTimeout(self.scrollTimer);
1724
+ self.cellBoundaryCrossed = true;
1725
+ self.rowBoundaryCrossed = true;
1726
+ self.columnBoundaryCrossed = true;
1727
+ self.selecting = undefined;
1728
+ self.draggingItem = undefined;
1729
+ self.dragStartObject = undefined;
1730
+ if (
1731
+ self.dispatchEvent('mouseup', { NativeEvent: e, cell: self.currentCell })
1732
+ ) {
1733
+ return;
1734
+ }
1735
+ if (!self.hasFocus && e.target !== self.canvas) {
1736
+ return;
1737
+ }
1738
+ if (self.currentCell && self.currentCell.grid !== undefined) {
1739
+ return;
1740
+ }
1741
+ if (self.contextMenu || self.input) {
1742
+ return;
1743
+ }
1744
+ if (self.dragStart && self.isInGrid(self.dragStart)) {
1745
+ self.controlInput.focus();
1746
+ }
1747
+ e.preventDefault();
1748
+ };
1749
+ // gets the horizontal adjacent cells as well as first/last based on column visibility
1750
+ self.getAdjacentCells = function () {
1751
+ var x,
1752
+ i,
1753
+ s = self.getSchema(),
1754
+ o = {};
1755
+ for (x = 0; x < s.length; x += 1) {
1756
+ i = self.orders.columns[x];
1757
+ if (!s[i].hidden) {
1758
+ if (o.first === undefined) {
1759
+ o.first = x;
1760
+ o.left = x;
1761
+ }
1762
+ o.last = x;
1763
+ if (x > self.activeCell.columnIndex && o.right === undefined) {
1764
+ o.right = x;
1765
+ }
1766
+ if (x < self.activeCell.columnIndex) {
1767
+ o.left = x;
1768
+ }
1769
+ }
1770
+ }
1771
+ if (o.right === undefined) {
1772
+ o.right = o.last;
1773
+ }
1774
+ return o;
1775
+ };
1776
+ self.keydown = function (e) {
1777
+ var i,
1778
+ ev,
1779
+ adjacentCells = self.getAdjacentCells(),
1780
+ x = Math.max(self.activeCell.columnIndex, 0),
1781
+ y = Math.max(self.activeCell.rowIndex, 0),
1782
+ ctrl = e.ctrlKey || e.metaKey,
1783
+ last = self.viewData.length - 1,
1784
+ s = self.getSchema(),
1785
+ cols = s.length - 1;
1786
+
1787
+ var defaultPrevented = self.dispatchEvent('keydown', {
1788
+ NativeEvent: e,
1789
+ cell: self.currentCell,
1790
+ });
1791
+
1792
+ if (defaultPrevented) {
1793
+ return;
1794
+ }
1795
+
1796
+ if (!self.attributes.keepFocusOnMouseOut && !self.hasFocus) {
1797
+ return;
1798
+ }
1799
+
1800
+ // If a user starts typing content, switch to "Enter" mode
1801
+ if (isPrintableKeyEvent(e) && !ctrl) {
1802
+ return self.beginEditAt(x, y, e, true);
1803
+ }
1804
+
1805
+ if (self.attributes.showNewRow) {
1806
+ last += 1;
1807
+ }
1808
+
1809
+ if (e.key === 'Tab') {
1810
+ e.preventDefault();
1811
+ }
1812
+
1813
+ if (e.key === 'Escape') {
1814
+ self.selectNone();
1815
+ } else if (ctrl && e.key === 'a') {
1816
+ self.selectAll();
1817
+ } else if (['Backspace', 'Delete'].includes(e.key)) {
1818
+ // read-only grids must not lose data on Delete/Backspace (#587, #458)
1819
+ if (self.attributes.editable) {
1820
+ self.deleteSelectedData();
1821
+ }
1822
+ } else if (e.key === 'ArrowDown') {
1823
+ y += 1;
1824
+ } else if (e.key === 'ArrowUp') {
1825
+ y -= 1;
1826
+ } else if (
1827
+ (e.key === 'ArrowLeft' && !ctrl) ||
1828
+ (e.shiftKey && e.key === 'Tab')
1829
+ ) {
1830
+ x = adjacentCells.left;
1831
+ } else if (
1832
+ (e.key === 'ArrowRight' && !ctrl) ||
1833
+ (!e.shiftKey && e.key === 'Tab')
1834
+ ) {
1835
+ x = adjacentCells.right;
1836
+ } else if (e.key === 'PageUp') {
1837
+ y -= self.page;
1838
+ e.preventDefault();
1839
+ } else if (e.key === 'PageDown') {
1840
+ y += self.page;
1841
+ e.preventDefault();
1842
+ } else if (e.key === 'Home' || (ctrl && e.key === 'ArrowUp')) {
1843
+ y = 0;
1844
+ } else if (e.key === 'End' || (ctrl && e.key === 'ArrowDown')) {
1845
+ y = self.viewData.length - 1;
1846
+ } else if (ctrl && e.key === 'ArrowRight') {
1847
+ x = adjacentCells.last;
1848
+ } else if (ctrl && e.key === 'ArrowLeft') {
1849
+ x = adjacentCells.first;
1850
+ }
1851
+
1852
+ if (e.key === 'Enter') {
1853
+ e.preventDefault();
1854
+ return self.beginEditAt(x, y, e);
1855
+ }
1856
+ if (x < 0 || Number.isNaN(x)) {
1857
+ x = adjacentCells.first;
1858
+ }
1859
+ if (y > last) {
1860
+ y = last;
1861
+ }
1862
+ if (y < 0 || Number.isNaN(y)) {
1863
+ y = 0;
1864
+ }
1865
+ if (x > cols) {
1866
+ x = adjacentCells.last;
1867
+ }
1868
+
1869
+ // TODO - most likely some column order related bugs in key based selection
1870
+ // Arrows
1871
+ const isArrowKey = [
1872
+ 'ArrowLeft',
1873
+ 'ArrowUp',
1874
+ 'ArrowRight',
1875
+ 'ArrowDown',
1876
+ ].includes(e.key);
1877
+
1878
+ // Shrinking and expanding selection using shift and arrow keys
1879
+ if (e.shiftKey && isArrowKey && !self.attributes.singleSelectionMode) {
1880
+ const changed = self.shrinkOrExpandSelections(
1881
+ { rowIndex: y, columnIndex: x },
1882
+ e,
1883
+ true,
1884
+ );
1885
+ if (changed) self.draw(true);
1886
+ }
1887
+
1888
+ if (x !== self.activeCell.columnIndex || y !== self.activeCell.rowIndex) {
1889
+ self.scrollIntoView(
1890
+ x !== self.activeCell.columnIndex ? x : undefined,
1891
+ y !== self.activeCell.rowIndex && !Number.isNaN(y) ? y : undefined,
1892
+ );
1893
+
1894
+ self.setActiveCell(x, y);
1895
+ if (!e.shiftKey && self.attributes.selectionFollowsActiveCell) {
1896
+ if (!ctrl) self.clearSelections();
1897
+ self.selectCell({ rowIndex: y, viewColumnIndex: x });
1898
+ }
1899
+ self.draw(true);
1900
+ }
1901
+ };
1902
+ self.keyup = function (e) {
1903
+ if (
1904
+ self.dispatchEvent('keyup', { NativeEvent: e, cell: self.currentCell })
1905
+ ) {
1906
+ return;
1907
+ }
1908
+ if (!self.hasFocus) {
1909
+ return;
1910
+ }
1911
+ };
1912
+ self.keypress = function (e) {
1913
+ if (!self.hasFocus) {
1914
+ return;
1915
+ }
1916
+ if (
1917
+ self.dispatchEvent('keypress', { NativeEvent: e, cell: self.currentCell })
1918
+ ) {
1919
+ return;
1920
+ }
1921
+ };
1922
+ self.dblclick = function (e) {
1923
+ if (
1924
+ self.dispatchEvent('dblclick', { NativeEvent: e, cell: self.currentCell })
1925
+ ) {
1926
+ return;
1927
+ }
1928
+ if (!self.hasFocus) {
1929
+ return;
1930
+ }
1931
+ if (
1932
+ self.currentCell.context === 'ew-resize' &&
1933
+ self.currentCell.style === 'columnHeaderCell'
1934
+ ) {
1935
+ // Check that double-clicked cell is selected or part of selection.
1936
+ const currentCellIsSelected = self.isColumnSelected(
1937
+ self.currentCell.columnIndex,
1938
+ );
1939
+
1940
+ if (currentCellIsSelected) {
1941
+ // There might be more
1942
+ self.fitSelectedColumns();
1943
+ } else {
1944
+ self.fitColumnToValues(self.currentCell.header.name);
1945
+ }
1946
+ } else if (
1947
+ self.currentCell.context === 'ew-resize' &&
1948
+ self.currentCell.style === 'cornerCell'
1949
+ ) {
1950
+ self.autosize();
1951
+ } else if (
1952
+ ['cell', 'activeCell'].includes(self.currentCell.style) &&
1953
+ !self.hovers.onFilterButton &&
1954
+ !self.hovers.onCellTreeIcon
1955
+ ) {
1956
+ if (self.currentCell.isRowTree || self.currentCell.isColumnTree) {
1957
+ self.cellTreeExpandCollapse(
1958
+ self.currentCell.rowIndex,
1959
+ self.currentCell.columnIndex,
1960
+ );
1961
+ self.draw();
1962
+ } else {
1963
+ self.beginEditAt(
1964
+ self.currentCell.columnIndex,
1965
+ self.currentCell.rowIndex,
1966
+ );
1967
+ }
1968
+ }
1969
+ };
1970
+ self.scrollWheel = function (e) {
1971
+ var l,
1972
+ t,
1973
+ ev = e,
1974
+ deltaX = e.deltaX === undefined ? e.NativeEvent.deltaX : e.deltaX,
1975
+ deltaY = e.deltaY === undefined ? e.NativeEvent.deltaY : e.deltaY,
1976
+ deltaMode =
1977
+ e.deltaMode === undefined ? e.NativeEvent.deltaMode : e.deltaMode;
1978
+ if (e.NativeEvent) e = e.NativeEvent;
1979
+ if (wheeling) {
1980
+ ev.preventDefault(e);
1981
+ return;
1982
+ }
1983
+ if (self.dispatchEvent('wheel', { NativeEvent: e })) {
1984
+ return;
1985
+ }
1986
+ self.touchHaltAnimation = true;
1987
+ l = self.scrollBox.scrollLeft;
1988
+ t = self.scrollBox.scrollTop;
1989
+ if (self.hasFocus) {
1990
+ //BUG Issue 42: https://github.com/TonyGermaneri/canvas-datagrid/issues/42
1991
+ //https://stackoverflow.com/questions/20110224/what-is-the-height-of-a-line-in-a-wheel-event-deltamode-dom-delta-line
1992
+ if (deltaMode === 1) {
1993
+ // line mode = 17 pixels per line
1994
+ deltaY = deltaY * 17;
1995
+ }
1996
+ if (
1997
+ (self.scrollBox.scrollTop < self.scrollBox.scrollHeight &&
1998
+ deltaY > 0) ||
1999
+ (self.scrollBox.scrollLeft < self.scrollBox.scrollWidth &&
2000
+ deltaX > 0) ||
2001
+ (self.scrollBox.scrollTop > 0 && deltaY < 0) ||
2002
+ (self.scrollBox.scrollLeft > 0 && deltaX < 0)
2003
+ ) {
2004
+ ev.preventDefault(e);
2005
+ }
2006
+ wheeling = setTimeout(function () {
2007
+ wheeling = undefined;
2008
+ self.scrollBox.scrollTo(deltaX + l, deltaY + t);
2009
+ }, 1);
2010
+ }
2011
+ };
2012
+ self.pasteData = function (
2013
+ pasteValue,
2014
+ mimeType,
2015
+ startRowIndex,
2016
+ startColIndex,
2017
+ minRowsLength = 0,
2018
+ minColumnsLength = 0,
2019
+ ) {
2020
+ if (mimeType === 'text/html' && !isSupportedHtml(pasteValue)) {
2021
+ console.warn(
2022
+ 'Unrecognized HTML format. HTML must be a simple table, e.g.: <table><tr><td>data</td></tr></table>.',
2023
+ );
2024
+ console.warn(
2025
+ 'Data with the mime type text/html not in this format will not be imported as row data.',
2026
+ );
2027
+
2028
+ return;
2029
+ }
2030
+
2031
+ const rows = parseData(pasteValue, mimeType);
2032
+ const columnsLength = rows[0].length;
2033
+
2034
+ // selected cell. This mimics Excel's paste functionality, and works
2035
+ // as a poor-man's fill-down.
2036
+ if (
2037
+ rows.length === 1 &&
2038
+ columnsLength === 1 &&
2039
+ minRowsLength <= 1 &&
2040
+ minColumnsLength <= 1
2041
+ ) {
2042
+ var cellData = rows[0][0].value.map((item) => item.value).join();
2043
+
2044
+ self.forEachSelectedCell(function (data, rowIndex, colName) {
2045
+ data[rowIndex][colName] = cellData;
2046
+ });
2047
+ } else {
2048
+ let direction = null;
2049
+
2050
+ if (minRowsLength > rows.length && minColumnsLength > columnsLength) {
2051
+ direction = 'both';
2052
+ } else if (minRowsLength > rows.length) {
2053
+ direction = 'vertical';
2054
+ } else if (minColumnsLength > columnsLength) {
2055
+ direction = 'horizontal';
2056
+ }
2057
+
2058
+ self.insert({
2059
+ rows: rows,
2060
+ startRowIndex: startRowIndex,
2061
+ startColumnIndex: startColIndex,
2062
+ minRowsLength: minRowsLength,
2063
+ minColumnsLength: minColumnsLength,
2064
+ reverseRows: false,
2065
+ reverseColumns: false,
2066
+ clearSelections: true,
2067
+ alwaysFilling: false,
2068
+ direction: direction,
2069
+ });
2070
+ }
2071
+
2072
+ // selections is a sparse array, so we condense:
2073
+ const affectedCells = [];
2074
+ self.forEachSelectedCell((viewData, viewRowIndex, columnName, cell) => {
2075
+ affectedCells.push([
2076
+ viewRowIndex,
2077
+ cell.viewColumnIndex,
2078
+ cell.boundRowIndex,
2079
+ cell.boundColumnIndex,
2080
+ // the pasted value, so listeners need not look it up (#549)
2081
+ viewData[viewRowIndex] ? viewData[viewRowIndex][columnName] : undefined,
2082
+ ]);
2083
+ });
2084
+
2085
+ self.dispatchEvent('afterpaste', {
2086
+ cells: affectedCells,
2087
+ });
2088
+ self.dispatchDataChanged('paste', { cells: affectedCells });
2089
+
2090
+ return rows.length;
2091
+ };
2092
+ /**
2093
+ * Grows the data set and the schema so that `insert` can write every pasted
2094
+ * cell, adding all missing rows and columns in one pass with a single
2095
+ * refresh instead of calling `addRow`/`addColumn` per cell.
2096
+ * @param {Array} rows Parsed clipboard rows (see `parseData`).
2097
+ * @param {number} requiredRowCount Number of rows the grid needs to have.
2098
+ * @param {number} requiredColumnCount Number of columns the grid needs to have.
2099
+ */
2100
+ self.expandGridForInsert = function (
2101
+ rows,
2102
+ requiredRowCount,
2103
+ requiredColumnCount,
2104
+ ) {
2105
+ const schema = self.getSchema();
2106
+ let changed = false;
2107
+ const missingColumns = requiredColumnCount - schema.length;
2108
+ if (missingColumns > 0) {
2109
+ const lastColumn = schema[self.orders.columns[schema.length - 1]] || {};
2110
+ for (let i = 0; i < missingColumns; i += 1) {
2111
+ const column = {
2112
+ name: `col${schema.length + 1}:${Date.now()}${i}`,
2113
+ type: deduceColTypeFromData(rows, i),
2114
+ title: ' ',
2115
+ width: lastColumn.width,
2116
+ };
2117
+ self.validateColumn(column, schema);
2118
+ schema.push(column);
2119
+ self.originalData.forEach(function (row, rowIndex) {
2120
+ self.applyDefaultValue(row, column, rowIndex);
2121
+ });
2122
+ }
2123
+ self.intf.schema = schema;
2124
+ changed = true;
2125
+ }
2126
+ const missingRows = requiredRowCount - self.originalData.length;
2127
+ if (missingRows > 0) {
2128
+ for (let i = 0; i < missingRows; i += 1) {
2129
+ const row = {};
2130
+ self.originalData.push(row);
2131
+ self.getSchema().forEach(function (c) {
2132
+ self.applyDefaultValue(row, c, self.originalData.length - 1);
2133
+ });
2134
+ }
2135
+ changed = true;
2136
+ }
2137
+ if (changed) {
2138
+ // setFilter re-applies filters and sorting and calls refresh() once
2139
+ self.setFilter();
2140
+ self.resize(true);
2141
+ }
2142
+ return changed;
2143
+ };
2144
+ self.insert = function ({
2145
+ rows = [],
2146
+ startRowIndex = 0,
2147
+ startColumnIndex = 0,
2148
+ minRowsLength = 0,
2149
+ minColumnsLength = 0,
2150
+ reverseRows = false,
2151
+ reverseColumns = false,
2152
+ clearSelections = false,
2153
+ alwaysFilling = false,
2154
+ direction = 'both',
2155
+ }) {
2156
+ const rowsLength = Math.max(rows.length, minRowsLength);
2157
+ const fillCellCallback = self.fillCellCallback;
2158
+ const filledCells = [];
2159
+
2160
+ if (self.attributes.allowGridExpandOnPaste) {
2161
+ self.expandGridForInsert(
2162
+ rows,
2163
+ startRowIndex + rowsLength,
2164
+ startColumnIndex +
2165
+ Math.max(minColumnsLength, ...rows.map((cells) => cells.length)),
2166
+ );
2167
+ }
2168
+
2169
+ if (clearSelections) self.selections = [];
2170
+
2171
+ for (
2172
+ let rowPosReal = 0, rowDataPos = 0;
2173
+ rowPosReal < rowsLength;
2174
+ rowPosReal++, rowDataPos++
2175
+ ) {
2176
+ if (rowDataPos >= rows.length) {
2177
+ rowDataPos = 0;
2178
+ }
2179
+
2180
+ const fillingRow = rowPosReal >= rows.length;
2181
+ const rowPosition = reverseRows
2182
+ ? rowsLength - rowPosReal - 1
2183
+ : rowPosReal;
2184
+ // Rows may have been moved by user, so get the actual row index
2185
+ // (instead of the row index at which the row is rendered):
2186
+ if (self.originalData[startRowIndex + rowPosition] === undefined) {
2187
+ console.warn('Paste data exceeded grid bounds. Skipping.');
2188
+ break;
2189
+ }
2190
+ const realRowIndex = self.orders.rows[startRowIndex + rowPosition];
2191
+
2192
+ const cells = rows[rowDataPos];
2193
+ const cellsLength = Math.max(cells.length, minColumnsLength);
2194
+
2195
+ const existingRowData = self.viewData[realRowIndex];
2196
+ const newRowData = Object.assign({}, existingRowData);
2197
+ const fillArgs = fillCellCallback
2198
+ ? {
2199
+ rows: rows,
2200
+ direction: direction,
2201
+ rowData: newRowData,
2202
+ existingRowData: existingRowData,
2203
+ rowIndex: realRowIndex,
2204
+ rowOffset: rowDataPos,
2205
+ cells: cells,
2206
+ reversed: direction === 'horizontal' ? reverseColumns : reverseRows,
2207
+ isFillingRow: fillingRow || alwaysFilling,
2208
+ fillingRowPosition: alwaysFilling
2209
+ ? rowPosReal
2210
+ : fillingRow
2211
+ ? rowPosReal - rows.length
2212
+ : -1,
2213
+ fillingRowLength: alwaysFilling
2214
+ ? rowsLength
2215
+ : fillingRow
2216
+ ? rowsLength - rows.length
2217
+ : -1,
2218
+ }
2219
+ : undefined;
2220
+
2221
+ for (
2222
+ let colPosReal = 0, cellDataPos = 0;
2223
+ colPosReal < cellsLength;
2224
+ colPosReal++, cellDataPos++
2225
+ ) {
2226
+ if (cellDataPos >= cells.length) {
2227
+ cellDataPos = 0;
2228
+ }
2229
+
2230
+ const fillingColumn = colPosReal >= cells.length;
2231
+ const colPosition = reverseColumns
2232
+ ? cellsLength - colPosReal - 1
2233
+ : colPosReal;
2234
+ const columnIndex = startColumnIndex + colPosition;
2235
+ if (!self.getSchema()[columnIndex]) {
2236
+ console.warn('Paste data exceeded grid bounds. Skipping.');
2237
+ continue;
2238
+ }
2239
+ const column = self.getSchema()[self.orders.columns[columnIndex]];
2240
+ const columnName = column.name;
2241
+ let cellData = cells[cellDataPos];
2242
+ if (cellData && cellData.value) {
2243
+ cellData = cellData.value.map((item) => item.value).join('');
2244
+ }
2245
+
2246
+ const existingCellData = existingRowData[columnName];
2247
+
2248
+ if (
2249
+ fillCellCallback &&
2250
+ (fillingColumn || fillingRow || alwaysFilling)
2251
+ ) {
2252
+ newRowData[columnName] = fillCellCallback({
2253
+ ...fillArgs,
2254
+ column: column,
2255
+ columnIndex: columnIndex,
2256
+ columnOffset: cellDataPos,
2257
+ newCellData: cellData,
2258
+ existingCellData: existingCellData,
2259
+ isFillingColumn: fillingColumn || alwaysFilling,
2260
+ fillingColumnPosition: alwaysFilling
2261
+ ? colPosReal
2262
+ : fillingColumn
2263
+ ? colPosReal - cells.length
2264
+ : -1,
2265
+ fillingColumnLength: alwaysFilling
2266
+ ? cellsLength
2267
+ : fillingColumn
2268
+ ? cellsLength - cells.length
2269
+ : -1,
2270
+ });
2271
+ } else {
2272
+ newRowData[columnName] =
2273
+ cellData === undefined || cellData === null
2274
+ ? existingCellData
2275
+ : cellData;
2276
+ }
2277
+
2278
+ self.selectCell(
2279
+ { rowIndex: realRowIndex, viewColumnIndex: columnIndex },
2280
+ true,
2281
+ );
2282
+
2283
+ if (alwaysFilling || fillingRow || fillingColumn) {
2284
+ filledCells.push([
2285
+ realRowIndex,
2286
+ columnIndex,
2287
+ self.getBoundRowIndexFromViewRowIndex(realRowIndex),
2288
+ self.getBoundColumnIndexFromViewColumnIndex(columnIndex),
2289
+ ]);
2290
+ }
2291
+ }
2292
+
2293
+ self.originalData[self.boundRowIndexMap.get(realRowIndex)] = newRowData;
2294
+ // Update view date here to avoid a full refresh of `viewData`.
2295
+ // To stay in line with Excel and Google Sheet behaviour,
2296
+ // don't perform a full refresh (and filter/sort results)
2297
+ // as this would make any pasted values disappear and/or suddenly change position.
2298
+ self.viewData[realRowIndex] = newRowData;
2299
+ }
2300
+
2301
+ if (filledCells.length > 0 || alwaysFilling) {
2302
+ self.dispatchEvent('afterfill', {
2303
+ filledCells: filledCells,
2304
+ });
2305
+ }
2306
+ };
2307
+ self.getNextVisibleColumnIndex = function (visibleColumnIndex) {
2308
+ var x,
2309
+ s = self.getVisibleSchema();
2310
+ for (x = 0; x < s.length; x += 1) {
2311
+ if (s[x].columnIndex === visibleColumnIndex) {
2312
+ return s[x + 1].columnIndex;
2313
+ }
2314
+ }
2315
+ return -1;
2316
+ };
2317
+ self.getVisibleColumnIndexOf = function (columnIndex) {
2318
+ var x,
2319
+ s = self.getVisibleSchema();
2320
+ for (x = 0; x < s.length; x += 1) {
2321
+ if (s[x].columnIndex === columnIndex) {
2322
+ return x;
2323
+ }
2324
+ }
2325
+ return -1;
2326
+ };
2327
+ self.getSelectionIndex = function () {
2328
+ const bounds = self.getSelectionBounds(true);
2329
+ if (!bounds) return;
2330
+ const { top, bottom } = bounds;
2331
+ for (let rowIndex = top; rowIndex <= bottom; rowIndex++) {
2332
+ const row = self.getRowSelectionStates(rowIndex);
2333
+ if (row) {
2334
+ if (row[0] === undefined) break;
2335
+
2336
+ return {
2337
+ row: rowIndex,
2338
+ column: row[0] < 0 ? row[1] : row[0],
2339
+ rowLength: bottom - rowIndex + 1,
2340
+ columnLength: row.length - (row[0] < 0 ? 1 : 0),
2341
+ };
2342
+ }
2343
+ }
2344
+
2345
+ return null;
2346
+ };
2347
+ self.paste = function (event) {
2348
+ if (!self.attributes.editable) {
2349
+ return;
2350
+ }
2351
+
2352
+ var defaultPrevented = self.dispatchEvent('beforepaste', {
2353
+ NativeEvent: event,
2354
+ });
2355
+
2356
+ if (defaultPrevented) {
2357
+ return;
2358
+ }
2359
+
2360
+ var clipboardItems = new Map(
2361
+ Array.from(event.clipboardData.items).map((item) => [item.type, item]),
2362
+ );
2363
+
2364
+ // Supported MIME types, in order of preference:
2365
+ var supportedMimeTypes = ['text/html', 'text/csv', 'text/plain'];
2366
+
2367
+ // The clipboard will often contain the same data in multiple formats,
2368
+ // which can be used depending on the context in which it's pasted. Here
2369
+ // we'll prefere more structured (HTML/CSV) over less structured, when
2370
+ // available, so we try to find those first:
2371
+ var pasteableItems = supportedMimeTypes
2372
+ .map((mimeType) => clipboardItems.get(mimeType))
2373
+ .filter((item) => !!item); // filter out not-found MIME types (= undefined)
2374
+
2375
+ if (pasteableItems.length === 0) {
2376
+ console.warn(
2377
+ 'Cannot find supported clipboard data type. Supported types are:',
2378
+ supportedMimeTypes.join(', '),
2379
+ );
2380
+ return;
2381
+ }
2382
+
2383
+ const selectionIndex = self.getSelectionIndex();
2384
+ if (!selectionIndex) return;
2385
+
2386
+ var itemToPaste = pasteableItems[0];
2387
+
2388
+ // itemToPaste is cleared once accessed (getData or getAsString),
2389
+ // so we need to store the type here, before reading its value:
2390
+ var pasteType = itemToPaste.type;
2391
+
2392
+ itemToPaste.getAsString(function (pasteValue) {
2393
+ self.pasteData(
2394
+ pasteValue,
2395
+ pasteType,
2396
+ selectionIndex.row,
2397
+ selectionIndex.column,
2398
+ selectionIndex.rowLength,
2399
+ selectionIndex.columnLength,
2400
+ );
2401
+
2402
+ self.draw();
2403
+ });
2404
+ };
2405
+ self.cut = function (event) {
2406
+ if (self.dispatchEvent('cut', { NativeEvent: event })) {
2407
+ return;
2408
+ }
2409
+
2410
+ // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
2411
+ if (!self.hasFocus || !event.clipboardData) {
2412
+ return;
2413
+ }
2414
+
2415
+ if (!self.attributes.editable) {
2416
+ // A read-only grid can still put the selection on the clipboard,
2417
+ // but nothing is removed from the data (#587).
2418
+ self.copySelectedCellsToClipboard(event.clipboardData);
2419
+ event.preventDefault();
2420
+ return;
2421
+ }
2422
+
2423
+ self.copySelectedCellsToClipboard(event.clipboardData);
2424
+
2425
+ const affectedCells = self.clearSelectedCells();
2426
+ const apiCompatibleCells = affectedCells.map((cell) => {
2427
+ return [
2428
+ cell.viewRowIndex,
2429
+ cell.viewColumnIndex,
2430
+ cell.boundRowIndex,
2431
+ cell.boundColumnIndex,
2432
+ ];
2433
+ });
2434
+
2435
+ self.dispatchEvent('aftercut', {
2436
+ cells: apiCompatibleCells,
2437
+ });
2438
+ self.dispatchDataChanged('cut', { cells: apiCompatibleCells });
2439
+
2440
+ requestAnimationFrame(() => self.draw());
2441
+ event.preventDefault();
2442
+ };
2443
+ self.copy = function (event) {
2444
+ if (self.dispatchEvent('copy', { NativeEvent: event })) {
2445
+ return;
2446
+ }
2447
+
2448
+ // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
2449
+ if (!self.hasFocus || !event.clipboardData) {
2450
+ return;
2451
+ }
2452
+
2453
+ self.copySelectedCellsToClipboard(event.clipboardData);
2454
+
2455
+ event.preventDefault();
2456
+ };
2457
+ self.selectionHandleMove = function (e) {
2458
+ if (!self.movingSelectionHandle) {
2459
+ return;
2460
+ }
2461
+
2462
+ const clippingRect = self.getClippingRect(e);
2463
+ const rowIndex = self.currentCell.rowIndex;
2464
+ const columnIndex = self.currentCell.columnIndex;
2465
+ const isInSelectionBounds =
2466
+ rowIndex >= -1 &&
2467
+ columnIndex >= -1 &&
2468
+ self.fillOverlay.selection.left <= columnIndex &&
2469
+ self.fillOverlay.selection.right >= columnIndex &&
2470
+ self.fillOverlay.selection.top <= rowIndex &&
2471
+ self.fillOverlay.selection.bottom >= rowIndex;
2472
+
2473
+ self.fillOverlay.minX = clippingRect.x;
2474
+ self.fillOverlay.minY = clippingRect.y;
2475
+ self.fillOverlay.x = Math.max(clippingRect.x, e.clientX);
2476
+ self.fillOverlay.y = Math.max(clippingRect.y, e.clientY);
2477
+
2478
+ // If we are in the selection bounds, allow user to change directions.
2479
+ if (isInSelectionBounds) {
2480
+ self.fillOverlay.lastInBoundsLocation = {
2481
+ x: e.clientX,
2482
+ y: e.clientY,
2483
+ };
2484
+ } else if (
2485
+ self.fillOverlay.lastInBoundsLocation ||
2486
+ !self.fillOverlay.direction
2487
+ ) {
2488
+ const lastInBoundsLocation = self.fillOverlay.lastInBoundsLocation;
2489
+ self.fillOverlay.lastInBoundsLocation = undefined;
2490
+
2491
+ const x = lastInBoundsLocation
2492
+ ? lastInBoundsLocation.x
2493
+ : self.fillOverlay.handle.x;
2494
+ const y = lastInBoundsLocation
2495
+ ? lastInBoundsLocation.y
2496
+ : self.fillOverlay.handle.y;
2497
+ const dx = Math.abs(e.clientX - x);
2498
+ const dy = Math.abs(e.clientY - y);
2499
+
2500
+ if (dx > 5 || dy > 5) {
2501
+ self.fillOverlay.direction = dx > dy ? 'x' : 'y';
2502
+ }
2503
+ }
2504
+
2505
+ if (rowIndex >= 0) {
2506
+ self.fillOverlay.rowIndex = rowIndex;
2507
+ }
2508
+ if (columnIndex >= 0) {
2509
+ self.fillOverlay.columnIndex = columnIndex;
2510
+ if (rowIndex === -1 && self.visibleRows.length) {
2511
+ self.fillOverlay.rowIndex = self.visibleRows[0];
2512
+ }
2513
+ }
2514
+
2515
+ if (rowIndex >= -1 && columnIndex >= -1) {
2516
+ self.fillOverlay.snap = self.currentCell;
2517
+ } else {
2518
+ self.fillOverlay.snap = undefined;
2519
+ }
2520
+ };
2521
+ self.stopSelectionHandleMove = function (e) {
2522
+ if (!self.fillOverlay.handle) {
2523
+ return false;
2524
+ }
2525
+
2526
+ self.ignoreNextClick = true;
2527
+
2528
+ window.removeEventListener('mousemove', self.selectionHandleMove, false);
2529
+ window.removeEventListener('mouseup', self.stopSelectionHandleMove, false);
2530
+
2531
+ const overlay = self.fillOverlay;
2532
+ const selectionIndex = self.getSelectionIndex();
2533
+ const bounds = overlay.selection;
2534
+ if (!selectionIndex) return;
2535
+
2536
+ self.movingSelectionHandle = undefined;
2537
+ self.fillOverlay = {};
2538
+
2539
+ if (overlay.rowIndex >= 0 && overlay.columnIndex >= 0) {
2540
+ const boundsOld = { ...bounds };
2541
+ const isVertical = overlay.direction === 'y';
2542
+ const isHorizontal = overlay.direction === 'x';
2543
+
2544
+ let startRow,
2545
+ rowLength,
2546
+ startColumn,
2547
+ columnLength,
2548
+ reverseVertically = false,
2549
+ reverseHorizontally = false;
2550
+
2551
+ if (isVertical) {
2552
+ if (overlay.rowIndex < bounds.top) {
2553
+ bounds.top = overlay.rowIndex;
2554
+ reverseVertically = true;
2555
+ } else if (overlay.rowIndex > bounds.bottom) {
2556
+ bounds.bottom = overlay.rowIndex;
2557
+ }
2558
+
2559
+ startRow =
2560
+ bounds.top < boundsOld.top ? bounds.top : boundsOld.bottom + 1;
2561
+ rowLength =
2562
+ bounds.top < boundsOld.top
2563
+ ? boundsOld.top - bounds.top
2564
+ : bounds.bottom - boundsOld.bottom;
2565
+ startColumn = selectionIndex.column;
2566
+ columnLength = selectionIndex.columnLength;
2567
+ } else if (isHorizontal) {
2568
+ if (overlay.columnIndex < bounds.left) {
2569
+ bounds.left = overlay.columnIndex;
2570
+ reverseHorizontally = true;
2571
+ } else if (overlay.columnIndex > bounds.right) {
2572
+ bounds.right = overlay.columnIndex;
2573
+ }
2574
+
2575
+ startColumn =
2576
+ bounds.left < boundsOld.left ? bounds.left : boundsOld.right + 1;
2577
+ columnLength =
2578
+ bounds.left < boundsOld.left
2579
+ ? boundsOld.left - bounds.left
2580
+ : bounds.right - boundsOld.right;
2581
+ startRow = selectionIndex.row;
2582
+ rowLength = selectionIndex.rowLength;
2583
+ }
2584
+
2585
+ if (
2586
+ bounds.left <= bounds.right &&
2587
+ bounds.top <= bounds.bottom &&
2588
+ (bounds.left < boundsOld.left ||
2589
+ bounds.top < boundsOld.top ||
2590
+ bounds.right > boundsOld.right ||
2591
+ bounds.bottom > boundsOld.bottom)
2592
+ ) {
2593
+ const schema = self.getSchema();
2594
+ const rows = [];
2595
+
2596
+ for (
2597
+ let rowIndex = 0;
2598
+ rowIndex < Math.min(rowLength, selectionIndex.rowLength);
2599
+ rowIndex++
2600
+ ) {
2601
+ const rowData = self.viewData[selectionIndex.row + rowIndex];
2602
+
2603
+ rows[rowIndex] = [];
2604
+
2605
+ for (
2606
+ let columnIndex = 0;
2607
+ columnIndex < Math.min(columnLength, selectionIndex.columnLength);
2608
+ columnIndex++
2609
+ ) {
2610
+ const column = schema[selectionIndex.column + columnIndex];
2611
+ if (!column) continue;
2612
+
2613
+ const cellData = rowData[column.name];
2614
+
2615
+ rows[rowIndex][columnIndex] = cellData;
2616
+ }
2617
+ }
2618
+
2619
+ self.insert({
2620
+ rows: rows,
2621
+ startRowIndex: startRow,
2622
+ startColumnIndex: startColumn,
2623
+ minRowsLength: rowLength,
2624
+ minColumnsLength: columnLength,
2625
+ reverseRows: reverseVertically,
2626
+ reverseColumns: reverseHorizontally,
2627
+ clearSelections: false,
2628
+ alwaysFilling: true,
2629
+ direction: isHorizontal ? 'horizontal' : 'vertical',
2630
+ });
2631
+ self.dispatchDataChanged('fill');
2632
+
2633
+ self.draw();
2634
+ }
2635
+ }
2636
+
2637
+ return true;
2638
+ };
2639
+ return;
2640
+ }