canvas-datagrid 0.4.2 → 0.4.3

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.
@@ -74,6 +74,43 @@ export default function (self) {
74
74
  1),
75
75
  );
76
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
+ };
77
114
  self.resize = function (drawAfterResize) {
78
115
  if (!self.canvas) {
79
116
  return;
@@ -132,8 +169,7 @@ export default function (self) {
132
169
  rowHeaderCellWidth = self.getRowHeaderCellWidth(),
133
170
  topGroupAreaHeight = self.getColumnGroupAreaHeight(),
134
171
  leftGroupAreaWidth = self.getRowGroupAreaWidth(),
135
- ch = self.style.cellHeight,
136
- s = self.getSchema();
172
+ ch = self.style.cellHeight;
137
173
  // sets actual DOM canvas element
138
174
  function checkScrollBoxVisibility() {
139
175
  self.scrollBox.horizontalBarVisible =
@@ -187,7 +223,7 @@ export default function (self) {
187
223
  }
188
224
  });
189
225
  }
190
- self.scrollCache.x = [];
226
+ dataWidth = self.refreshScrollCacheX();
191
227
  self.scrollCache.y = [];
192
228
  for (x = 0; x < l; x += 1) {
193
229
  self.scrollCache.y[x] = dataHeight;
@@ -199,32 +235,6 @@ export default function (self) {
199
235
  if (l > 1) {
200
236
  self.scrollCache.y[x] = dataHeight;
201
237
  }
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;
209
- dataWidth =
210
- s.reduce(function reduceSchema(accumulator, column, columnIndex) {
211
- // intentional redefintion of column. This causes scrollCache to be in the correct order
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) {
218
- self.scrollCache.x[columnIndex] = accumulator;
219
- frozenWidth = accumulator;
220
- } else {
221
- self.scrollCache.x[columnIndex] = Math.max(
222
- frozenWidth + columnWidth,
223
- accumulator,
224
- );
225
- }
226
- return accumulator;
227
- }, 0) || 0;
228
238
  if (self.attributes.showNewRow) {
229
239
  dataHeight += ch;
230
240
  }
@@ -501,6 +511,20 @@ export default function (self) {
501
511
  }
502
512
  self.currentCell = cell;
503
513
 
514
+ if (
515
+ !self.draggingItem && // It is not in dragging mode (avoid changing hovers frequent)
516
+ cell &&
517
+ (cell.context === 'cell' || cell.context === self.cursorGrab)
518
+ ) {
519
+ const indicator = self.getUnhideIndicator(self.mouse.x, self.mouse.y);
520
+ if (indicator) {
521
+ self.cursor = 'pointer';
522
+ self.hovers = { unhideIndicator: indicator };
523
+ self.draw();
524
+ return;
525
+ }
526
+ }
527
+
504
528
  self.hovers = {};
505
529
  if (!self.draggingItem && cell) {
506
530
  self.dragItem = cell;
@@ -791,6 +815,15 @@ export default function (self) {
791
815
  return;
792
816
  }
793
817
 
818
+ const unhideIndicator = self.getUnhideIndicator(pos.x, pos.y);
819
+ if (unhideIndicator) {
820
+ const { dir, orderIndex0, orderIndex1 } = unhideIndicator;
821
+ if (dir === 'l' || dir === 'r')
822
+ self.unhideColumns(orderIndex0, orderIndex1);
823
+ else self.unhideRows(orderIndex0, orderIndex1);
824
+ return;
825
+ }
826
+
794
827
  let group = self.getColumnGroupAt(pos.x, pos.y);
795
828
  if (!group) group = self.getRowGroupAt(pos.x, pos.y);
796
829
  if (group) {
@@ -1294,13 +1327,14 @@ export default function (self) {
1294
1327
  self.currentCell.columnIndex !== undefined &&
1295
1328
  self.dragMode === 'frozen-column-marker'
1296
1329
  ) {
1330
+ const dataWidth = self.refreshScrollCacheX();
1297
1331
  self.scrollBox.scrollLeft = 0;
1298
1332
  self.frozenColumn = self.currentCell.columnIndex + 1;
1299
1333
  self.scrollBox.bar.h.x =
1300
1334
  rowHeaderCellWidth + self.scrollCache.x[self.frozenColumn - 1];
1301
1335
  self.scrollBox.box.h.x =
1302
1336
  rowHeaderCellWidth + self.scrollCache.x[self.frozenColumn - 1];
1303
- var dataWidth = self.scrollCache.x[self.scrollCache.x.length - 1];
1337
+ // var dataWidth = self.scrollCache.x[self.scrollCache.x.length - 1];
1304
1338
  self.scrollBox.widthBoxRatio =
1305
1339
  (self.scrollBox.width - self.scrollCache.x[self.frozenColumn - 1]) /
1306
1340
  dataWidth;
@@ -1359,6 +1393,7 @@ export default function (self) {
1359
1393
  move = /-move/.test(self.dragMode),
1360
1394
  freeze = /frozen-row-marker|frozen-column-marker/.test(self.dragMode),
1361
1395
  resize = /-resize/.test(self.dragMode);
1396
+ const onUnhideIndicator = self.hovers && self.hovers.unhideIndicator;
1362
1397
  self.dragStart = overridePos || self.getLayerPos(e);
1363
1398
  self.scrollStart = {
1364
1399
  left: self.scrollBox.scrollLeft,
@@ -1388,7 +1423,10 @@ export default function (self) {
1388
1423
  if (self.dragStartObject.isGrid) {
1389
1424
  return;
1390
1425
  }
1391
- if (self.scrollModes.indexOf(self.dragStartObject.context) !== -1) {
1426
+ if (
1427
+ self.scrollModes.indexOf(self.dragStartObject.context) !== -1 &&
1428
+ !onUnhideIndicator
1429
+ ) {
1392
1430
  self.scrollMode = self.dragStartObject.context;
1393
1431
  self.scrollStartMode = self.dragStartObject.context;
1394
1432
  self.scrollGrid(e);
@@ -1457,7 +1495,7 @@ export default function (self) {
1457
1495
  document.body.addEventListener('mouseup', self.stopFreezeMove, false);
1458
1496
  return self.mousemove(e);
1459
1497
  }
1460
- if (resize) {
1498
+ if (resize && !onUnhideIndicator) {
1461
1499
  self.draggingItem = self.dragItem;
1462
1500
  if (self.draggingItem.rowOpen) {
1463
1501
  self.resizingStartingHeight =
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Merge a new hidden row range into existed ranges array
5
+ * @param {any[]} hiddenRowRanges tuples: Array<[bgeinRowIndex, endRowIndex]>
6
+ * @param {number[]} newRange tuple: [beginRowIndex, endRowIndex]
7
+ * @returns {boolean}
8
+ */
9
+ const mergeHiddenRowRanges = function (hiddenRowRanges, newRange) {
10
+ const [beginRowIndex, endRowIndex] = newRange;
11
+ if (endRowIndex < beginRowIndex) return false;
12
+ let inserted = false;
13
+ for (let i = 0; i < hiddenRowRanges.length; i++) {
14
+ const range = hiddenRowRanges[i];
15
+ if (beginRowIndex > range[1] + 1) continue;
16
+ if (beginRowIndex <= range[0] && endRowIndex >= range[0]) {
17
+ hiddenRowRanges[i] = [beginRowIndex, Math.max(endRowIndex, range[1])];
18
+ inserted = true;
19
+ break;
20
+ }
21
+ if (beginRowIndex >= range[0]) {
22
+ hiddenRowRanges[i] = [range[0], Math.max(endRowIndex, range[1])];
23
+ inserted = true;
24
+ break;
25
+ }
26
+ }
27
+ if (!inserted) hiddenRowRanges.push([beginRowIndex, endRowIndex]);
28
+ // merge intersections after sorting ranges
29
+ hiddenRowRanges.sort((a, b) => a[0] - b[0]);
30
+ for (let i = 0; i < hiddenRowRanges.length - 1; i++) {
31
+ const range = hiddenRowRanges[i];
32
+ const nextRange = hiddenRowRanges[i + 1];
33
+ if (nextRange[0] <= range[1] + 1) {
34
+ hiddenRowRanges[i] = [range[0], Math.max(range[1], nextRange[1])];
35
+ hiddenRowRanges.splice(i + 1, 1);
36
+ i--;
37
+ }
38
+ }
39
+ return true;
40
+ };
41
+
42
+ export { mergeHiddenRowRanges };
package/lib/intf.js CHANGED
@@ -57,6 +57,19 @@ export default function (self, ctor) {
57
57
  self.scrollBox = {};
58
58
  self.visibleRows = [];
59
59
  self.visibleCells = [];
60
+ /**
61
+ * Each item of this array contains these properties:
62
+ * - `x`, `y`, `x2`, `y2`
63
+ * - `orderIndex0`, `orderIndex1`: The closed interval of the hiding rows/columns.
64
+ * - `dir`: The directon of the unhide indicator. 'l' and 'r' for columns, 't' and 'b' for rows
65
+ */
66
+ self.visibleUnhideIndicators = [];
67
+ /**
68
+ * Each item is a tuple conatins two numbers:
69
+ * its type difination: Array<[beginRowIndex, endRowIndex]>
70
+ * Each tuple represents a closed Interval
71
+ */
72
+ self.hiddenRowRanges = [];
60
73
  /**
61
74
  * This array stored all groups information with context for drawing,
62
75
  * it is generated by drawing functions,
@@ -654,6 +667,16 @@ export default function (self, ctor) {
654
667
  originalRowIndex,
655
668
  ]);
656
669
 
670
+ // Remove hidden rows here. So we can keep the bound indexes correct
671
+ if (self.hiddenRowRanges.length > 0) {
672
+ const ranges = self.hiddenRowRanges.sort((a, b) => b[1] - a[1]);
673
+ for (let i = 0; i < ranges.length; i++) {
674
+ const [beginRowIndex, endRowIndex] = ranges[i];
675
+ const countOfRows = endRowIndex - beginRowIndex + 1;
676
+ newViewData.splice(beginRowIndex, countOfRows);
677
+ }
678
+ }
679
+
657
680
  // Apply filtering
658
681
  for (const [headerName, filterText] of Object.entries(self.columnFilters)) {
659
682
  const header = self.getHeaderByName(headerName);
@@ -1257,6 +1280,17 @@ export default function (self, ctor) {
1257
1280
  };
1258
1281
  };
1259
1282
 
1283
+ self.unhideColumns = function (orderIndex0, orderIndex1) {
1284
+ const orders = self.orders.columns;
1285
+ const schema = self.getSchema();
1286
+ for (let i = orderIndex0; i <= orderIndex1; i++) {
1287
+ const columnIndex = orders[i];
1288
+ const s = schema[columnIndex];
1289
+ if (s && s.hidden) s.hidden = false;
1290
+ }
1291
+ self.refresh();
1292
+ };
1293
+
1260
1294
  self.getDomRoot = function () {
1261
1295
  return self.shadowRoot ? self.shadowRoot.host : self.parentNode;
1262
1296
  };
@@ -1487,6 +1521,9 @@ export default function (self, ctor) {
1487
1521
  self.intf.cut = self.cut;
1488
1522
  self.intf.paste = self.paste;
1489
1523
  self.intf.setStyleProperty = self.setStyleProperty;
1524
+ self.intf.hideColumns = self.hideColumns;
1525
+ self.intf.hideRows = self.hideRows;
1526
+ self.intf.unhideRows = self.unhideRows;
1490
1527
  Object.defineProperty(self.intf, 'defaults', {
1491
1528
  get: function () {
1492
1529
  return {
@@ -2,6 +2,8 @@
2
2
  /*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false*/
3
3
  'use strict';
4
4
 
5
+ import { mergeHiddenRowRanges } from './groups/util';
6
+
5
7
  export default function (self) {
6
8
  /**
7
9
  * Converts a integer into a letter A - ZZZZZ...
@@ -762,6 +764,63 @@ export default function (self) {
762
764
  }
763
765
  }
764
766
  };
767
+ /**
768
+ * Hide column/columns
769
+ * @memberof canvasDatagrid
770
+ * @name hideColumns
771
+ * @param {number} beginColumnOrderIndex
772
+ * @param {number} [endColumnOrderIndex]
773
+ */
774
+ self.hideColumns = function (beginColumnOrderIndex, endColumnOrderIndex) {
775
+ const schema = self.getSchema();
776
+ const orders = self.orders.columns;
777
+ let count = 0;
778
+ if (typeof endColumnOrderIndex !== 'number')
779
+ endColumnOrderIndex = beginColumnOrderIndex;
780
+ for (
781
+ let orderIndex = beginColumnOrderIndex;
782
+ orderIndex <= endColumnOrderIndex;
783
+ orderIndex++
784
+ ) {
785
+ const columnIndex = orders[orderIndex];
786
+ if (columnIndex >= 0 && !schema[columnIndex].hidden) {
787
+ count++;
788
+ schema[columnIndex].hidden = true;
789
+ }
790
+ }
791
+ if (count > 0) {
792
+ self.setStorageData();
793
+ setTimeout(function () {
794
+ self.resize(true);
795
+ }, 10);
796
+ }
797
+ };
798
+ /**
799
+ * Hide rows
800
+ * @memberof canvasDatagrid
801
+ * @name hideRows
802
+ * @param {number} beginRowIndex
803
+ * @param {number} endRowIndex
804
+ */
805
+ self.hideRows = function (beginRowIndex, endRowIndex) {
806
+ if (
807
+ mergeHiddenRowRanges(self.hiddenRowRanges, [beginRowIndex, endRowIndex])
808
+ )
809
+ self.refresh();
810
+ };
811
+ /**
812
+ * Unhide rows
813
+ * @memberof canvasDatagrid
814
+ * @name unhideRows
815
+ * @param {number} beginRowIndex
816
+ * @param {number} endRowIndex
817
+ */
818
+ self.unhideRows = function (beginRowIndex, endRowIndex) {
819
+ self.hiddenRowRanges = self.hiddenRowRanges.filter(
820
+ (range) => range[0] !== beginRowIndex || range[1] !== endRowIndex,
821
+ );
822
+ self.refresh();
823
+ };
765
824
  /**
766
825
  * Resizes a column to fit the longest value in the column. Call without a value to resize all columns.
767
826
  * Warning, can be slow on very large record sets (1m records ~3-5 seconds on an i7).
@@ -1336,6 +1395,28 @@ export default function (self) {
1336
1395
  return c.columnIndex === x && c.rowIndex === y;
1337
1396
  })[0];
1338
1397
  };
1398
+ /**
1399
+ * Get an unhide indicator at grid pixel coordinate x and y.
1400
+ * @memberof canvasDatagrid
1401
+ * @name getUnhideIndicator
1402
+ * @method
1403
+ * @param {number} x Number of pixels from the left.
1404
+ * @param {number} y Number of pixels from the top.
1405
+ */
1406
+ self.getUnhideIndicator = function (x, y) {
1407
+ const indicators = self.visibleUnhideIndicators;
1408
+ if (indicators.length <= 0) return;
1409
+ for (let i = 0; i < indicators.length; i++) {
1410
+ const indicator = indicators[i];
1411
+ if (
1412
+ x >= indicator.x &&
1413
+ y >= indicator.y &&
1414
+ x <= indicator.x2 &&
1415
+ y <= indicator.y2
1416
+ )
1417
+ return indicator;
1418
+ }
1419
+ };
1339
1420
  /**
1340
1421
  * Get a column group at grid pixel coordinate x and y.
1341
1422
  * @memberof canvasDatagrid
@@ -1783,8 +1864,9 @@ export default function (self) {
1783
1864
  self.ctx.font = self.style.columnHeaderCellFont;
1784
1865
  var t =
1785
1866
  self.ctx.measureText(col.title || col.name).width +
1786
- self.style.headerCellPaddingRight +
1787
- self.style.headerCellPaddingLeft;
1867
+ self.style.columnHeaderCellPaddingRight +
1868
+ self.style.columnHeaderCellPaddingLeft +
1869
+ self.style.cellAutoResizePadding;
1788
1870
  m = t > m ? t : m;
1789
1871
  formatter = self.formatters[col.type];
1790
1872
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvas-datagrid",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Canvas based data grid web component. Capable of displaying millions of contiguous hierarchical rows and columns without paging or loading, on a single canvas element.",
5
5
  "main": "./dist/canvas-datagrid.js",
6
6
  "module": "./dist/canvas-datagrid.module.js",