canvas-datagrid 0.3.22 → 0.4.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.
@@ -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 = {
@@ -549,12 +605,20 @@ export default function (self) {
549
605
  }
550
606
  self.draw();
551
607
  return;
608
+ } else if (
609
+ cell.hovered &&
610
+ self.hovers.onCellTreeIcon &&
611
+ e.type == 'mousedown'
612
+ ) {
613
+ self.toggleCollapseTree(cell.rowIndex, cell.columnIndex);
614
+ return;
552
615
  } else {
553
616
  self.selectedFilterButton = {
554
617
  columnIndex: -1,
555
618
  rowIndex: -1,
556
619
  };
557
620
  if (self.hovers.onFilterButton) return;
621
+ if (self.hovers.onCellTreeIcon) return;
558
622
  if (!self.dragAddToSelection && cell.rowIndex !== undefined) {
559
623
  if (
560
624
  self.selections[cell.rowIndex] &&
@@ -627,6 +691,12 @@ export default function (self) {
627
691
  }
628
692
  }
629
693
  }
694
+ const columnGroup = self.getColumnGroupAt(self.mouse.x, self.mouse.y);
695
+ if (columnGroup) self.cursor = 'pointer';
696
+
697
+ const rowGroup = self.getRowGroupAt(self.mouse.x, self.mouse.y);
698
+ if (rowGroup) self.cursor = 'pointer';
699
+
630
700
  self.cellBoundaryCrossed = false;
631
701
  self.rowBoundaryCrossed = false;
632
702
  self.columnBoundaryCrossed = false;
@@ -675,9 +745,25 @@ export default function (self) {
675
745
  ) {
676
746
  return;
677
747
  }
748
+
749
+ let group = self.getColumnGroupAt(pos.x, pos.y);
750
+ if (!group) group = self.getRowGroupAt(pos.x, pos.y);
751
+ if (group) {
752
+ if (self.toggleGroup(group)) {
753
+ self.setStorageData();
754
+ self.refresh();
755
+ return;
756
+ }
757
+ }
758
+
678
759
  if (!self.hasFocus) {
679
760
  return;
680
761
  }
762
+
763
+ const leftOffset = self.getRowGroupAreaWidth();
764
+ const topOffset = self.getColumnGroupAreaHeight();
765
+ const xInGrid = pos.x - leftOffset;
766
+ const yInGrid = pos.y - topOffset;
681
767
  if (
682
768
  ['rowHeaderCell', 'columnHeaderCell'].indexOf(self.currentCell.style) ===
683
769
  -1 &&
@@ -715,17 +801,17 @@ export default function (self) {
715
801
  if (
716
802
  self.currentCell.style === 'rowHeaderCell' &&
717
803
  self.attributes.tree &&
718
- pos.x > 0 &&
719
- pos.x - self.currentCell.x <
804
+ xInGrid > 0 &&
805
+ xInGrid - self.currentCell.x <
720
806
  self.style.treeArrowWidth +
721
807
  self.style.treeArrowMarginLeft +
722
808
  self.style.treeArrowMarginRight +
723
809
  self.style.treeArrowClickRadius &&
724
- pos.y - self.currentCell.y <
810
+ yInGrid - self.currentCell.y <
725
811
  self.style.treeArrowHeight +
726
812
  self.style.treeArrowMarginTop +
727
813
  self.style.treeArrowClickRadius &&
728
- pos.y > 0
814
+ yInGrid > 0
729
815
  ) {
730
816
  self.toggleTree(i.rowIndex);
731
817
  return;
@@ -765,7 +851,6 @@ export default function (self) {
765
851
  self.dragMode === 'ew-resize'
766
852
  ) {
767
853
  self.resize(true);
768
- self.scrollBox.scrollLeft += x;
769
854
  }
770
855
  if (self.dragMode === 'ew-resize') {
771
856
  self.sizes.columns[
@@ -797,30 +882,24 @@ export default function (self) {
797
882
  }
798
883
  self.ellipsisCache = {};
799
884
  };
800
- self.stopDragResize = function (e) {
801
- const pos = self.getLayerPos(e);
802
- const hasMoved = !!(pos.x - self.dragStart.x);
803
- const width = self.resizingStartingWidth + pos.x - self.dragStart.x;
885
+ self.stopDragResize = function (event) {
886
+ const pos = self.getLayerPos(event);
804
887
 
805
888
  if (self.dragMode === 'ew-resize') {
806
- const selectedColumns = self.selections[0];
889
+ const hasMoved = !!(pos.x - self.dragStart.x);
890
+ // Check that dragItem is selected or part of selection.
807
891
  const dragItemIsSelected = self.isColumnSelected(
808
892
  self.dragItem.columnIndex,
809
893
  );
810
894
 
811
- if (dragItemIsSelected)
812
- for (const selectedColumnIndex of selectedColumns) {
813
- const boundColumnIndex = self.getBoundColumnIndexFromViewColumnIndex(
814
- selectedColumnIndex,
815
- );
816
-
817
- if (
818
- hasMoved &&
819
- dragItemIsSelected &&
820
- self.isColumnSelected(selectedColumnIndex)
821
- )
822
- self.sizes.columns[boundColumnIndex] = width;
823
- }
895
+ if (hasMoved && dragItemIsSelected) {
896
+ const width = Math.max(
897
+ self.resizingStartingWidth + pos.x - self.dragStart.x,
898
+ self.style.minColumnWidth,
899
+ );
900
+ // If the column is selected, resize it to width plus any other selected columns.
901
+ self.fitSelectedColumns(width);
902
+ }
824
903
  }
825
904
 
826
905
  self.resize();
@@ -981,6 +1060,7 @@ export default function (self) {
981
1060
  -1,
982
1061
  self.activeCell.rowIndex,
983
1062
  );
1063
+ if (!self.reorderObject) return;
984
1064
  self.reorderTarget = self.currentCell;
985
1065
  self.reorderObject.dragOffset = {
986
1066
  x: x,
@@ -1132,6 +1212,8 @@ export default function (self) {
1132
1212
 
1133
1213
  self.ignoreNextClick = true;
1134
1214
  self.freezeMarkerPosition = pos;
1215
+ pos.x -= self.getRowGroupAreaWidth();
1216
+ pos.y -= self.getColumnGroupAreaHeight();
1135
1217
  if (
1136
1218
  self.currentCell &&
1137
1219
  self.currentCell.rowIndex !== undefined &&
@@ -1652,9 +1734,21 @@ export default function (self) {
1652
1734
  self.autosize();
1653
1735
  } else if (
1654
1736
  ['cell', 'activeCell'].includes(self.currentCell.style) &&
1655
- !self.hovers.onFilterButton
1737
+ !self.hovers.onFilterButton &&
1738
+ !self.hovers.onCellTreeIcon
1656
1739
  ) {
1657
- self.beginEditAt(self.currentCell.columnIndex, self.currentCell.rowIndex);
1740
+ if (self.currentCell.isRowTree || self.currentCell.isColumnTree) {
1741
+ self.cellTreeExpandCollapse(
1742
+ self.currentCell.rowIndex,
1743
+ self.currentCell.columnIndex,
1744
+ );
1745
+ self.draw();
1746
+ } else {
1747
+ self.beginEditAt(
1748
+ self.currentCell.columnIndex,
1749
+ self.currentCell.rowIndex,
1750
+ );
1751
+ }
1658
1752
  }
1659
1753
  };
1660
1754
  self.scrollWheel = function (e) {
@@ -1876,17 +1970,17 @@ export default function (self) {
1876
1970
  self.draw();
1877
1971
  });
1878
1972
  };
1879
- self.cut = function (e) {
1880
- if (self.dispatchEvent('cut', { NativeEvent: e })) {
1973
+ self.cut = function (event) {
1974
+ if (self.dispatchEvent('cut', { NativeEvent: event })) {
1881
1975
  return;
1882
1976
  }
1883
1977
 
1884
1978
  // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
1885
- if (!self.hasFocus || !e.clipboardData) {
1979
+ if (!self.hasFocus || !event.clipboardData) {
1886
1980
  return;
1887
1981
  }
1888
1982
 
1889
- self.copySelectedCellsToClipboard(e.clipboardData);
1983
+ self.copySelectedCellsToClipboard(event.clipboardData);
1890
1984
 
1891
1985
  const affectedCells = self.clearSelectedCells();
1892
1986
 
@@ -1895,20 +1989,21 @@ export default function (self) {
1895
1989
  });
1896
1990
 
1897
1991
  requestAnimationFrame(() => self.draw());
1992
+ event.preventDefault();
1898
1993
  };
1899
- self.copy = function (e) {
1900
- if (self.dispatchEvent('copy', { NativeEvent: e })) {
1994
+ self.copy = function (event) {
1995
+ if (self.dispatchEvent('copy', { NativeEvent: event })) {
1901
1996
  return;
1902
1997
  }
1903
1998
 
1904
1999
  // Expecting instance of `ClipboardEvent` with `clipboardData` attribute
1905
- if (!self.hasFocus || !e.clipboardData) {
2000
+ if (!self.hasFocus || !event.clipboardData) {
1906
2001
  return;
1907
2002
  }
1908
2003
 
1909
- self.copySelectedCellsToClipboard(e.clipboardData);
2004
+ self.copySelectedCellsToClipboard(event.clipboardData);
1910
2005
 
1911
- e.preventDefault();
2006
+ event.preventDefault();
1912
2007
  };
1913
2008
  return;
1914
2009
  }
@@ -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
  )