canvas-datagrid 0.3.25 → 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.
@@ -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...
@@ -331,6 +333,39 @@ export default function (self) {
331
333
  imgSrc,
332
334
  );
333
335
  };
336
+
337
+ /**
338
+ * Expand/Collapse CellTree.
339
+ * @memberof canvasDatagrid
340
+ * @name toggleCellCollapseTree
341
+ * @method
342
+ * @param {array} treeData The array of cellTree to expand or collapse.
343
+ */
344
+
345
+ self.toggleCellCollapseTree = function (treeData) {
346
+ for (let type in treeData) {
347
+ for (let t of treeData[type])
348
+ if (t.length > 0) self.toggleCollapseTree(t[0], t[1], type);
349
+ }
350
+ self.draw();
351
+ };
352
+
353
+ /**
354
+ * Expand/Collapse CellTree.
355
+ * @memberof canvasDatagrid
356
+ * @name expandCollapseCellTree
357
+ * @method
358
+ * @param {array} treeData The array of cellTree to expand or collapse.
359
+ */
360
+
361
+ self.expandCollapseCellTree = function (treeData) {
362
+ for (let type in treeData) {
363
+ for (let t of treeData[type])
364
+ if (t.length > 0) self.cellTreeExpandCollapse(t[0], t[1], type);
365
+ }
366
+ self.draw();
367
+ };
368
+
334
369
  /**
335
370
  * Scrolls the cell at cell x, row y into view if it is not already.
336
371
  * @memberof canvasDatagrid
@@ -729,6 +764,63 @@ export default function (self) {
729
764
  }
730
765
  }
731
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
+ };
732
824
  /**
733
825
  * Resizes a column to fit the longest value in the column. Call without a value to resize all columns.
734
826
  * Warning, can be slow on very large record sets (1m records ~3-5 seconds on an i7).
@@ -853,6 +945,244 @@ export default function (self) {
853
945
  }
854
946
  self.setStorageData();
855
947
  };
948
+
949
+ /**
950
+ * Add grouping
951
+ * @param {'columns'|'rows'} groupFor
952
+ * @param {number} from
953
+ * @param {number} to
954
+ */
955
+ function addGroup(groupFor, from, to) {
956
+ let newRow = false;
957
+ const allGroups =
958
+ groupFor === 'rows' ? self.groupedRows : self.groupedColumns;
959
+ for (let i = allGroups.length - 1; i >= 0; i--) {
960
+ const groups = allGroups[i];
961
+ const min = groups[0].from,
962
+ max = groups[groups.length - 1].to;
963
+ if (from <= min && to >= max) {
964
+ if (from === min && to === max && groups.length === 1) return; // nothings happened
965
+ // new group wrap this row
966
+ continue;
967
+ }
968
+ for (let gi = 0; gi < groups.length; gi++) {
969
+ const g = groups[gi];
970
+ if (from > g.to) continue;
971
+ if (from >= g.from) {
972
+ if (to > g.to) {
973
+ if (from === g.from) {
974
+ allGroups.splice(i, 0, [{ from, to, collapsed: false }]);
975
+ self.refresh();
976
+ return;
977
+ }
978
+ throw new Error(`Can't group these ${groupFor}`);
979
+ }
980
+ if (to === g.to) {
981
+ if (from === g.from) return; // nothings happened
982
+ }
983
+ newRow = true;
984
+ break;
985
+ }
986
+ if (to >= g.from) {
987
+ if (to < g.to) throw new Error(`Can't group these ${groupFor}`);
988
+ allGroups.splice(i, 0, [{ from, to, collapsed: false }]);
989
+ } else {
990
+ groups.splice(gi, 0, { from, to, collapsed: false });
991
+ }
992
+ self.refresh();
993
+ return;
994
+ }
995
+ if (newRow) continue;
996
+ groups.push({ from, to, collapsed: false });
997
+ self.refresh();
998
+ return;
999
+ }
1000
+ if (newRow) allGroups.push([{ from, to, collapsed: false }]);
1001
+ else allGroups.unshift([{ from, to, collapsed: false }]);
1002
+ self.refresh();
1003
+ }
1004
+ /**
1005
+ * Remove grouping
1006
+ * @param {Array<Array<{from:number,to:number,collapsed:boolean}>>} allGroups
1007
+ * @param {number} from
1008
+ * @param {number} to
1009
+ */
1010
+ function removeGroup(allGroups, from, to) {
1011
+ for (let i = 0; i < allGroups.length; i++) {
1012
+ const groups = allGroups[i];
1013
+ for (let gi = 0; gi < groups.length; gi++) {
1014
+ const group = groups[gi];
1015
+ if (group.from === from && group.to === to) {
1016
+ if (groups.length <= 1) allGroups.splice(i, 1);
1017
+ else groups.splice(gi, 1);
1018
+ self.refresh();
1019
+ return;
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ /**
1025
+ * Grouping columns
1026
+ * @memberof canvasDatagrid
1027
+ * @name groupColumns
1028
+ * @method
1029
+ * @param {number|string} firstColumnName Name of the first column to be grouped.
1030
+ * @param {number|string} lastColumnName Name of the last column to be grouped.
1031
+ */
1032
+ self.groupColumns = function (firstColumnName, lastColumnName) {
1033
+ /** @type {Array<{name: string,columnIndex:number}>} */
1034
+ const schema = self.getSchema();
1035
+ let firstOne, lastOne;
1036
+ for (let i = 0; i < schema.length; i++) {
1037
+ const it = schema[i];
1038
+ if (firstOne && lastOne) break;
1039
+ if (it.name === firstColumnName) {
1040
+ firstOne = it;
1041
+ continue;
1042
+ }
1043
+ if (it.name === lastColumnName) {
1044
+ lastOne = it;
1045
+ continue;
1046
+ }
1047
+ }
1048
+ if (!firstOne) throw new Error(`No such column name for first column`);
1049
+ if (!lastOne) throw new Error(`No such column name for last column`);
1050
+ if (lastOne.columnIndex > firstOne.columnIndex !== true)
1051
+ throw new Error(`Can't group these columns`);
1052
+
1053
+ const from = firstOne.columnIndex;
1054
+ const to = lastOne.columnIndex;
1055
+ const ev = { group: { type: 'columns', from, to } };
1056
+ try {
1057
+ addGroup('columns', from, to);
1058
+ } catch (error) {
1059
+ ev.error = error;
1060
+ }
1061
+ self.dispatchEvent('aftercreategroup', ev);
1062
+ };
1063
+ /**
1064
+ * Grouping columns
1065
+ * @memberof canvasDatagrid
1066
+ * @name groupRows
1067
+ * @method
1068
+ * @param {number} rowIndexFrom The row index which is the beginning of the group
1069
+ * @param {number} rowIndexTo The row index which is the end of the group
1070
+ */
1071
+ self.groupRows = function (rowIndexFrom, rowIndexTo) {
1072
+ if (!Number.isInteger(rowIndexFrom) || rowIndexFrom < 0)
1073
+ throw new Error(`No such row for the beginning of the group`);
1074
+
1075
+ const dataLength = self.viewData.length;
1076
+ if (
1077
+ !Number.isInteger(rowIndexFrom) ||
1078
+ rowIndexTo <= rowIndexFrom ||
1079
+ rowIndexTo >= dataLength
1080
+ )
1081
+ throw new Error(`No such row for the end of the group`);
1082
+ const ev = {
1083
+ group: {
1084
+ type: 'rows',
1085
+ from: rowIndexFrom,
1086
+ to: rowIndexTo,
1087
+ },
1088
+ };
1089
+ try {
1090
+ addGroup('rows', rowIndexFrom, rowIndexTo);
1091
+ } catch (error) {
1092
+ ev.error = error;
1093
+ }
1094
+ self.dispatchEvent('aftercreategroup', ev);
1095
+ };
1096
+ /**
1097
+ * Remove grouping columns
1098
+ * @memberof canvasDatagrid
1099
+ * @name removeGroupColumns
1100
+ * @method
1101
+ * @param {number|string} firstColumnName Name of the first column to be grouped.
1102
+ * @param {number|string} lastColumnName Name of the last column to be grouped.
1103
+ */
1104
+ self.removeGroupColumns = function (firstColumnName, lastColumnName) {
1105
+ /** @type {Array<{name:string,columnIndex:number}>} */
1106
+ const schema = self.getSchema();
1107
+ let firstOne, lastOne;
1108
+ for (let i = 0; i < schema.length; i++) {
1109
+ const it = schema[i];
1110
+ if (firstOne && lastOne) break;
1111
+ if (it.name === firstColumnName) {
1112
+ firstOne = it;
1113
+ continue;
1114
+ }
1115
+ if (it.name === lastColumnName) {
1116
+ lastOne = it;
1117
+ continue;
1118
+ }
1119
+ }
1120
+ if (!firstOne) throw new Error(`No such column name for first column`);
1121
+ if (!lastOne) throw new Error(`No such column name for last column`);
1122
+ const from = firstOne.columnIndex;
1123
+ const to = lastOne.columnIndex;
1124
+ removeGroup(self.groupedColumns, from, to);
1125
+ };
1126
+ /**
1127
+ * Remove grouping columns
1128
+ * @memberof canvasDatagrid
1129
+ * @name removeGroupRows
1130
+ * @method
1131
+ * @param {number} rowIndexFrom The row index which is the beginning of the group
1132
+ * @param {number} rowIndexTo The row index which is the end of the group
1133
+ */
1134
+ self.removeGroupRows = function (rowIndexFrom, rowIndexTo) {
1135
+ removeGroup(self.groupedRows, rowIndexFrom, rowIndexTo);
1136
+ };
1137
+ /**
1138
+ * Toggle(expand/collapsed) grouping columns
1139
+ * @memberof canvasDatagrid
1140
+ * @name toggleGroupColumns
1141
+ * @method
1142
+ * @param {number|string} firstColumnName Name of the first column to be grouped.
1143
+ * @param {number|string} lastColumnName Name of the last column to be grouped.
1144
+ */
1145
+ self.toggleGroupColumns = function (firstColumnName, lastColumnName) {
1146
+ /** @type {Array<{name:string,columnIndex:number}>} */
1147
+ const schema = self.getSchema();
1148
+ let firstOne, lastOne;
1149
+ for (let i = 0; i < schema.length; i++) {
1150
+ const it = schema[i];
1151
+ if (firstOne && lastOne) break;
1152
+ if (it.name === firstColumnName) {
1153
+ firstOne = it;
1154
+ continue;
1155
+ }
1156
+ if (it.name === lastColumnName) {
1157
+ lastOne = it;
1158
+ continue;
1159
+ }
1160
+ }
1161
+ if (!firstOne || !lastOne) return;
1162
+ const from = firstOne.columnIndex;
1163
+ const to = lastOne.columnIndex;
1164
+ if (self.toggleGroup({ type: 'c', from, to })) {
1165
+ self.disposeContextMenu();
1166
+ self.setStorageData();
1167
+ self.refresh();
1168
+ }
1169
+ };
1170
+ /**
1171
+ * Toggle(expand/collapsed) grouping rows
1172
+ * @memberof canvasDatagrid
1173
+ * @name toggleGroupRows
1174
+ * @method
1175
+ * @param {number} rowIndexFrom The row index which is the beginning of the group
1176
+ * @param {number} rowIndexTo The row index which is the end of the group
1177
+ */
1178
+ self.toggleGroupRows = function (rowIndexFrom, rowIndexTo) {
1179
+ if (self.toggleGroup({ type: 'r', from: rowIndexFrom, to: rowIndexTo })) {
1180
+ self.disposeContextMenu();
1181
+ self.setStorageData();
1182
+ self.refresh();
1183
+ }
1184
+ };
1185
+
856
1186
  self.isInGrid = function (e) {
857
1187
  if (e.x < 0 || e.x > self.width || e.y < 0 || e.y > self.height) {
858
1188
  return false;
@@ -931,6 +1261,9 @@ export default function (self) {
931
1261
  return;
932
1262
  }
933
1263
  // TODO:
1264
+ if (!self.data) {
1265
+ self.data = {};
1266
+ }
934
1267
  if (!self.data[rowIndex]) {
935
1268
  self.data[rowIndex] = {};
936
1269
  }
@@ -971,6 +1304,53 @@ export default function (self) {
971
1304
  });
972
1305
  });
973
1306
  };
1307
+ /**
1308
+ * Get the column group info given column belongs to
1309
+ * @memberof canvasDatagrid
1310
+ * @name getGroupsColumnBelongsTo
1311
+ * @method
1312
+ * @param {number} columnIndex Column index.
1313
+ * @returns {Array<{from:number,to:number,collapsed:boolean}>}
1314
+ */
1315
+ self.getGroupsColumnBelongsTo = function (columnIndex) {
1316
+ if (!self.attributes.allowGroupingColumns) return [];
1317
+ const result = [];
1318
+ for (let i = 0; i < self.groupedColumns.length; i++) {
1319
+ const groups = self.groupedColumns[i];
1320
+ for (let j = 0; j < groups.length; j++) {
1321
+ const group = groups[j];
1322
+ if (columnIndex >= group.from && columnIndex <= group.to) {
1323
+ result.push(group);
1324
+ break;
1325
+ }
1326
+ }
1327
+ }
1328
+ return result;
1329
+ };
1330
+ /**
1331
+ * Get the row group info given row belongs to
1332
+ * @memberof canvasDatagrid
1333
+ * @name getGroupsRowBelongsTo
1334
+ * @method
1335
+ * @param {number} rowIndex Row index.
1336
+ * @returns {Array<{from:number,to:number,collapsed:boolean}>}
1337
+ */
1338
+ self.getGroupsRowBelongsTo = function (rowIndex) {
1339
+ if (!self.attributes.allowGroupingRows) return [];
1340
+ const result = [];
1341
+ for (let i = 0; i < self.groupedRows.length; i++) {
1342
+ const groups = self.groupedRows[i];
1343
+ for (let j = 0; j < groups.length; j++) {
1344
+ const group = groups[j];
1345
+ if (rowIndex >= group.from && rowIndex <= group.to) {
1346
+ result.push(group);
1347
+ break;
1348
+ }
1349
+ }
1350
+ }
1351
+ return result;
1352
+ };
1353
+
974
1354
  /**
975
1355
  * Checks if a given column is visible.
976
1356
  * @memberof canvasDatagrid
@@ -1015,6 +1395,66 @@ export default function (self) {
1015
1395
  return c.columnIndex === x && c.rowIndex === y;
1016
1396
  })[0];
1017
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
+ };
1420
+ /**
1421
+ * Get a column group at grid pixel coordinate x and y.
1422
+ * @memberof canvasDatagrid
1423
+ * @name getColumnGroupAt
1424
+ * @method
1425
+ * @param {number} x Number of pixels from the left.
1426
+ * @param {number} y Number of pixels from the top.
1427
+ */
1428
+ self.getColumnGroupAt = function (x, y) {
1429
+ const groups = self.groupedColumns.length;
1430
+ if (groups <= 0) return;
1431
+ const yZero = self.getColumnGroupAreaHeight();
1432
+ if (y >= yZero) return;
1433
+ for (let i = 0; i < self.visibleGroups.length; i++) {
1434
+ const g = self.visibleGroups[i];
1435
+ if (g.type !== 'c') continue;
1436
+ if (x >= g.x && y >= g.y && x <= g.x2 && y <= g.y2) return g;
1437
+ }
1438
+ };
1439
+ /**
1440
+ * Get a row group at grid pixel coordinate x and y.
1441
+ * @memberof canvasDatagrid
1442
+ * @name getRowGroupAt
1443
+ * @method
1444
+ * @param {number} x Number of pixels from the left.
1445
+ * @param {number} y Number of pixels from the top.
1446
+ */
1447
+ self.getRowGroupAt = function (x, y) {
1448
+ const groups = self.groupedRows.length;
1449
+ if (groups <= 0) return;
1450
+ const xZero = self.getRowGroupAreaWidth();
1451
+ if (x >= xZero) return;
1452
+ for (let i = 0; i < self.visibleGroups.length; i++) {
1453
+ const g = self.visibleGroups[i];
1454
+ if (g.type !== 'r') continue;
1455
+ if (x >= g.x && y >= g.y && x <= g.x2 && y <= g.y2) return g;
1456
+ }
1457
+ };
1018
1458
  /**
1019
1459
  * Gets the cell at grid pixel coordinate x and y. Author's note. This function ties drawing and events together. This is a very complex function and is core to the component.
1020
1460
  * @memberof canvasDatagrid
@@ -1056,6 +1496,8 @@ export default function (self) {
1056
1496
  if (!self.visibleCells) {
1057
1497
  return;
1058
1498
  }
1499
+ x -= self.getRowGroupAreaWidth();
1500
+ y -= self.getColumnGroupAreaHeight();
1059
1501
  var border,
1060
1502
  tsz = useTouchScrollZones ? self.attributes.touchScrollZone : 0,
1061
1503
  moveMode = self.attributes.borderDragBehavior === 'move',
@@ -1204,8 +1646,20 @@ export default function (self) {
1204
1646
  }
1205
1647
  }
1206
1648
  if (cell.style === 'columnHeaderCell') {
1207
- cell.context = 'cell';
1208
- cell.dragContext = 'column-reorder';
1649
+ if (
1650
+ cell.x +
1651
+ cell.width -
1652
+ self.attributes.columnGrabZoneSize -
1653
+ self.style.cellBorderWidth >
1654
+ x ||
1655
+ !self.attributes.allowRowReordering
1656
+ ) {
1657
+ cell.dragContext = 'cell';
1658
+ cell.context = 'cell';
1659
+ } else {
1660
+ cell.context = self.cursorGrab;
1661
+ cell.dragContext = 'column-reorder';
1662
+ }
1209
1663
  return cell;
1210
1664
  }
1211
1665
  if (cell.style === 'rowHeaderCell') {
@@ -1410,8 +1864,9 @@ export default function (self) {
1410
1864
  self.ctx.font = self.style.columnHeaderCellFont;
1411
1865
  var t =
1412
1866
  self.ctx.measureText(col.title || col.name).width +
1413
- self.style.headerCellPaddingRight +
1414
- self.style.headerCellPaddingLeft;
1867
+ self.style.columnHeaderCellPaddingRight +
1868
+ self.style.columnHeaderCellPaddingLeft +
1869
+ self.style.cellAutoResizePadding;
1415
1870
  m = t > m ? t : m;
1416
1871
  formatter = self.formatters[col.type];
1417
1872
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvas-datagrid",
3
- "version": "0.3.25",
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",