canvas-datagrid 0.25.4 → 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.
@@ -173,6 +173,33 @@ export default function (self) {
173
173
  self.parentDOMNode.appendChild(downArrow);
174
174
  }
175
175
  }
176
+ function fade(element) {
177
+ var opacity = 1;
178
+ var timer = setInterval(function () {
179
+ if (opacity <= 0.1) {
180
+ clearInterval(timer);
181
+ element.style.display = 'none';
182
+ if (element.parentNode) {
183
+ element.parentNode.removeChild(element);
184
+ }
185
+ }
186
+ element.style.opacity = opacity;
187
+ element.style.filter = 'alpha(opacity=' + opacity * 100 + ')';
188
+ opacity -= opacity * 0.1;
189
+ }, self.attributes.animationDurationHideContextMenu * 0.1);
190
+ }
191
+ function unfade(element) {
192
+ var opacity = 0.1;
193
+ element.style.display = 'block';
194
+ var timer = setInterval(function () {
195
+ if (opacity >= 1) {
196
+ clearInterval(timer);
197
+ }
198
+ element.style.opacity = opacity;
199
+ element.style.filter = 'alpha(opacity=' + opacity * 100 + ')';
200
+ opacity += opacity * 0.1;
201
+ }, self.attributes.animationDurationShowContextMenu * 0.1);
202
+ }
176
203
  function startHoverScroll(type) {
177
204
  return function t() {
178
205
  var a = self.attributes.contextHoverScrollAmount;
@@ -210,6 +237,7 @@ export default function (self) {
210
237
  loc.y = pos.top - s.top;
211
238
  loc.height = 0;
212
239
  zIndexTop += 1;
240
+ container.style.opacity = 0;
213
241
  container.style.position = 'absolute';
214
242
  upArrow.style.color = self.style.contextMenuArrowColor;
215
243
  downArrow.style.color = self.style.contextMenuArrowColor;
@@ -246,6 +274,7 @@ export default function (self) {
246
274
  container.appendChild(upArrow);
247
275
  document.body.appendChild(downArrow);
248
276
  document.body.appendChild(container);
277
+ unfade(container);
249
278
  rect = container.getBoundingClientRect();
250
279
  // TODO: fix !(parentContextMenu && parentContextMenu.inputDropdown) state (autocomplete)
251
280
  if (rect.bottom > window.innerHeight) {
@@ -307,9 +336,7 @@ export default function (self) {
307
336
  c.dispose();
308
337
  });
309
338
  [downArrow, upArrow, container].forEach(function (el) {
310
- if (el.parentNode) {
311
- el.parentNode.removeChild(el);
312
- }
339
+ fade(el);
313
340
  });
314
341
  };
315
342
  Object.defineProperty(intf, 'selectedIndex', {
@@ -317,7 +344,7 @@ export default function (self) {
317
344
  return selectedIndex;
318
345
  },
319
346
  set: function (value) {
320
- if (typeof value !== 'number' || isNaN(value || !isFinite(value))) {
347
+ if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
321
348
  throw new Error('Context menu selected index must be a sane number.');
322
349
  }
323
350
  selectedIndex = value;
@@ -367,7 +394,7 @@ export default function (self) {
367
394
  var items = {};
368
395
  var blanksItem = [];
369
396
 
370
- self.data.forEach(function (row) {
397
+ self.viewData.forEach(function (row) {
371
398
  var cellValue =
372
399
  row[e.cell.header.name] == null
373
400
  ? row[e.cell.header.name]
@@ -511,8 +538,23 @@ export default function (self) {
511
538
  });
512
539
  }
513
540
  }
541
+
514
542
  function addDefaultContextMenuItem(e) {
515
- var isNormalCell =
543
+ const schema = self.getSchema();
544
+ /**
545
+ * A map between columnIndex and column data
546
+ * @type {Map<string,any>}
547
+ */
548
+ let columns;
549
+ const getColumnsMap = () => {
550
+ if (!columns)
551
+ columns = new Map(schema.map((_col) => [_col.columnIndex, _col]));
552
+ return columns;
553
+ };
554
+ const isSorting =
555
+ self.orderings.columns && self.orderings.columns.length > 0;
556
+
557
+ const isNormalCell =
516
558
  !(
517
559
  e.cell.isBackground ||
518
560
  e.cell.isColumnHeaderCellCap ||
@@ -523,12 +565,7 @@ export default function (self) {
523
565
  if (self.attributes.showFilter && isNormalCell) {
524
566
  createFilterContextMenuItems(e);
525
567
  }
526
- if (
527
- self.attributes.showCopy &&
528
- self.selections.reduce(function (p, r) {
529
- return p + r.length;
530
- }, 0) > 0
531
- ) {
568
+ if (self.attributes.showCopy && self.canSelectionsBeCopied()) {
532
569
  e.items.push({
533
570
  title: self.attributes.copyText,
534
571
  click: function () {
@@ -542,7 +579,11 @@ export default function (self) {
542
579
  e.items.push({
543
580
  title: self.attributes.pasteText,
544
581
  click: function () {
545
- self.paste(self.clipBoardData, e.cell.columnIndex, e.cell.rowIndex);
582
+ // Original function call:
583
+ // self.paste(self.clipBoardData, e.cell.columnIndex, e.cell.rowIndex);
584
+ // We can remove the last two parameters follow
585
+ // because this function only need one parameter by following the function declaration
586
+ self.paste(self.clipBoardData);
546
587
  self.draw();
547
588
  },
548
589
  });
@@ -582,20 +623,35 @@ export default function (self) {
582
623
  },
583
624
  });
584
625
  if (e.cell && e.cell.header && e.cell.columnIndex > -1) {
626
+ // This variable represents the order index because of the following codes from `draw.js`:
627
+ // columnIndex: columnOrderIndex,
628
+ const columnOrderIndex = e.cell.columnIndex;
629
+ const columnIndex = self.orders.columns[columnOrderIndex];
630
+
631
+ const contiguousColumns = self.getSelectedContiguousColumns(schema);
632
+ let title = '';
633
+ if (contiguousColumns) {
634
+ title = contiguousColumns
635
+ .map((col) => col.title || col.name)
636
+ .join('-');
637
+ } else {
638
+ const column = schema[columnIndex];
639
+ if (column) title = column.title || column.name;
640
+ }
585
641
  e.items.push({
586
- title: self.attributes.hideColumnText.replace(
587
- /%s/gi,
588
- e.cell.header.title || e.cell.header.name,
589
- ),
642
+ title: self.attributes.hideColumnText.replace(/%s/gi, title),
590
643
  click: function (ev) {
591
- self.getSchema()[e.cell.columnIndex].hidden = true;
592
644
  ev.preventDefault();
593
645
  self.stopPropagation(ev);
594
646
  self.disposeContextMenu();
595
- self.setStorageData();
596
- setTimeout(function () {
597
- self.resize(true);
598
- }, 10);
647
+ if (contiguousColumns) {
648
+ self.hideColumns(
649
+ contiguousColumns[0].orderIndex,
650
+ contiguousColumns[1].orderIndex,
651
+ );
652
+ } else {
653
+ self.hideColumns(columnOrderIndex);
654
+ }
599
655
  },
600
656
  });
601
657
  }
@@ -618,8 +674,8 @@ export default function (self) {
618
674
  self.dispatchEvent('resizecolumn', {
619
675
  columnWidth: self.style.cellWidth,
620
676
  });
621
- self.dispatchEvent('resizerow', {
622
- cellHeight: self.style.cellHeight,
677
+ self.dispatchResizeRow({
678
+ height: self.style.cellHeight,
623
679
  });
624
680
  self.setStorageData();
625
681
  self.resize(true);
@@ -657,6 +713,254 @@ export default function (self) {
657
713
  },
658
714
  });
659
715
  }
716
+
717
+ //#region hide rows
718
+ const canHideRows = !isSorting && e.cell.isRowHeader && e.cell.header;
719
+ if (canHideRows) {
720
+ const range = self.getSelectedContiguousRows(true);
721
+ if (range) {
722
+ const boundRowIndexes = range.map((viewRowIndex) =>
723
+ self.getBoundRowIndexFromViewRowIndex(viewRowIndex),
724
+ );
725
+ let title;
726
+ if (boundRowIndexes.length === 1) {
727
+ if (typeof boundRowIndexes[0] === 'number')
728
+ title = boundRowIndexes[0] + 1;
729
+ else title = range[0] + 1;
730
+
731
+ title = self.attributes.showHideRow.replace('%s', title);
732
+ // hide one row
733
+ e.items.push({
734
+ title,
735
+ click: function (ev) {
736
+ ev.preventDefault();
737
+ self.hideRows(boundRowIndexes[0], boundRowIndexes[0]);
738
+ },
739
+ });
740
+ } else if (boundRowIndexes[0] <= boundRowIndexes[1]) {
741
+ title = boundRowIndexes
742
+ .map((it, index) => {
743
+ if (typeof it === 'number') return it + 1;
744
+ return range[index] + 1;
745
+ })
746
+ .join('-');
747
+ title = self.attributes.showHideRows.replace('%s', title);
748
+ // hide rows
749
+ e.items.push({
750
+ title,
751
+ click: function (ev) {
752
+ ev.preventDefault();
753
+ self.hideRows(boundRowIndexes[0], boundRowIndexes[1]);
754
+ },
755
+ });
756
+ }
757
+ }
758
+ }
759
+ //#endregion hide rows
760
+
761
+ //#region group/ungroup columns
762
+ const groupAreaHeight = self.getColumnGroupAreaHeight();
763
+ const groupAreaWidth = self.getRowGroupAreaWidth();
764
+ const setCollapseStateForAllGroups = (allGroups, collapsed) => {
765
+ if (allGroups.length === 0) return;
766
+ for (let i = 0; i < allGroups.length; i++) {
767
+ const groups = allGroups[i];
768
+ for (let j = 0; j < groups.length; j++) {
769
+ const group = groups[j];
770
+ group.collapsed = collapsed;
771
+ }
772
+ }
773
+ self.refresh();
774
+ };
775
+ if (e.pos && e.pos.y < groupAreaHeight) {
776
+ e.items.push({
777
+ title: self.attributes.showRemoveAllGroupColumns,
778
+ click: function (ev) {
779
+ ev.preventDefault();
780
+ self.groupedColumns = [];
781
+ self.refresh();
782
+ },
783
+ });
784
+ e.items.push({
785
+ title: self.attributes.showExpandAllGroupColumns,
786
+ click: function (ev) {
787
+ ev.preventDefault();
788
+ setCollapseStateForAllGroups(self.groupedColumns, false);
789
+ },
790
+ });
791
+ e.items.push({
792
+ title: self.attributes.showCollapseAllGroupColumns,
793
+ click: function (ev) {
794
+ ev.preventDefault();
795
+ setCollapseStateForAllGroups(self.groupedColumns, true);
796
+ },
797
+ });
798
+ }
799
+ if (e.pos && e.pos.x < groupAreaWidth) {
800
+ e.items.push({
801
+ title: self.attributes.showRemoveAllGroupRows,
802
+ click: function (ev) {
803
+ ev.preventDefault();
804
+ self.groupedRows = [];
805
+ self.refresh();
806
+ },
807
+ });
808
+ e.items.push({
809
+ title: self.attributes.showExpandAllGroupRows,
810
+ click: function (ev) {
811
+ ev.preventDefault();
812
+ setCollapseStateForAllGroups(self.groupedRows, false);
813
+ },
814
+ });
815
+ e.items.push({
816
+ title: self.attributes.showCollapseAllGroupRows,
817
+ click: function (ev) {
818
+ ev.preventDefault();
819
+ setCollapseStateForAllGroups(self.groupedRows, true);
820
+ },
821
+ });
822
+ }
823
+
824
+ const canGroupByColumns =
825
+ self.attributes.allowGroupingColumns &&
826
+ e.cell.isColumnHeader &&
827
+ e.cell.header &&
828
+ e.cell.header.index > 0;
829
+ const canUngroupColumns =
830
+ self.attributes.allowGroupingColumns && e.cell.isColumnHeader;
831
+ const canGroupByRows =
832
+ !isSorting &&
833
+ self.attributes.allowGroupingRows &&
834
+ e.cell.isRowHeader &&
835
+ e.cell.header;
836
+ const canUngroupRows =
837
+ self.attributes.allowGroupingRows && e.cell.isRowHeader;
838
+
839
+ if (canGroupByColumns) {
840
+ /** @type {number[]} */
841
+ const groupIndexes = [];
842
+ /** @type {number} */
843
+ const headerIndex = e.cell.header.index;
844
+ let col = headerIndex;
845
+ for (; col >= 0; col--) {
846
+ if (!self.isColumnSelected(col)) break;
847
+ groupIndexes[0] = col;
848
+ }
849
+ for (col = headerIndex; ; col++) {
850
+ if (!self.isColumnSelected(col)) break;
851
+ groupIndexes[1] = col;
852
+ }
853
+ if (
854
+ col !== headerIndex &&
855
+ groupIndexes.length === 2 &&
856
+ groupIndexes[1] > groupIndexes[0] &&
857
+ self.isNewGroupRangeValid(
858
+ self.groupedColumns,
859
+ groupIndexes[0],
860
+ groupIndexes[1],
861
+ )
862
+ ) {
863
+ const columns = getColumnsMap();
864
+ const groupTitles = [];
865
+ const groupNames = [];
866
+ for (let i = 0; i < groupIndexes.length; i++) {
867
+ const columnIndex = groupIndexes[i];
868
+ const column = columns.get(columnIndex);
869
+ if (column) {
870
+ groupNames.push(column.name);
871
+ groupTitles.push(column.title || column.name || column.index);
872
+ }
873
+ }
874
+ if (groupNames[0] && groupNames[1]) {
875
+ // show group options
876
+ e.items.push({
877
+ title: self.attributes.showGroupColumns.replace(
878
+ '%s',
879
+ groupTitles[0] + '-' + groupTitles[1],
880
+ ),
881
+ click: function (ev) {
882
+ ev.preventDefault();
883
+ self.groupColumns(groupNames[0], groupNames[1]);
884
+ self.controlInput.focus();
885
+ },
886
+ });
887
+ }
888
+ }
889
+ }
890
+ if (canUngroupColumns) {
891
+ const columnIndex = e.cell.columnIndex;
892
+ const groups = self.getGroupsColumnBelongsTo(columnIndex);
893
+ const columns = getColumnsMap();
894
+ for (let i = 0; i < groups.length; i++) {
895
+ const { from, to } = groups[i];
896
+ const cell0 = columns.get(from);
897
+ const cell1 = columns.get(to);
898
+ if (cell0 && cell1) {
899
+ const formatArgs =
900
+ (cell0.title || cell0.name || cell0.index) +
901
+ '-' +
902
+ (cell1.title || cell1.name || cell1.index);
903
+ e.items.push({
904
+ title: self.attributes.showRemoveGroupColumns.replace(
905
+ '%s',
906
+ formatArgs,
907
+ ),
908
+ click: function (ev) {
909
+ ev.preventDefault();
910
+ self.removeGroupColumns(cell0.name, cell1.name);
911
+ self.controlInput.focus();
912
+ },
913
+ });
914
+ } else {
915
+ console.warn(`Cannot find column ${from} or column ${to}`);
916
+ }
917
+ }
918
+ }
919
+ if (canGroupByRows) {
920
+ const range = self.getSelectedContiguousRows(false) || [];
921
+ const rangeTitle = range
922
+ .map((rowIndex) => {
923
+ const index = self.getBoundRowIndexFromViewRowIndex(rowIndex);
924
+ if (typeof index === 'number') return index + 1;
925
+ return rowIndex + 1;
926
+ })
927
+ .join('-');
928
+ if (
929
+ range.length === 2 &&
930
+ self.isNewGroupRangeValid(self.groupedRows, range[0], range[1])
931
+ ) {
932
+ e.items.push({
933
+ title: self.attributes.showGroupRows.replace('%s', rangeTitle),
934
+ click: function (ev) {
935
+ ev.preventDefault();
936
+ self.groupRows(range[0], range[1]);
937
+ },
938
+ });
939
+ }
940
+ }
941
+ if (canUngroupRows) {
942
+ const rowIndex = e.cell.rowIndex;
943
+ const groups = self.getGroupsRowBelongsTo(rowIndex);
944
+ for (let i = 0; i < groups.length; i++) {
945
+ const { from, to } = groups[i];
946
+ const rangeTitle = [from, to]
947
+ .map((rowIndex) => {
948
+ const index = self.getBoundRowIndexFromViewRowIndex(rowIndex);
949
+ if (typeof index === 'number') return index + 1;
950
+ return rowIndex + 1;
951
+ })
952
+ .join('-');
953
+ e.items.push({
954
+ title: self.attributes.showRemoveGroupRows.replace('%s', rangeTitle),
955
+ click: function (ev) {
956
+ ev.preventDefault();
957
+ self.removeGroupRows(from, to);
958
+ self.controlInput.focus();
959
+ },
960
+ });
961
+ }
962
+ }
963
+ //#endregion group/ungroup columns
660
964
  }
661
965
  self.disposeAutocomplete = function () {
662
966
  if (autoCompleteContext) {
@@ -664,7 +968,18 @@ export default function (self) {
664
968
  autoCompleteContext = undefined;
665
969
  }
666
970
  };
667
- self.disposeContextMenu = function () {
971
+ self.disposeContextMenu = function (event) {
972
+ if (
973
+ event &&
974
+ event.type === 'click' &&
975
+ event.ctrlKey &&
976
+ self.contextMenuOpenedAt !== undefined &&
977
+ performance.now() - self.contextMenuOpenedAt < 500
978
+ ) {
979
+ // macOS Safari dispatches a click right after a ctrl-click contextmenu,
980
+ // which would close the menu immediately (#521)
981
+ return;
982
+ }
668
983
  document.removeEventListener('click', self.disposeContextMenu);
669
984
  zIndexTop = self.style.contextMenuZIndex;
670
985
  self.disposeAutocomplete();
@@ -672,15 +987,67 @@ export default function (self) {
672
987
  self.contextMenu.dispose();
673
988
  }
674
989
  self.contextMenu = undefined;
990
+ if (event) {
991
+ self.canvas.focus();
992
+ self.mousedown(event);
993
+ self.mouseup(event);
994
+ }
675
995
  };
676
996
  self.contextmenuEvent = function (e, overridePos) {
677
997
  if (!self.hasFocus && e.target !== self.canvas) {
678
998
  return;
679
999
  }
680
- function createDiposeEvent() {
1000
+ // don't create context menu for parents if current position is located in child grid
1001
+ const children = Object.keys(self.childGrids);
1002
+ for (let i = 0; i < children.length; i++) {
1003
+ const childGrid = self.childGrids[children[i]];
1004
+ const parentNode = childGrid && childGrid.parentNode;
1005
+ if (!parentNode) continue;
1006
+ const { offsetLeft, offsetWidth, offsetTop, offsetHeight } = parentNode;
1007
+ if ((e.x >= offsetLeft && e.x <= offsetLeft + offsetWidth) === false)
1008
+ continue;
1009
+ if ((e.y >= offsetTop && e.y <= offsetTop + offsetHeight) === false)
1010
+ continue;
1011
+ return; // in child grid
1012
+ }
1013
+ // don't create context menu for child if current position is located in parent grid
1014
+ if (self.isChildGrid && self.parentNode) {
1015
+ //#region check is current child grid closed
1016
+ const childGridsOfParent = self.parentGrid && self.parentGrid.childGrids;
1017
+ if (!childGridsOfParent || !Array.isArray(childGridsOfParent)) return;
1018
+ const matchedMe = childGridsOfParent.find((grid) => {
1019
+ const nodeA = grid.parentNode;
1020
+ const nodeB = self.parentNode;
1021
+ return (
1022
+ nodeA.offsetTop == nodeB.offsetTop &&
1023
+ nodeA.offsetLeft === nodeB.offsetLeft
1024
+ );
1025
+ });
1026
+ if (!matchedMe) return;
1027
+ //#endregion
1028
+
1029
+ let x0 = self.parentNode.offsetLeft;
1030
+ let x1 = self.parentNode.offsetLeft + self.parentNode.offsetWidth;
1031
+ let y0 = self.parentNode.offsetTop;
1032
+ let y1 = self.parentNode.offsetTop + self.parentNode.offsetHeight;
1033
+ let node = self.parentNode.parentNode;
1034
+ while (node) {
1035
+ const { offsetLeft, offsetWidth, offsetTop, offsetHeight } = node;
1036
+ if (offsetLeft > x0) x0 = offsetLeft;
1037
+ if (offsetTop > y0) y0 = offsetTop;
1038
+ const newX1 = offsetLeft + offsetWidth;
1039
+ const newY1 = offsetTop + offsetHeight;
1040
+ if (newX1 < x1) x1 = newX1;
1041
+ if (newY1 < y1) y1 = newY1;
1042
+ if (node.nodeType !== 'canvas-datagrid-tree') break;
1043
+ node = node.parentNode;
1044
+ }
1045
+ if ((e.x >= x0 && e.x <= x1 && e.y >= y0 && e.y <= y1) === false) return;
1046
+ }
1047
+ function createDisposeEvent() {
681
1048
  requestAnimationFrame(function () {
682
1049
  document.addEventListener('click', self.disposeContextMenu);
683
- document.removeEventListener('mouseup', createDiposeEvent);
1050
+ window.removeEventListener('mouseup', createDisposeEvent);
684
1051
  });
685
1052
  }
686
1053
  var contextPosition,
@@ -689,12 +1056,13 @@ export default function (self) {
689
1056
  ev = {
690
1057
  NativeEvent: e,
691
1058
  cell: self.getCellAt(pos.x, pos.y),
1059
+ pos,
692
1060
  items: items,
693
1061
  };
694
1062
  if (!ev.cell.isGrid) {
695
1063
  addDefaultContextMenuItem(ev);
696
1064
  }
697
- if (self.dispatchEvent('contextmenu', ev)) {
1065
+ if (e.type !== 'mousedown' && self.dispatchEvent('contextmenu', ev)) {
698
1066
  return;
699
1067
  }
700
1068
  if (!ev.cell.isGrid) {
@@ -722,8 +1090,18 @@ export default function (self) {
722
1090
  contextPosition.width =
723
1091
  self.width - self.style.mobileContextMenuMargin * 2 + 'px';
724
1092
  }
725
- self.contextMenu = createContextMenu(ev, contextPosition, items);
726
- document.addEventListener('mouseup', createDiposeEvent);
1093
+ if (e.type == 'mousedown') {
1094
+ contextPosition.top += self.style.filterButtonMenuOffsetTop;
1095
+ }
1096
+ // use ev.items, not the captured array: a listener may assign a new
1097
+ // array to e.items (#263)
1098
+ self.contextMenu = createContextMenu(ev, contextPosition, ev.items);
1099
+ self.contextMenuOpenedAt = performance.now();
1100
+ if (e.type == 'mousedown') {
1101
+ window.addEventListener('mouseup', createDisposeEvent);
1102
+ } else {
1103
+ createDisposeEvent();
1104
+ }
727
1105
  e.preventDefault();
728
1106
  }
729
1107
  };