canvas-datagrid 0.3.23 → 0.4.1

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.
@@ -130,6 +130,8 @@ export default function (self) {
130
130
  l = (self.viewData || []).length,
131
131
  columnHeaderCellHeight = self.getColumnHeaderCellHeight(),
132
132
  rowHeaderCellWidth = self.getRowHeaderCellWidth(),
133
+ topGroupAreaHeight = self.getColumnGroupAreaHeight(),
134
+ leftGroupAreaWidth = self.getRowGroupAreaWidth(),
133
135
  ch = self.style.cellHeight,
134
136
  s = self.getSchema();
135
137
  // sets actual DOM canvas element
@@ -148,8 +150,10 @@ export default function (self) {
148
150
  self.scrollBox.verticalBoxVisible = dataHeight > self.scrollBox.height;
149
151
  }
150
152
  function setScrollBoxSize() {
151
- self.scrollBox.width = self.width - rowHeaderCellWidth;
152
- self.scrollBox.height = self.height - columnHeaderCellHeight;
153
+ self.scrollBox.width =
154
+ self.width - rowHeaderCellWidth - leftGroupAreaWidth;
155
+ self.scrollBox.height =
156
+ self.height - columnHeaderCellHeight - topGroupAreaHeight;
153
157
  }
154
158
  function setCanvasSize() {
155
159
  if (self.isChildGrid) {
@@ -157,8 +161,13 @@ export default function (self) {
157
161
  }
158
162
  dims = {
159
163
  // HACK +1 ? maybe it's a magic cell border? Required to line up properly in auto height mode.
160
- height: columnHeaderCellHeight + dataHeight + cellBorder + 1,
161
- width: dataWidth + rowHeaderCellWidth + cellBorder,
164
+ height:
165
+ columnHeaderCellHeight +
166
+ topGroupAreaHeight +
167
+ dataHeight +
168
+ cellBorder +
169
+ 1,
170
+ width: dataWidth + rowHeaderCellWidth + cellBorder + leftGroupAreaWidth,
162
171
  };
163
172
  ['width', 'height'].forEach(function (dim) {
164
173
  //TODO: support inherit
@@ -190,18 +199,31 @@ export default function (self) {
190
199
  if (l > 1) {
191
200
  self.scrollCache.y[x] = dataHeight;
192
201
  }
202
+ const collapsedColumnGroups = self.getCollapsedColumnGroups();
203
+ const isColumnCollapsed = (columnIndex) =>
204
+ collapsedColumnGroups.findIndex(
205
+ (group) => columnIndex >= group.from && columnIndex <= group.to,
206
+ ) >= 0;
207
+ /** @type {number} it will be used in `reduceSchema` only */
208
+ let frozenWidth = 0;
193
209
  dataWidth =
194
210
  s.reduce(function reduceSchema(accumulator, column, columnIndex) {
195
211
  // intentional redefintion of column. This causes scrollCache to be in the correct order
196
- column = s[self.orders.columns[columnIndex]];
197
- if (column.hidden) {
212
+ const schemaIndex = self.orders.columns[columnIndex];
213
+ const columnWidth = self.getColumnWidth(schemaIndex);
214
+ column = s[schemaIndex];
215
+ if (!column.hidden && !isColumnCollapsed(columnIndex))
216
+ accumulator += columnWidth;
217
+ if (columnIndex < self.frozenColumn) {
198
218
  self.scrollCache.x[columnIndex] = accumulator;
199
- return accumulator;
219
+ frozenWidth = accumulator;
220
+ } else {
221
+ self.scrollCache.x[columnIndex] = Math.max(
222
+ frozenWidth + columnWidth,
223
+ accumulator,
224
+ );
200
225
  }
201
- var va =
202
- accumulator + self.getColumnWidth(self.orders.columns[columnIndex]);
203
- self.scrollCache.x[columnIndex] = va;
204
- return va;
226
+ return accumulator;
205
227
  }, 0) || 0;
206
228
  if (self.attributes.showNewRow) {
207
229
  dataHeight += ch;
@@ -224,8 +246,9 @@ export default function (self) {
224
246
  }
225
247
  /// calculate scroll bar dimensions
226
248
  // non-controversial
227
- self.scrollBox.top = columnHeaderCellHeight + columnHeaderCellBorder;
228
- self.scrollBox.left = rowHeaderCellWidth;
249
+ self.scrollBox.top =
250
+ columnHeaderCellHeight + topGroupAreaHeight + columnHeaderCellBorder;
251
+ self.scrollBox.left = rowHeaderCellWidth + leftGroupAreaWidth;
229
252
  // width and height of scroll box
230
253
  setScrollBoxSize();
231
254
  // is the data larger than the scroll box
@@ -274,6 +297,7 @@ export default function (self) {
274
297
  self.scrollBox.heightBoxRatio =
275
298
  (self.scrollBox.height -
276
299
  columnHeaderCellHeight -
300
+ topGroupAreaHeight -
277
301
  self.scrollCache.y[self.frozenRow]) /
278
302
  dataHeight;
279
303
  self.scrollBox.scrollBoxHeight =
@@ -291,9 +315,14 @@ export default function (self) {
291
315
  );
292
316
  // horizontal
293
317
  n.x += rowHeaderCellWidth;
294
- n.y += self.height - self.style.scrollBarWidth - d;
318
+ n.y += self.height - self.style.scrollBarWidth - d - topGroupAreaHeight;
295
319
  n.width =
296
- self.width - self.style.scrollBarWidth - rowHeaderCellWidth - d - m;
320
+ self.width -
321
+ self.style.scrollBarWidth -
322
+ rowHeaderCellWidth -
323
+ leftGroupAreaWidth -
324
+ d -
325
+ m;
297
326
  n.height = self.style.scrollBarWidth + self.style.scrollBarBorderWidth + d;
298
327
  // horizontal box
299
328
  nb.y = n.y + self.style.scrollBarBoxMargin;
@@ -302,13 +331,19 @@ export default function (self) {
302
331
  // vertical
303
332
  v.x +=
304
333
  self.width -
334
+ leftGroupAreaWidth -
305
335
  self.style.scrollBarWidth -
306
336
  self.style.scrollBarBorderWidth -
307
337
  d;
308
338
  v.y += columnHeaderCellHeight + self.scrollCache.y[self.frozenRow];
309
339
  v.width = self.style.scrollBarWidth + self.style.scrollBarBorderWidth + d;
310
340
  v.height =
311
- self.height - columnHeaderCellHeight - self.style.scrollBarWidth - d - m;
341
+ self.height -
342
+ columnHeaderCellHeight -
343
+ topGroupAreaHeight -
344
+ self.style.scrollBarWidth -
345
+ d -
346
+ m;
312
347
  // vertical box
313
348
  vb.x = v.x + self.style.scrollBarBoxMargin;
314
349
  vb.y += self.scrollCache.y[self.frozenRow];
@@ -469,6 +504,7 @@ export default function (self) {
469
504
  rowIndex: cell.rowIndex,
470
505
  columnIndex: cell.columnIndex,
471
506
  onFilterButton: false,
507
+ onCellTreeIcon: false,
472
508
  };
473
509
  if (
474
510
  cell.isFilterable &&
@@ -488,6 +524,26 @@ export default function (self) {
488
524
  self.hovers.onFilterButton = true;
489
525
  self.draw();
490
526
  }
527
+ if (cell.isRowTree || cell.isColumnTree) {
528
+ const pc = cell.isRowTree
529
+ ? self.cellTree.rows[cell.rowIndex].parentCount
530
+ : 0;
531
+ const rc = self.style.cellTreeIconWidth * self.scale,
532
+ rx =
533
+ cell.x +
534
+ cell.paddingLeft +
535
+ self.canvasOffsetLeft +
536
+ self.style.cellTreeIconMarginLeft +
537
+ pc * (rc + cell.paddingLeft),
538
+ ry =
539
+ cell.y +
540
+ self.canvasOffsetTop +
541
+ self.style.cellTreeIconMarginTop * self.scale;
542
+ if (x >= rx && x <= rx + rc && y >= ry && y < ry + rc) {
543
+ self.hovers.onCellTreeIcon = true;
544
+ self.draw();
545
+ }
546
+ }
491
547
  }
492
548
  if (self.selecting || self.reorderObject) {
493
549
  delta = {
@@ -512,7 +568,24 @@ export default function (self) {
512
568
  dragBounds.right = s.length - 1;
513
569
  dragBounds.top = Math.min(sBounds.top, cell.rowIndex);
514
570
  dragBounds.bottom = Math.max(sBounds.bottom, cell.rowIndex);
571
+ if (dragBounds.top < 0) dragBounds.top = 0;
572
+ }
573
+
574
+ if (self.dragStartObject.rowIndex === -1) {
575
+ sBounds = self.getSelectionBounds();
576
+ dragBounds.left =
577
+ cell.columnIndex === undefined
578
+ ? sBounds.left
579
+ : Math.min(sBounds.left, cell.columnIndex);
580
+ dragBounds.right =
581
+ cell.columnIndex === undefined
582
+ ? sBounds.right
583
+ : Math.max(sBounds.right, cell.columnIndex);
584
+ dragBounds.top = -1;
585
+ dragBounds.bottom = self.viewData.length - 1;
586
+ if (dragBounds.left < 0) dragBounds.left = 0;
515
587
  }
588
+
516
589
  if (
517
590
  self.dragStartObject.rowIndex !== cell.rowIndex ||
518
591
  self.dragStartObject.columnIndex !== cell.columnIndex
@@ -525,11 +598,31 @@ export default function (self) {
525
598
  self.attributes.selectionMode === 'row'
526
599
  ) {
527
600
  if (
601
+ cell.rowIndex !== undefined &&
528
602
  (self.attributes.selectionMode === 'row' ||
529
603
  self.dragStartObject.columnIndex === -1) &&
530
604
  self.rowBoundaryCrossed
531
605
  ) {
532
- self.selectRow(cell.rowIndex, ctrl, null, true);
606
+ if (self.dragStartObject.rowIndex < cell.rowIndex) {
607
+ dragBounds.top = self.dragStartObject.rowIndex;
608
+ dragBounds.bottom = cell.rowIndex;
609
+ } else {
610
+ dragBounds.top = cell.rowIndex;
611
+ dragBounds.bottom = self.dragStartObject.rowIndex;
612
+ }
613
+ } else if (
614
+ cell.rowIndex !== undefined &&
615
+ (self.attributes.selectionMode === 'column' ||
616
+ self.dragStartObject.rowIndex === -1) &&
617
+ self.columnBoundaryCrossed
618
+ ) {
619
+ if (self.dragStartObject.columnIndex < cell.columnIndex) {
620
+ dragBounds.left = self.dragStartObject.columnIndex;
621
+ dragBounds.right = cell.columnIndex;
622
+ } else {
623
+ dragBounds.left = cell.columnIndex;
624
+ dragBounds.right = self.dragStartObject.columnIndex;
625
+ }
533
626
  } else if (self.attributes.selectionMode !== 'row') {
534
627
  if (cell.hovered && self.hovers.onFilterButton) {
535
628
  if (cell.openedFilter) {
@@ -549,12 +642,20 @@ export default function (self) {
549
642
  }
550
643
  self.draw();
551
644
  return;
645
+ } else if (
646
+ cell.hovered &&
647
+ self.hovers.onCellTreeIcon &&
648
+ e.type == 'mousedown'
649
+ ) {
650
+ self.toggleCollapseTree(cell.rowIndex, cell.columnIndex);
651
+ return;
552
652
  } else {
553
653
  self.selectedFilterButton = {
554
654
  columnIndex: -1,
555
655
  rowIndex: -1,
556
656
  };
557
657
  if (self.hovers.onFilterButton) return;
658
+ if (self.hovers.onCellTreeIcon) return;
558
659
  if (!self.dragAddToSelection && cell.rowIndex !== undefined) {
559
660
  if (
560
661
  self.selections[cell.rowIndex] &&
@@ -627,6 +728,12 @@ export default function (self) {
627
728
  }
628
729
  }
629
730
  }
731
+ const columnGroup = self.getColumnGroupAt(self.mouse.x, self.mouse.y);
732
+ if (columnGroup) self.cursor = 'pointer';
733
+
734
+ const rowGroup = self.getRowGroupAt(self.mouse.x, self.mouse.y);
735
+ if (rowGroup) self.cursor = 'pointer';
736
+
630
737
  self.cellBoundaryCrossed = false;
631
738
  self.rowBoundaryCrossed = false;
632
739
  self.columnBoundaryCrossed = false;
@@ -675,9 +782,25 @@ export default function (self) {
675
782
  ) {
676
783
  return;
677
784
  }
785
+
786
+ let group = self.getColumnGroupAt(pos.x, pos.y);
787
+ if (!group) group = self.getRowGroupAt(pos.x, pos.y);
788
+ if (group) {
789
+ if (self.toggleGroup(group)) {
790
+ self.setStorageData();
791
+ self.refresh();
792
+ return;
793
+ }
794
+ }
795
+
678
796
  if (!self.hasFocus) {
679
797
  return;
680
798
  }
799
+
800
+ const leftOffset = self.getRowGroupAreaWidth();
801
+ const topOffset = self.getColumnGroupAreaHeight();
802
+ const xInGrid = pos.x - leftOffset;
803
+ const yInGrid = pos.y - topOffset;
681
804
  if (
682
805
  ['rowHeaderCell', 'columnHeaderCell'].indexOf(self.currentCell.style) ===
683
806
  -1 &&
@@ -715,17 +838,17 @@ export default function (self) {
715
838
  if (
716
839
  self.currentCell.style === 'rowHeaderCell' &&
717
840
  self.attributes.tree &&
718
- pos.x > 0 &&
719
- pos.x - self.currentCell.x <
841
+ xInGrid > 0 &&
842
+ xInGrid - self.currentCell.x <
720
843
  self.style.treeArrowWidth +
721
844
  self.style.treeArrowMarginLeft +
722
845
  self.style.treeArrowMarginRight +
723
846
  self.style.treeArrowClickRadius &&
724
- pos.y - self.currentCell.y <
847
+ yInGrid - self.currentCell.y <
725
848
  self.style.treeArrowHeight +
726
849
  self.style.treeArrowMarginTop +
727
850
  self.style.treeArrowClickRadius &&
728
- pos.y > 0
851
+ yInGrid > 0
729
852
  ) {
730
853
  self.toggleTree(i.rowIndex);
731
854
  return;
@@ -765,7 +888,6 @@ export default function (self) {
765
888
  self.dragMode === 'ew-resize'
766
889
  ) {
767
890
  self.resize(true);
768
- self.scrollBox.scrollLeft += x;
769
891
  }
770
892
  if (self.dragMode === 'ew-resize') {
771
893
  self.sizes.columns[
@@ -975,6 +1097,7 @@ export default function (self) {
975
1097
  -1,
976
1098
  self.activeCell.rowIndex,
977
1099
  );
1100
+ if (!self.reorderObject) return;
978
1101
  self.reorderTarget = self.currentCell;
979
1102
  self.reorderObject.dragOffset = {
980
1103
  x: x,
@@ -1126,6 +1249,8 @@ export default function (self) {
1126
1249
 
1127
1250
  self.ignoreNextClick = true;
1128
1251
  self.freezeMarkerPosition = pos;
1252
+ pos.x -= self.getRowGroupAreaWidth();
1253
+ pos.y -= self.getColumnGroupAreaHeight();
1129
1254
  if (
1130
1255
  self.currentCell &&
1131
1256
  self.currentCell.rowIndex !== undefined &&
@@ -1284,6 +1409,12 @@ export default function (self) {
1284
1409
  self.dragStartObject.rowIndex > -1
1285
1410
  ) {
1286
1411
  self.selectRow(self.dragStartObject.rowIndex, ctrl, null);
1412
+ } else if (
1413
+ (self.attributes.selectionMode === 'column' ||
1414
+ self.dragStartObject.rowIndex === -1) &&
1415
+ self.dragStartObject.columnIndex > -1
1416
+ ) {
1417
+ self.selectColumn(self.currentCell.header.index, ctrl, e.shiftKey);
1287
1418
  } else if (self.attributes.selectionMode !== 'row') {
1288
1419
  self.mousemove(e);
1289
1420
  }
@@ -1646,9 +1777,21 @@ export default function (self) {
1646
1777
  self.autosize();
1647
1778
  } else if (
1648
1779
  ['cell', 'activeCell'].includes(self.currentCell.style) &&
1649
- !self.hovers.onFilterButton
1780
+ !self.hovers.onFilterButton &&
1781
+ !self.hovers.onCellTreeIcon
1650
1782
  ) {
1651
- self.beginEditAt(self.currentCell.columnIndex, self.currentCell.rowIndex);
1783
+ if (self.currentCell.isRowTree || self.currentCell.isColumnTree) {
1784
+ self.cellTreeExpandCollapse(
1785
+ self.currentCell.rowIndex,
1786
+ self.currentCell.columnIndex,
1787
+ );
1788
+ self.draw();
1789
+ } else {
1790
+ self.beginEditAt(
1791
+ self.currentCell.columnIndex,
1792
+ self.currentCell.rowIndex,
1793
+ );
1794
+ }
1652
1795
  }
1653
1796
  };
1654
1797
  self.scrollWheel = function (e) {
@@ -1870,17 +2013,17 @@ export default function (self) {
1870
2013
  self.draw();
1871
2014
  });
1872
2015
  };
1873
- self.cut = function (e) {
1874
- if (self.dispatchEvent('cut', { NativeEvent: e })) {
2016
+ self.cut = function (event) {
2017
+ if (self.dispatchEvent('cut', { NativeEvent: event })) {
1875
2018
  return;
1876
2019
  }
1877
2020
 
1878
2021
  // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
1879
- if (!self.hasFocus || !e.clipboardData) {
2022
+ if (!self.hasFocus || !event.clipboardData) {
1880
2023
  return;
1881
2024
  }
1882
2025
 
1883
- self.copySelectedCellsToClipboard(e.clipboardData);
2026
+ self.copySelectedCellsToClipboard(event.clipboardData);
1884
2027
 
1885
2028
  const affectedCells = self.clearSelectedCells();
1886
2029
 
@@ -1889,20 +2032,21 @@ export default function (self) {
1889
2032
  });
1890
2033
 
1891
2034
  requestAnimationFrame(() => self.draw());
2035
+ event.preventDefault();
1892
2036
  };
1893
- self.copy = function (e) {
1894
- if (self.dispatchEvent('copy', { NativeEvent: e })) {
2037
+ self.copy = function (event) {
2038
+ if (self.dispatchEvent('copy', { NativeEvent: event })) {
1895
2039
  return;
1896
2040
  }
1897
2041
 
1898
2042
  // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
1899
- if (!self.hasFocus || !e.clipboardData) {
2043
+ if (!self.hasFocus || !event.clipboardData) {
1900
2044
  return;
1901
2045
  }
1902
2046
 
1903
- self.copySelectedCellsToClipboard(e.clipboardData);
2047
+ self.copySelectedCellsToClipboard(event.clipboardData);
1904
2048
 
1905
- e.preventDefault();
2049
+ event.preventDefault();
1906
2050
  };
1907
2051
  return;
1908
2052
  }
@@ -86,12 +86,13 @@ const parseData = function (data, mimeType) {
86
86
  return parseText(data);
87
87
  };
88
88
 
89
- const htmlSafe = (value) => {
90
- if (typeof value === 'number') return value;
89
+ const htmlSafe = function (value) {
90
+ if (typeof value !== 'string') return value;
91
+
91
92
  return value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
92
93
  };
93
94
 
94
- const createTextString = (selectedData, isNeat) => {
95
+ const createTextString = function (selectedData, isNeat) {
95
96
  // Selected like [[0, 1], [0, 1]] of [[0, 3]] is neat; Selected like [[0, 1], [1, 2]] is untidy.
96
97
  // If not isNeat we just return a simple string of concatenated values.
97
98
  if (!isNeat)
@@ -101,7 +102,7 @@ const createTextString = (selectedData, isNeat) => {
101
102
  return selectedData.map((row) => Object.values(row).join('\t')).join('\n');
102
103
  };
103
104
 
104
- const createHTMLString = (selectedData, isNeat) => {
105
+ const createHTMLString = function (selectedData, isNeat) {
105
106
  if (!isNeat) return createTextString(selectedData, isNeat);
106
107
 
107
108
  // If isNeat, we can create a HTML table with the selected data.
@@ -111,7 +112,7 @@ const createHTMLString = (selectedData, isNeat) => {
111
112
  (row) =>
112
113
  '<tr>' +
113
114
  Object.values(row)
114
- .map((value) => '<td>' + htmlSafe(value) + '</td>')
115
+ .map((value) => ['<td>', htmlSafe(value), '</td>'].join(''))
115
116
  .join('') +
116
117
  '</tr>',
117
118
  )