canvas-datagrid 0.4.4 → 0.4.6

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.
@@ -493,6 +493,8 @@ export default function (self) {
493
493
  y = self.mouse.y,
494
494
  cell = self.getCellAt(x, y),
495
495
  delta,
496
+ disallowVerticalAutoScroll = false,
497
+ disallowHorizontalAutoScroll = false,
496
498
  ev = { NativeEvent: e, cell: cell, x: x, y: y },
497
499
  previousCell = self.currentCell;
498
500
  clearTimeout(self.scrollTimer);
@@ -700,32 +702,9 @@ export default function (self) {
700
702
  if (self.hovers.onFilterButton) return;
701
703
  if (self.hovers.onCellTreeIcon) return;
702
704
  if (!self.dragAddToSelection && cell.rowIndex !== undefined) {
703
- if (
704
- self.selections[cell.rowIndex] &&
705
- self.selections[cell.rowIndex].indexOf(cell.columnIndex) !==
706
- -1
707
- ) {
708
- self.selections[cell.rowIndex].splice(
709
- self.selections[cell.rowIndex].indexOf(cell.columnIndex),
710
- 1,
711
- );
712
- }
705
+ if (self.isCellSelected(cell)) self.unselectCell(cell);
713
706
  } else {
714
- self.selections[cell.rowIndex] =
715
- self.selections[cell.rowIndex] || [];
716
- if (
717
- self.selections[cell.rowIndex].indexOf(cell.columnIndex) ===
718
- -1
719
- ) {
720
- self.selections[cell.rowIndex].push(cell.columnIndex);
721
- var event = {
722
- selections: self.selections,
723
- selectedData: self.getSelectedData(),
724
- selectedCells: self.getSelectedCells(),
725
- selectionBounds: self.getSelectionBounds(),
726
- };
727
- self.dispatchEvent('selectionchanged', event);
728
- }
707
+ if (!self.isCellSelected(cell)) self.selectCell(cell);
729
708
  }
730
709
  }
731
710
  }
@@ -738,11 +717,8 @@ export default function (self) {
738
717
  dragBounds.right !== self.selectionBounds.right) &&
739
718
  !ctrl
740
719
  ) {
741
- if (!(cell.hovered && self.hovers.onFilterButton)) {
742
- self.selections = [];
743
- self.selectedRows = [];
744
- self.selectedColumns = [];
745
- }
720
+ if (!(cell.hovered && self.hovers.onFilterButton))
721
+ self.clearSelections();
746
722
  if (dragBounds.top === -1) {
747
723
  dragBounds.top = 0;
748
724
  }
@@ -762,10 +738,33 @@ export default function (self) {
762
738
  }
763
739
  }
764
740
  }
741
+ } else if (self.movingSelectionHandle) {
742
+ delta = {
743
+ x: Math.abs(self.dragStart.x - x),
744
+ y: Math.abs(self.dragStart.y - y),
745
+ };
765
746
 
766
- if (self.attributes.autoScrollOnMousemove) {
767
- var movedVertically = delta.y > self.attributes.autoScrollMargin;
768
- var movedHorizontally = delta.x > self.attributes.autoScrollMargin;
747
+ // Disallow auto-scroll to the direction that overlay is not
748
+ // moving towards.
749
+ if (self.fillOverlay.direction === 'y') {
750
+ disallowHorizontalAutoScroll = true;
751
+ } else if (self.fillOverlay.direction === 'x') {
752
+ disallowVerticalAutoScroll = true;
753
+ }
754
+ }
755
+
756
+ if (delta) {
757
+ if (
758
+ self.attributes.autoScrollOnMousemove ||
759
+ disallowVerticalAutoScroll ||
760
+ disallowHorizontalAutoScroll
761
+ ) {
762
+ var movedVertically =
763
+ !disallowVerticalAutoScroll &&
764
+ delta.y > self.attributes.autoScrollMargin;
765
+ var movedHorizontally =
766
+ !disallowHorizontalAutoScroll &&
767
+ delta.x > self.attributes.autoScrollMargin;
769
768
 
770
769
  if (movedVertically && !movedHorizontally)
771
770
  self.autoScrollZone(e, -1, y, ctrl);
@@ -807,17 +806,7 @@ export default function (self) {
807
806
  if (startingBounds === JSON.stringify(sb)) {
808
807
  return;
809
808
  }
810
- ev = {
811
- selections: self.selections,
812
- selectionBounds: self.getSelectionBounds(),
813
- selectedCells: self.getSelectedCells(),
814
- };
815
- Object.defineProperty(ev, 'selectedData', {
816
- get: function () {
817
- return self.getSelectedData();
818
- },
819
- });
820
- self.dispatchEvent('selectionchanged', ev);
809
+ self.dispatchEvent('selectionchanged', self.getContextOfSelectionEvent());
821
810
  }
822
811
  if (self.input) {
823
812
  self.endEdit();
@@ -889,7 +878,6 @@ export default function (self) {
889
878
  return;
890
879
  }
891
880
  }
892
- self.selections[i.rowIndex] = self.selections[i.rowIndex] || [];
893
881
  if (
894
882
  self.attributes.selectionMode === 'row' ||
895
883
  self.currentCell.style === 'rowHeaderCell'
@@ -922,6 +910,7 @@ export default function (self) {
922
910
  self.draw(true);
923
911
  };
924
912
  self.dragResizeColumn = function (e) {
913
+ const resizingColumn = self.dragMode === 'ew-resize';
925
914
  var pos, x, y;
926
915
  pos = self.getLayerPos(e);
927
916
  x = self.resizingStartingWidth + pos.x - self.dragStart.x;
@@ -933,14 +922,31 @@ export default function (self) {
933
922
  y = self.style.minRowHeight;
934
923
  }
935
924
  if (
936
- self.dispatchEvent('resizecolumn', {
925
+ self.dispatchEvent(resizingColumn ? 'resizecolumn' : 'resizerow', {
937
926
  x: x,
938
927
  y: y,
928
+ width: x,
929
+ height: y,
930
+ columnIndex: resizingColumn ? self.draggingItem.columnIndex : undefined,
931
+ rowIndex: resizingColumn ? undefined : self.draggingItem.rowIndex,
939
932
  draggingItem: self.draggingItem,
940
933
  })
941
934
  ) {
942
935
  return false;
943
936
  }
937
+ if (self.attributes.resizeAfterDragged) {
938
+ self.pendingDragResize = {
939
+ item: self.draggingItem,
940
+ width: x,
941
+ height: y,
942
+ x: e.clientX,
943
+ y: e.clientY,
944
+ };
945
+ } else {
946
+ self.dragResizeApply(self.draggingItem, x, y);
947
+ }
948
+ };
949
+ self.dragResizeApply = function (draggingItem, width, height) {
944
950
  if (
945
951
  self.scrollBox.scrollLeft >
946
952
  self.scrollBox.scrollWidth - self.attributes.resizeScrollZone &&
@@ -950,29 +956,27 @@ export default function (self) {
950
956
  }
951
957
  if (self.dragMode === 'ew-resize') {
952
958
  self.sizes.columns[
953
- self.draggingItem.header.style === 'rowHeaderCell'
959
+ draggingItem.header.style === 'rowHeaderCell'
954
960
  ? 'cornerCell'
955
- : self.draggingItem.sortColumnIndex
956
- ] = x;
961
+ : draggingItem.sortColumnIndex
962
+ ] = width;
957
963
  if (
958
- ['rowHeaderCell', 'cornerCell'].indexOf(
959
- self.draggingItem.header.style,
960
- ) !== -1
964
+ ['rowHeaderCell', 'cornerCell'].indexOf(draggingItem.header.style) !==
965
+ -1
961
966
  ) {
962
967
  self.resize(true);
963
968
  }
964
969
  self.resizeChildGrids();
965
970
  return;
966
- }
967
- if (self.dragMode === 'ns-resize') {
968
- if (self.draggingItem.rowOpen) {
969
- self.sizes.trees[self.draggingItem.rowIndex] = y;
971
+ } else if (self.dragMode === 'ns-resize') {
972
+ if (draggingItem.rowOpen) {
973
+ self.sizes.trees[draggingItem.rowIndex] = height;
970
974
  } else if (self.attributes.globalRowResize) {
971
- self.style.cellHeight = y;
975
+ self.style.cellHeight = height;
972
976
  } else {
973
- self.sizes.rows[self.draggingItem.rowIndex] = y;
977
+ self.sizes.rows[draggingItem.rowIndex] = height;
974
978
  }
975
- self.dispatchEvent('resizerow', { row: y });
979
+ self.dispatchEvent('resizerow', { row: height });
976
980
  self.resizeChildGrids();
977
981
  return;
978
982
  }
@@ -981,6 +985,15 @@ export default function (self) {
981
985
  self.stopDragResize = function (event) {
982
986
  const pos = self.getLayerPos(event);
983
987
 
988
+ if (self.attributes.resizeAfterDragged) {
989
+ self.dragResizeApply(
990
+ self.pendingDragResize.item,
991
+ self.pendingDragResize.width,
992
+ self.pendingDragResize.height,
993
+ );
994
+ self.pendingDragResize = undefined;
995
+ }
996
+
984
997
  if (self.dragMode === 'ew-resize') {
985
998
  const hasMoved = !!(pos.x - self.dragStart.x);
986
999
  // Check that dragItem is selected or part of selection.
@@ -996,6 +1009,18 @@ export default function (self) {
996
1009
  // If the column is selected, resize it to width plus any other selected columns.
997
1010
  self.fitSelectedColumns(width);
998
1011
  }
1012
+ } else if (self.dragMode === 'ns-resize') {
1013
+ // Do the above for rows.
1014
+ const hasMoved = !!(pos.y - self.dragStart.y);
1015
+ const dragItemIsSelected = self.isRowSelected(self.dragItem.rowIndex);
1016
+
1017
+ if (hasMoved && dragItemIsSelected) {
1018
+ const height = Math.max(
1019
+ self.resizingStartingHeight + pos.y - self.dragStart.y,
1020
+ self.style.minRowHeight,
1021
+ );
1022
+ self.fitSelectedRows(height);
1023
+ }
999
1024
  }
1000
1025
 
1001
1026
  self.resize();
@@ -1169,17 +1194,16 @@ export default function (self) {
1169
1194
  }
1170
1195
  };
1171
1196
  self.stopDragReorder = function (e) {
1172
- var oIndex,
1173
- tIndex,
1174
- odata = [],
1175
- cr = {
1176
- 'row-reorder': self.orders.rows,
1177
- 'column-reorder': self.orders.columns,
1178
- },
1179
- i = {
1180
- 'row-reorder': 'rowIndex',
1181
- 'column-reorder': 'sortColumnIndex',
1182
- }[self.dragMode];
1197
+ let originalIndex;
1198
+ let targetIndex;
1199
+ const orderLists = {
1200
+ 'row-reorder': self.orders.rows,
1201
+ 'column-reorder': self.orders.columns,
1202
+ };
1203
+ const indexName = {
1204
+ 'row-reorder': 'rowIndex',
1205
+ 'column-reorder': 'sortColumnIndex',
1206
+ }[self.dragMode];
1183
1207
  window.removeEventListener('mousemove', self.dragReorder, false);
1184
1208
  window.removeEventListener('mouseup', self.stopDragReorder, false);
1185
1209
  if (
@@ -1191,7 +1215,7 @@ export default function (self) {
1191
1215
  (self.dragMode === 'row-reorder' &&
1192
1216
  self.reorderTarget.rowIndex > -1 &&
1193
1217
  self.reorderTarget.rowIndex < self.viewData.length)) &&
1194
- self.reorderObject[i] !== self.reorderTarget[i] &&
1218
+ self.reorderObject[indexName] !== self.reorderTarget[indexName] &&
1195
1219
  !self.dispatchEvent('reorder', {
1196
1220
  NativeEvent: e,
1197
1221
  source: self.reorderObject,
@@ -1200,61 +1224,64 @@ export default function (self) {
1200
1224
  })
1201
1225
  ) {
1202
1226
  self.ignoreNextClick = true;
1203
- oIndex = cr[self.dragMode].indexOf(self.reorderObject[i]);
1204
- tIndex = cr[self.dragMode].indexOf(self.reorderTarget[i]);
1227
+ originalIndex = orderLists[self.dragMode].indexOf(
1228
+ self.reorderObject[indexName],
1229
+ );
1230
+ targetIndex = orderLists[self.dragMode].indexOf(
1231
+ self.reorderTarget[indexName],
1232
+ );
1205
1233
  if (self.dragMode === 'column-reorder') {
1206
- var sortColumnIndices = [];
1207
- var selectedColumnIndices = [];
1208
- if (self.selections[0]) {
1209
- oIndex = self.selections[0][0];
1210
- self.selections[0].forEach(function (value) {
1234
+ /** Select column view indexes in the first row */
1235
+ const selectedIndexes = self.getRowSelectionStates(0);
1236
+ const sortColumnIndices = [];
1237
+ const selectedColumnIndices = [];
1238
+ if (selectedIndexes) {
1239
+ originalIndex = selectedIndexes[0];
1240
+ selectedIndexes.forEach(function (value) {
1211
1241
  sortColumnIndices.push(self.orders.columns[value]);
1212
1242
  });
1213
1243
  }
1214
- var deleteCount = sortColumnIndices.length;
1244
+ const deleteCount = sortColumnIndices.length;
1215
1245
  if (
1216
- tIndex < oIndex ||
1217
- (tIndex > oIndex && Math.abs(tIndex - oIndex) >= deleteCount)
1246
+ targetIndex < originalIndex ||
1247
+ (targetIndex > originalIndex &&
1248
+ Math.abs(targetIndex - originalIndex) >= deleteCount)
1218
1249
  ) {
1219
- cr[self.dragMode].splice(oIndex, deleteCount);
1220
- if (tIndex > oIndex) tIndex = tIndex - (deleteCount - 1);
1221
-
1222
- self.selectedColumns = [];
1223
- for (var i = 0; i < sortColumnIndices.length; i++) {
1224
- if (i === 0) self.activeCell.columnIndex = tIndex;
1225
- selectedColumnIndices.push(tIndex + i);
1226
- self.selectedColumns.push(sortColumnIndices[i]);
1227
- cr[self.dragMode].splice(tIndex + i, 0, sortColumnIndices[i]);
1250
+ orderLists[self.dragMode].splice(originalIndex, deleteCount);
1251
+ if (targetIndex > originalIndex)
1252
+ targetIndex = targetIndex - (deleteCount - 1);
1253
+
1254
+ for (let i = 0; i < sortColumnIndices.length; i++) {
1255
+ if (i === 0) self.activeCell.columnIndex = targetIndex;
1256
+ selectedColumnIndices.push(targetIndex + i);
1257
+ orderLists[self.dragMode].splice(
1258
+ targetIndex + i,
1259
+ 0,
1260
+ sortColumnIndices[i],
1261
+ );
1228
1262
  }
1229
1263
 
1230
- self.orders.columns = cr[self.dragMode];
1231
- self.viewData.forEach(function (row, rowIndex) {
1232
- self.selections[rowIndex] = selectedColumnIndices;
1233
- });
1264
+ self.orders.columns = orderLists[self.dragMode];
1265
+ self.selectColumnViewIndexes(selectedColumnIndices);
1234
1266
  }
1235
1267
  } else {
1236
- var selectedRows = [];
1237
- self.selections.forEach(function (row, rowIndex) {
1238
- if (row) {
1239
- selectedRows.push(row);
1240
- odata.push(self.viewData[rowIndex]);
1241
- }
1242
- });
1268
+ /** The original name of this variable is `odata` */
1269
+ const rowIndexes = self.getRowViewIndexesFromSelection();
1270
+ const originalData = rowIndexes.map(
1271
+ (rowIndex) => self.viewData[rowIndex],
1272
+ );
1243
1273
  if (
1244
- tIndex < oIndex ||
1245
- (tIndex > oIndex && Math.abs(tIndex - oIndex) >= odata.length)
1274
+ targetIndex < originalIndex ||
1275
+ (targetIndex > originalIndex &&
1276
+ Math.abs(targetIndex - originalIndex) >= originalData.length)
1246
1277
  ) {
1247
- self.viewData.splice(oIndex, odata.length);
1248
- self.selections = [];
1249
- self.selectedRows = [];
1250
- if (tIndex > oIndex) tIndex = tIndex - (odata.length - 1);
1251
- self.activeCell.rowIndex = tIndex;
1252
-
1253
- for (var i = 0; i < odata.length; i++) {
1254
- self.viewData.splice(tIndex + i, 0, odata[i]);
1255
- self.selections[tIndex + i] = selectedRows[i];
1256
- self.selectedRows.push(tIndex + i);
1257
- }
1278
+ self.viewData.splice(originalIndex, originalData.length);
1279
+ if (targetIndex > originalIndex)
1280
+ targetIndex = targetIndex - (originalData.length - 1);
1281
+ self.activeCell.rowIndex = targetIndex;
1282
+ for (let i = 0; i < originalData.length; i++)
1283
+ self.viewData.splice(targetIndex + i, 0, originalData[i]);
1284
+ self.moveSelection(0, targetIndex);
1258
1285
  }
1259
1286
  }
1260
1287
  self.resize();
@@ -1445,7 +1472,8 @@ export default function (self) {
1445
1472
  var ctrl = e.ctrlKey || e.metaKey,
1446
1473
  move = /-move/.test(self.dragMode),
1447
1474
  freeze = /frozen-row-marker|frozen-column-marker/.test(self.dragMode),
1448
- resize = /-resize/.test(self.dragMode);
1475
+ resize = /-resize/.test(self.dragMode),
1476
+ selectionHandleMove = /selection-handle-br/.test(self.dragMode);
1449
1477
  const onUnhideIndicator = self.hovers && self.hovers.unhideIndicator;
1450
1478
  self.dragStart = overridePos || self.getLayerPos(e);
1451
1479
  self.scrollStart = {
@@ -1464,15 +1492,14 @@ export default function (self) {
1464
1492
  !self.currentCell.isColumnHeader &&
1465
1493
  !move &&
1466
1494
  !freeze &&
1467
- !resize
1495
+ !resize &&
1496
+ !selectionHandleMove
1468
1497
  ) {
1469
1498
  if (
1470
1499
  !(self.dragMode == 'row-reorder' && self.isMultiRowsSelected) &&
1471
1500
  !(self.currentCell.hovered && self.hovers.onFilterButton)
1472
1501
  ) {
1473
- self.selections = [];
1474
- self.selectedRows = [];
1475
- self.selectedColumns = [];
1502
+ self.clearSelections();
1476
1503
  }
1477
1504
  }
1478
1505
  if (self.dragStartObject.isGrid) {
@@ -1523,9 +1550,26 @@ export default function (self) {
1523
1550
  }
1524
1551
  return;
1525
1552
  }
1553
+ if (selectionHandleMove) {
1554
+ self.movingSelectionHandle = true;
1555
+ self.fillOverlay = {
1556
+ handle: {
1557
+ x: self.dragStartObject.x + self.dragStartObject.width / 2,
1558
+ y: self.dragStartObject.y + self.dragStartObject.height / 2,
1559
+ },
1560
+ snapTo: { x: -1, y: -1 },
1561
+ selection: self.getSelectionBounds(),
1562
+ };
1563
+ if (self.dispatchEvent('beginselectionhandlemove', { NativeEvent: e })) {
1564
+ return;
1565
+ }
1566
+ window.addEventListener('mousemove', self.selectionHandleMove, false);
1567
+ window.addEventListener('mouseup', self.stopSelectionHandleMove, false);
1568
+ return self.selectionHandleMove(e);
1569
+ }
1526
1570
  if (move) {
1527
1571
  self.draggingItem = self.dragItem;
1528
- self.movingSelection = self.selections.concat([]);
1572
+ self.movingSelection = self.cloneSelections();
1529
1573
  self.dragging = self.dragStartObject;
1530
1574
  if (
1531
1575
  self.dispatchEvent('beginmove', {
@@ -1733,7 +1777,7 @@ export default function (self) {
1733
1777
 
1734
1778
  // TODO - most likely some column order related bugs in key based selection
1735
1779
  // Arrows
1736
- var isArrowKey = [
1780
+ const isArrowKey = [
1737
1781
  'ArrowLeft',
1738
1782
  'ArrowUp',
1739
1783
  'ArrowRight',
@@ -1742,63 +1786,12 @@ export default function (self) {
1742
1786
 
1743
1787
  // Shrinking and expanding selection using shift and arrow keys
1744
1788
  if (e.shiftKey && isArrowKey) {
1745
- const firstSelectedRowIndex = self.selections.findIndex((el) => !!el);
1746
- const firstSelectedRow = self.selections[firstSelectedRowIndex];
1747
- const firstSelectedColumnIndex = firstSelectedRow[0];
1748
- const lastSelectedColumn = firstSelectedRow[firstSelectedRow.length - 1];
1749
- const yAtTop = y === 0;
1750
- const yAtBottom = y === last;
1751
- const xAtLeft = x === 0;
1752
- const xAtRight = x === cols;
1753
-
1754
- if (e.key === 'ArrowUp') {
1755
- if (y + 1 > firstSelectedRowIndex && !yAtTop) {
1756
- self.selections.pop();
1757
- } else if (y < firstSelectedRowIndex) {
1758
- self.selections[y] = self.selections[y] || [];
1759
- self.selections[y].push(x);
1760
- } else if (yAtTop && self.activeCell.rowIndex !== 0) {
1761
- self.selections.pop();
1762
- }
1763
- }
1764
-
1765
- if (e.key === 'ArrowDown') {
1766
- if (y > firstSelectedRowIndex && y === self.selections.length) {
1767
- self.selections[y] = self.selections[y] || [];
1768
- self.selections[y].push(x);
1769
- } else if (y >= firstSelectedRowIndex && !yAtBottom) {
1770
- delete self.selections[y - 1];
1771
- } else if (yAtBottom && self.activeCell.rowIndex !== last) {
1772
- delete self.selections[y - 1];
1773
- }
1774
- }
1775
-
1776
- for (const selection of self.selections) {
1777
- if (e.key === 'ArrowRight' && selection) {
1778
- if (x > lastSelectedColumn) {
1779
- selection.push(x);
1780
- } else if (x <= lastSelectedColumn && !xAtRight) {
1781
- selection.shift();
1782
- } else if (xAtRight && self.activeCell.columnIndex !== cols) {
1783
- selection.shift();
1784
- }
1785
- }
1786
-
1787
- if (e.key === 'ArrowLeft' && selection) {
1788
- if (x < firstSelectedColumnIndex) {
1789
- selection.unshift(x);
1790
- } else if (x >= firstSelectedColumnIndex && !xAtLeft) {
1791
- selection.pop();
1792
- } else if (xAtLeft && self.activeCell.columnIndex !== 0) {
1793
- selection.pop();
1794
- }
1795
- }
1796
- }
1797
-
1798
- self.selectionBounds = self.getSelectionBounds();
1799
- self.selectArea(undefined, ctrl);
1800
-
1801
- self.draw(true);
1789
+ const changed = self.shrinkOrExpandSelections(
1790
+ { rowIndex: y, columnIndex: x },
1791
+ e,
1792
+ true,
1793
+ );
1794
+ if (changed) self.draw(true);
1802
1795
  }
1803
1796
 
1804
1797
  if (x !== self.activeCell.columnIndex || y !== self.activeCell.rowIndex) {
@@ -1809,25 +1802,8 @@ export default function (self) {
1809
1802
 
1810
1803
  self.setActiveCell(x, y);
1811
1804
  if (!e.shiftKey && self.attributes.selectionFollowsActiveCell) {
1812
- if (!ctrl) {
1813
- self.selections = [];
1814
- self.selectedRows = [];
1815
- self.selectedColumns = [];
1816
- }
1817
- self.selections[y] = self.selections[y] || [];
1818
- self.selections[y].push(x);
1819
- ev = {
1820
- selectedData: self.getSelectedData(),
1821
- selections: self.selections,
1822
- selectionBounds: self.getSelectionBounds(),
1823
- selectedCells: self.getSelectedCells(),
1824
- };
1825
- Object.defineProperty(ev, 'selectedData', {
1826
- get: function () {
1827
- return self.getSelectedData();
1828
- },
1829
- });
1830
- self.dispatchEvent('selectionchanged', ev);
1805
+ if (!ctrl) self.clearSelections();
1806
+ self.selectCell({ rowIndex: y, viewColumnIndex: x });
1831
1807
  }
1832
1808
  self.draw(true);
1833
1809
  }
@@ -1908,7 +1884,7 @@ export default function (self) {
1908
1884
  deltaY = e.deltaY === undefined ? e.NativeEvent.deltaY : e.deltaY,
1909
1885
  deltaMode =
1910
1886
  e.deltaMode === undefined ? e.NativeEvent.deltaMode : e.deltaMode;
1911
- var e = e.NativeEvent || e;
1887
+ if (e.NativeEvent) e = e.NativeEvent;
1912
1888
  if (wheeling) {
1913
1889
  ev.preventDefault(e);
1914
1890
  return;
@@ -1947,6 +1923,8 @@ export default function (self) {
1947
1923
  mimeType,
1948
1924
  startRowIndex,
1949
1925
  startColIndex,
1926
+ minRowsLength = 0,
1927
+ minColumnsLength = 0,
1950
1928
  ) {
1951
1929
  if (mimeType === 'text/html' && !isSupportedHtml(pasteValue)) {
1952
1930
  console.warn(
@@ -1958,82 +1936,57 @@ export default function (self) {
1958
1936
 
1959
1937
  return;
1960
1938
  }
1961
- var schema = self.getSchema();
1962
- var rows = parseData(pasteValue, mimeType);
1939
+
1940
+ const rows = parseData(pasteValue, mimeType);
1941
+ const columnsLength = rows[0].length;
1963
1942
 
1964
1943
  // selected cell. This mimics Excel's paste functionality, and works
1965
1944
  // as a poor-man's fill-down.
1966
- if (rows.length === 1 && rows[0].length === 1) {
1945
+ if (
1946
+ rows.length === 1 &&
1947
+ columnsLength === 1 &&
1948
+ minRowsLength <= 1 &&
1949
+ minColumnsLength <= 1
1950
+ ) {
1967
1951
  var cellData = rows[0][0].value.map((item) => item.value).join();
1968
1952
 
1969
1953
  self.forEachSelectedCell(function (data, rowIndex, colName) {
1970
1954
  data[rowIndex][colName] = cellData;
1971
1955
  });
1972
1956
  } else {
1973
- var selections = [];
1974
- self.selectedRows = [];
1975
- self.selectedColumns = [];
1976
- for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) {
1977
- // Rows may have been moved by user, so get the actual row index
1978
- // (instead of the row index at which the row is rendered):
1979
- var realRowIndex = self.orders.rows[startRowIndex + rowIndex];
1980
- var cells = rows[rowIndex];
1981
-
1982
- var existingRowData = self.viewData[realRowIndex];
1983
- var newRowData = Object.assign({}, existingRowData);
1984
-
1985
- selections[realRowIndex] = [];
1986
- self.selectedRows.push(realRowIndex);
1987
-
1988
- for (var colIndex = 0; colIndex < cells.length; colIndex++) {
1989
- var column = schema[startColIndex + colIndex];
1990
-
1991
- if (!column) {
1992
- console.warn('Paste data exceeded grid bounds. Skipping.');
1993
- continue;
1994
- }
1995
-
1996
- var columnName = column.name;
1997
- var cellData = cells[colIndex].value
1998
- .map((item) => item.value)
1999
- .join('');
2000
-
2001
- if (cellData === undefined || cellData === null) {
2002
- newRowData[columnName] = existingRowData[columnName];
2003
- continue;
2004
- }
2005
-
2006
- selections[realRowIndex].push(startColIndex + colIndex);
2007
-
2008
- newRowData[columnName] = cellData;
2009
- if (self.selectedColumns.indexOf(startColIndex + colIndex) === -1)
2010
- self.selectedColumns.push(startColIndex + colIndex);
2011
- }
2012
-
2013
- self.originalData[self.boundRowIndexMap.get(realRowIndex)] = newRowData;
2014
- // Update view date here to avoid a full refresh of `viewData`.
2015
- // To stay in line with Excel and Google Sheet behaviour,
2016
- // don't perform a full refresh (and filter/sort results)
2017
- // as this would make any pasted values disappear and/or suddenly change position.
2018
- self.viewData[realRowIndex] = newRowData;
1957
+ let direction = null;
1958
+
1959
+ if (minRowsLength > rows.length && minColumnsLength > columnsLength) {
1960
+ direction = 'both';
1961
+ } else if (minRowsLength > rows.length) {
1962
+ direction = 'vertical';
1963
+ } else if (minColumnsLength > columnsLength) {
1964
+ direction = 'horizontal';
2019
1965
  }
2020
- self.selections = selections;
1966
+
1967
+ self.insert({
1968
+ rows: rows,
1969
+ startRowIndex: startRowIndex,
1970
+ startColumnIndex: startColIndex,
1971
+ minRowsLength: minRowsLength,
1972
+ minColumnsLength: minColumnsLength,
1973
+ reverseRows: false,
1974
+ reverseColumns: false,
1975
+ clearSelections: true,
1976
+ alwaysFilling: false,
1977
+ direction: direction,
1978
+ });
2021
1979
  }
2022
1980
 
2023
1981
  // selections is a sparse array, so we condense:
2024
- var affectedCells = [];
2025
-
2026
- self.selections.forEach(function (row, rowIndex) {
2027
- if (rowIndex === undefined) return;
2028
-
2029
- row.forEach(function (columnIndex) {
2030
- affectedCells.push([
2031
- rowIndex,
2032
- columnIndex,
2033
- self.getBoundRowIndexFromViewRowIndex(rowIndex),
2034
- self.getBoundColumnIndexFromViewColumnIndex(columnIndex),
2035
- ]);
2036
- });
1982
+ const affectedCells = [];
1983
+ self.forEachSelectedCell((viewData, viewRowIndex, columnName, cell) => {
1984
+ affectedCells.push([
1985
+ viewRowIndex,
1986
+ cell.viewColumnIndex,
1987
+ cell.boundRowIndex,
1988
+ cell.boundColumnIndex,
1989
+ ]);
2037
1990
  });
2038
1991
 
2039
1992
  self.dispatchEvent('afterpaste', {
@@ -2042,6 +1995,158 @@ export default function (self) {
2042
1995
 
2043
1996
  return rows.length;
2044
1997
  };
1998
+ self.insert = function ({
1999
+ rows = [],
2000
+ startRowIndex = 0,
2001
+ startColumnIndex = 0,
2002
+ minRowsLength = 0,
2003
+ minColumnsLength = 0,
2004
+ reverseRows = false,
2005
+ reverseColumns = false,
2006
+ clearSelections = false,
2007
+ alwaysFilling = false,
2008
+ direction = 'both',
2009
+ }) {
2010
+ var schema = self.getSchema();
2011
+ const rowsLength = Math.max(rows.length, minRowsLength);
2012
+ const fillCellCallback = self.fillCellCallback;
2013
+ const filledCells = [];
2014
+
2015
+ if (clearSelections) self.selections = [];
2016
+
2017
+ for (
2018
+ let rowPosReal = 0, rowDataPos = 0;
2019
+ rowPosReal < rowsLength;
2020
+ rowPosReal++, rowDataPos++
2021
+ ) {
2022
+ if (rowDataPos >= rows.length) {
2023
+ rowDataPos = 0;
2024
+ }
2025
+
2026
+ const fillingRow = rowPosReal >= rows.length;
2027
+ const rowPosition = reverseRows
2028
+ ? rowsLength - rowPosReal - 1
2029
+ : rowPosReal;
2030
+ // Rows may have been moved by user, so get the actual row index
2031
+ // (instead of the row index at which the row is rendered):
2032
+ var realRowIndex = self.orders.rows[startRowIndex + rowPosition];
2033
+ var cells = rows[rowDataPos];
2034
+ const cellsLength = Math.max(cells.length, minColumnsLength);
2035
+
2036
+ var existingRowData = self.viewData[realRowIndex];
2037
+ var newRowData = Object.assign({}, existingRowData);
2038
+ const fillArgs = fillCellCallback
2039
+ ? {
2040
+ rows: rows,
2041
+ direction: direction,
2042
+ rowData: newRowData,
2043
+ existingRowData: existingRowData,
2044
+ rowIndex: realRowIndex,
2045
+ rowOffset: rowDataPos,
2046
+ cells: cells,
2047
+ reversed: direction === 'horizontal' ? reverseColumns : reverseRows,
2048
+ isFillingRow: fillingRow || alwaysFilling,
2049
+ fillingRowPosition: alwaysFilling
2050
+ ? rowPosReal
2051
+ : fillingRow
2052
+ ? rowPosReal - rows.length
2053
+ : -1,
2054
+ fillingRowLength: alwaysFilling
2055
+ ? rowsLength
2056
+ : fillingRow
2057
+ ? rowsLength - rows.length
2058
+ : -1,
2059
+ }
2060
+ : undefined;
2061
+
2062
+ for (
2063
+ var colPosReal = 0, cellDataPos = 0;
2064
+ colPosReal < cellsLength;
2065
+ colPosReal++, cellDataPos++
2066
+ ) {
2067
+ if (cellDataPos >= cells.length) {
2068
+ cellDataPos = 0;
2069
+ }
2070
+
2071
+ const fillingColumn = colPosReal >= cells.length;
2072
+ const colPosition = reverseColumns
2073
+ ? cellsLength - colPosReal - 1
2074
+ : colPosReal;
2075
+ const columnIndex = startColumnIndex + colPosition;
2076
+ var column = schema[columnIndex];
2077
+
2078
+ if (!column) {
2079
+ console.warn('Paste data exceeded grid bounds. Skipping.');
2080
+ continue;
2081
+ }
2082
+
2083
+ var columnName = column.name;
2084
+ var cellData = cells[cellDataPos];
2085
+ if (cellData && cellData.value) {
2086
+ cellData = cellData.value.map((item) => item.value).join('');
2087
+ }
2088
+
2089
+ const existingCellData = existingRowData[columnName];
2090
+
2091
+ if (
2092
+ fillCellCallback &&
2093
+ (fillingColumn || fillingRow || alwaysFilling)
2094
+ ) {
2095
+ newRowData[columnName] = fillCellCallback({
2096
+ ...fillArgs,
2097
+ column: column,
2098
+ columnIndex: columnIndex,
2099
+ columnOffset: cellDataPos,
2100
+ newCellData: cellData,
2101
+ existingCellData: existingCellData,
2102
+ isFillingColumn: fillingColumn || alwaysFilling,
2103
+ fillingColumnPosition: alwaysFilling
2104
+ ? colPosReal
2105
+ : fillingColumn
2106
+ ? colPosReal - cells.length
2107
+ : -1,
2108
+ fillingColumnLength: alwaysFilling
2109
+ ? cellsLength
2110
+ : fillingColumn
2111
+ ? cellsLength - cells.length
2112
+ : -1,
2113
+ });
2114
+ } else {
2115
+ newRowData[columnName] =
2116
+ cellData === undefined || cellData === null
2117
+ ? existingCellData
2118
+ : cellData;
2119
+ }
2120
+
2121
+ self.selectCell(
2122
+ { rowIndex: realRowIndex, viewColumnIndex: columnIndex },
2123
+ true,
2124
+ );
2125
+
2126
+ if (alwaysFilling || fillingRow || fillingColumn) {
2127
+ filledCells.push([
2128
+ realRowIndex,
2129
+ columnIndex,
2130
+ self.getBoundRowIndexFromViewRowIndex(realRowIndex),
2131
+ self.getBoundColumnIndexFromViewColumnIndex(columnIndex),
2132
+ ]);
2133
+ }
2134
+ }
2135
+
2136
+ self.originalData[self.boundRowIndexMap.get(realRowIndex)] = newRowData;
2137
+ // Update view date here to avoid a full refresh of `viewData`.
2138
+ // To stay in line with Excel and Google Sheet behaviour,
2139
+ // don't perform a full refresh (and filter/sort results)
2140
+ // as this would make any pasted values disappear and/or suddenly change position.
2141
+ self.viewData[realRowIndex] = newRowData;
2142
+ }
2143
+
2144
+ if (filledCells.length > 0 || alwaysFilling) {
2145
+ self.dispatchEvent('afterfill', {
2146
+ filledCells: filledCells,
2147
+ });
2148
+ }
2149
+ };
2045
2150
  self.getNextVisibleColumnIndex = function (visibleColumnIndex) {
2046
2151
  var x,
2047
2152
  s = self.getVisibleSchema();
@@ -2062,6 +2167,26 @@ export default function (self) {
2062
2167
  }
2063
2168
  return -1;
2064
2169
  };
2170
+ self.getSelectionIndex = function () {
2171
+ const bounds = self.getSelectionBounds(true);
2172
+ if (!bounds) return;
2173
+ const { top, bottom } = bounds;
2174
+ for (let rowIndex = top; rowIndex <= bottom; rowIndex++) {
2175
+ const row = self.getRowSelectionStates(rowIndex);
2176
+ if (row) {
2177
+ if (row[0] === undefined) break;
2178
+
2179
+ return {
2180
+ row: rowIndex,
2181
+ column: row[0] < 0 ? row[1] : row[0],
2182
+ rowLength: bottom - rowIndex + 1,
2183
+ columnLength: row.length - (row[0] < 0 ? 1 : 0),
2184
+ };
2185
+ }
2186
+ }
2187
+
2188
+ return null;
2189
+ };
2065
2190
  self.paste = function (event) {
2066
2191
  if (!self.attributes.editable) {
2067
2192
  return;
@@ -2098,28 +2223,24 @@ export default function (self) {
2098
2223
  return;
2099
2224
  }
2100
2225
 
2101
- var itemToPaste = pasteableItems[0];
2226
+ const selectionIndex = self.getSelectionIndex();
2227
+ if (!selectionIndex) return;
2102
2228
 
2103
- let startCell = [];
2104
- for (
2105
- let rowIndex = 0, len = self.selections.length;
2106
- rowIndex < len;
2107
- rowIndex++
2108
- ) {
2109
- const row = self.selections[rowIndex];
2110
- if (row) {
2111
- if (row[0] === undefined) return;
2112
- startCell = [rowIndex, row[0] < 0 ? row[1] : row[0]];
2113
- break;
2114
- }
2115
- }
2229
+ var itemToPaste = pasteableItems[0];
2116
2230
 
2117
2231
  // itemToPaste is cleared once accessed (getData or getAsString),
2118
2232
  // so we need to store the type here, before reading its value:
2119
2233
  var pasteType = itemToPaste.type;
2120
2234
 
2121
2235
  itemToPaste.getAsString(function (pasteValue) {
2122
- self.pasteData(pasteValue, pasteType, startCell[0], startCell[1]);
2236
+ self.pasteData(
2237
+ pasteValue,
2238
+ pasteType,
2239
+ selectionIndex.row,
2240
+ selectionIndex.column,
2241
+ selectionIndex.rowLength,
2242
+ selectionIndex.columnLength,
2243
+ );
2123
2244
 
2124
2245
  self.draw();
2125
2246
  });
@@ -2137,9 +2258,17 @@ export default function (self) {
2137
2258
  self.copySelectedCellsToClipboard(event.clipboardData);
2138
2259
 
2139
2260
  const affectedCells = self.clearSelectedCells();
2261
+ const apiCompatibleCells = affectedCells.map((cell) => {
2262
+ return [
2263
+ cell.viewRowIndex,
2264
+ cell.viewColumnIndex,
2265
+ cell.boundRowIndex,
2266
+ cell.boundColumnIndex,
2267
+ ];
2268
+ });
2140
2269
 
2141
2270
  self.dispatchEvent('aftercut', {
2142
- cells: affectedCells,
2271
+ cells: apiCompatibleCells,
2143
2272
  });
2144
2273
 
2145
2274
  requestAnimationFrame(() => self.draw());
@@ -2159,5 +2288,186 @@ export default function (self) {
2159
2288
 
2160
2289
  event.preventDefault();
2161
2290
  };
2291
+ self.selectionHandleMove = function (e) {
2292
+ if (!self.movingSelectionHandle) {
2293
+ return;
2294
+ }
2295
+
2296
+ const clippingRect = self.getClippingRect(e);
2297
+ const rowIndex = self.currentCell.rowIndex;
2298
+ const columnIndex = self.currentCell.columnIndex;
2299
+ const isInSelectionBounds =
2300
+ rowIndex >= -1 &&
2301
+ columnIndex >= -1 &&
2302
+ self.fillOverlay.selection.left <= columnIndex &&
2303
+ self.fillOverlay.selection.right >= columnIndex &&
2304
+ self.fillOverlay.selection.top <= rowIndex &&
2305
+ self.fillOverlay.selection.bottom >= rowIndex;
2306
+
2307
+ self.fillOverlay.minX = clippingRect.x;
2308
+ self.fillOverlay.minY = clippingRect.y;
2309
+ self.fillOverlay.x = Math.max(clippingRect.x, e.clientX);
2310
+ self.fillOverlay.y = Math.max(clippingRect.y, e.clientY);
2311
+
2312
+ // If we are in the selection bounds, allow user to change directions.
2313
+ if (isInSelectionBounds) {
2314
+ self.fillOverlay.lastInBoundsLocation = {
2315
+ x: e.clientX,
2316
+ y: e.clientY,
2317
+ };
2318
+ } else if (
2319
+ self.fillOverlay.lastInBoundsLocation ||
2320
+ !self.fillOverlay.direction
2321
+ ) {
2322
+ const lastInBoundsLocation = self.fillOverlay.lastInBoundsLocation;
2323
+ self.fillOverlay.lastInBoundsLocation = undefined;
2324
+
2325
+ const x = lastInBoundsLocation
2326
+ ? lastInBoundsLocation.x
2327
+ : self.fillOverlay.handle.x;
2328
+ const y = lastInBoundsLocation
2329
+ ? lastInBoundsLocation.y
2330
+ : self.fillOverlay.handle.y;
2331
+ const dx = Math.abs(e.clientX - x);
2332
+ const dy = Math.abs(e.clientY - y);
2333
+
2334
+ if (dx > 5 || dy > 5) {
2335
+ self.fillOverlay.direction = dx > dy ? 'x' : 'y';
2336
+ }
2337
+ }
2338
+
2339
+ if (rowIndex >= 0) {
2340
+ self.fillOverlay.rowIndex = rowIndex;
2341
+ }
2342
+ if (columnIndex >= 0) {
2343
+ self.fillOverlay.columnIndex = columnIndex;
2344
+ if (rowIndex === -1 && self.visibleRows.length) {
2345
+ self.fillOverlay.rowIndex = self.visibleRows[0];
2346
+ }
2347
+ }
2348
+
2349
+ if (rowIndex >= -1 && columnIndex >= -1) {
2350
+ self.fillOverlay.snap = self.currentCell;
2351
+ } else {
2352
+ self.fillOverlay.snap = undefined;
2353
+ }
2354
+ };
2355
+ self.stopSelectionHandleMove = function (e) {
2356
+ if (!self.fillOverlay.handle) {
2357
+ return false;
2358
+ }
2359
+
2360
+ self.ignoreNextClick = true;
2361
+
2362
+ window.removeEventListener('mousemove', self.selectionHandleMove, false);
2363
+ window.removeEventListener('mouseup', self.stopSelectionHandleMove, false);
2364
+
2365
+ const overlay = self.fillOverlay;
2366
+ const selectionIndex = self.getSelectionIndex();
2367
+ const bounds = overlay.selection;
2368
+ if (!selectionIndex) return;
2369
+
2370
+ self.movingSelectionHandle = undefined;
2371
+ self.fillOverlay = {};
2372
+
2373
+ if (overlay.rowIndex >= 0 && overlay.columnIndex >= 0) {
2374
+ const boundsOld = { ...bounds };
2375
+ const isVertical = overlay.direction === 'y';
2376
+ const isHorizontal = overlay.direction === 'x';
2377
+
2378
+ let startRow,
2379
+ rowLength,
2380
+ startColumn,
2381
+ columnLength,
2382
+ reverseVertically = false,
2383
+ reverseHorizontally = false;
2384
+
2385
+ if (isVertical) {
2386
+ if (overlay.rowIndex < bounds.top) {
2387
+ bounds.top = overlay.rowIndex;
2388
+ reverseVertically = true;
2389
+ } else if (overlay.rowIndex > bounds.bottom) {
2390
+ bounds.bottom = overlay.rowIndex;
2391
+ }
2392
+
2393
+ startRow =
2394
+ bounds.top < boundsOld.top ? bounds.top : boundsOld.bottom + 1;
2395
+ rowLength =
2396
+ bounds.top < boundsOld.top
2397
+ ? boundsOld.top - bounds.top
2398
+ : bounds.bottom - boundsOld.bottom;
2399
+ startColumn = selectionIndex.column;
2400
+ columnLength = selectionIndex.columnLength;
2401
+ } else if (isHorizontal) {
2402
+ if (overlay.columnIndex < bounds.left) {
2403
+ bounds.left = overlay.columnIndex;
2404
+ reverseHorizontally = true;
2405
+ } else if (overlay.columnIndex > bounds.right) {
2406
+ bounds.right = overlay.columnIndex;
2407
+ }
2408
+
2409
+ startColumn =
2410
+ bounds.left < boundsOld.left ? bounds.left : boundsOld.right + 1;
2411
+ columnLength =
2412
+ bounds.left < boundsOld.left
2413
+ ? boundsOld.left - bounds.left
2414
+ : bounds.right - boundsOld.right;
2415
+ startRow = selectionIndex.row;
2416
+ rowLength = selectionIndex.rowLength;
2417
+ }
2418
+
2419
+ if (
2420
+ bounds.left <= bounds.right &&
2421
+ bounds.top <= bounds.bottom &&
2422
+ (bounds.left < boundsOld.left ||
2423
+ bounds.top < boundsOld.top ||
2424
+ bounds.right > boundsOld.right ||
2425
+ bounds.bottom > boundsOld.bottom)
2426
+ ) {
2427
+ const schema = self.getSchema();
2428
+ const rows = [];
2429
+
2430
+ for (
2431
+ let rowIndex = 0;
2432
+ rowIndex < Math.min(rowLength, selectionIndex.rowLength);
2433
+ rowIndex++
2434
+ ) {
2435
+ const rowData = self.viewData[selectionIndex.row + rowIndex];
2436
+
2437
+ rows[rowIndex] = [];
2438
+
2439
+ for (
2440
+ let columnIndex = 0;
2441
+ columnIndex < Math.min(columnLength, selectionIndex.columnLength);
2442
+ columnIndex++
2443
+ ) {
2444
+ const column = schema[selectionIndex.column + columnIndex];
2445
+ if (!column) continue;
2446
+
2447
+ const cellData = rowData[column.name];
2448
+
2449
+ rows[rowIndex][columnIndex] = cellData;
2450
+ }
2451
+ }
2452
+
2453
+ self.insert({
2454
+ rows: rows,
2455
+ startRowIndex: startRow,
2456
+ startColumnIndex: startColumn,
2457
+ minRowsLength: rowLength,
2458
+ minColumnsLength: columnLength,
2459
+ reverseRows: reverseVertically,
2460
+ reverseColumns: reverseHorizontally,
2461
+ clearSelections: false,
2462
+ alwaysFilling: true,
2463
+ direction: isHorizontal ? 'horizontal' : 'vertical',
2464
+ });
2465
+
2466
+ self.draw();
2467
+ }
2468
+ }
2469
+
2470
+ return true;
2471
+ };
2162
2472
  return;
2163
2473
  }