@odoo/o-spreadsheet 16.1.7 → 16.1.9

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.
@@ -1019,6 +1019,10 @@ function deepEquals(o1, o2) {
1019
1019
  }
1020
1020
  return true;
1021
1021
  }
1022
+ /** Check if the given array contains all the values of the other array. */
1023
+ function includesAll(arr, values) {
1024
+ return values.every((value) => arr.includes(value));
1025
+ }
1022
1026
  /**
1023
1027
  * Return an object with all the keys in the object that have a falsy value removed.
1024
1028
  */
@@ -1658,12 +1662,77 @@ const numberRepresentation = [];
1658
1662
  * - all digit stored in the decimal part of the number
1659
1663
  *
1660
1664
  * The 'maxDecimal' parameter allows to indicate the number of digits to not
1661
- * exceed in the decimal part, in which case digits are rounded
1665
+ * exceed in the decimal part, in which case digits are rounded.
1662
1666
  *
1663
- * Intl.Numberformat is used to properly handle all the roundings.
1664
- * e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
1665
1667
  **/
1666
1668
  function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
1669
+ const asString = value.toString();
1670
+ if (asString.includes("e"))
1671
+ return splitNumberIntl(value, maxDecimals);
1672
+ if (Number.isInteger(value)) {
1673
+ return { integerDigits: asString, decimalDigits: undefined };
1674
+ }
1675
+ const indexOfDot = asString.indexOf(".");
1676
+ let integerDigits = asString.substring(0, indexOfDot);
1677
+ let decimalDigits = asString.substring(indexOfDot + 1);
1678
+ if (maxDecimals === 0) {
1679
+ if (Number(decimalDigits[0]) >= 5) {
1680
+ integerDigits = (Number(integerDigits) + 1).toString();
1681
+ }
1682
+ return { integerDigits, decimalDigits: undefined };
1683
+ }
1684
+ if (decimalDigits.length > maxDecimals) {
1685
+ const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
1686
+ decimalDigits = roundedDecimalDigits;
1687
+ if (roundedIntegerDigits !== "0") {
1688
+ integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
1689
+ }
1690
+ }
1691
+ return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
1692
+ }
1693
+ /**
1694
+ * Return the given string minus the trailing "0" characters.
1695
+ *
1696
+ * @param numberString : a string of integers
1697
+ * @returns the numberString, minus the eventual zeroes at the end
1698
+ */
1699
+ function removeTrailingZeroes(numberString) {
1700
+ let i = numberString.length - 1;
1701
+ while (i >= 0 && numberString[i] === "0") {
1702
+ i--;
1703
+ }
1704
+ return numberString.slice(0, i + 1) || undefined;
1705
+ }
1706
+ /**
1707
+ * Limit the size of the decimal part of a number to the given number of digits.
1708
+ */
1709
+ function limitDecimalDigits(decimalDigits, maxDecimals) {
1710
+ let integerDigits = "0";
1711
+ let resultDecimalDigits = decimalDigits;
1712
+ // Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
1713
+ // but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
1714
+ let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
1715
+ const i = maxDecimals;
1716
+ if (Number(Number(decimalDigits[i]) < 5)) {
1717
+ return { integerDigits, decimalDigits: slicedDecimalDigits };
1718
+ }
1719
+ // round up
1720
+ const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
1721
+ if (slicedRoundedUp.length > slicedDecimalDigits.length) {
1722
+ integerDigits = (Number(integerDigits) + 1).toString();
1723
+ resultDecimalDigits = undefined;
1724
+ }
1725
+ else {
1726
+ resultDecimalDigits = slicedRoundedUp;
1727
+ }
1728
+ return { integerDigits, decimalDigits: resultDecimalDigits };
1729
+ }
1730
+ /**
1731
+ * Split numbers into decimal/integer digits using Intl.NumberFormat.
1732
+ * Supports numbers with a lot of digits that are transformed to scientific notation by
1733
+ * number.toString(), but is slow.
1734
+ */
1735
+ function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
1667
1736
  let formatter = numberRepresentation[maxDecimals];
1668
1737
  if (!formatter) {
1669
1738
  formatter = new Intl.NumberFormat("en-US", {
@@ -5902,13 +5971,16 @@ class Menu extends Component {
5902
5971
  }
5903
5972
  });
5904
5973
  }
5974
+ get visibleMenuItems() {
5975
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
5976
+ }
5905
5977
  get subMenuPosition() {
5906
5978
  const position = Object.assign({}, this.subMenu.position);
5907
5979
  position.y -= this.subMenu.scrollOffset || 0;
5908
5980
  return position;
5909
5981
  }
5910
5982
  get menuHeight() {
5911
- const menuItems = this.props.menuItems;
5983
+ const menuItems = this.visibleMenuItems;
5912
5984
  let menuItemsHeight = this.getMenuItemsHeight(menuItems);
5913
5985
  // We don't display separator at the end of a menu
5914
5986
  if (menuItems[menuItems.length - 1].separator) {
@@ -5950,7 +6022,7 @@ class Menu extends Component {
5950
6022
  * and the menu item at a given index.
5951
6023
  */
5952
6024
  subMenuVerticalPosition(menuIndex) {
5953
- const menusAbove = this.props.menuItems.slice(0, menuIndex);
6025
+ const menusAbove = this.visibleMenuItems.slice(0, menuIndex);
5954
6026
  return this.position.y + this.getMenuItemsHeight(menusAbove) + MENU_VERTICAL_PADDING;
5955
6027
  }
5956
6028
  onExternalClick(ev) {
@@ -6122,7 +6194,7 @@ class ChartFigure extends Component {
6122
6194
  openContextMenu(position) {
6123
6195
  const registry = this.getMenuItemRegistry();
6124
6196
  this.menuState.isOpen = true;
6125
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
6197
+ this.menuState.menuItems = registry.getAll();
6126
6198
  this.menuState.position = position;
6127
6199
  }
6128
6200
  get chartComponent() {
@@ -7253,6 +7325,11 @@ colMenuRegistry
7253
7325
  name: REMOVE_COLUMNS_NAME,
7254
7326
  sequence: 90,
7255
7327
  action: REMOVE_COLUMNS_ACTION,
7328
+ isVisible: (env) => {
7329
+ const sheetId = env.model.getters.getActiveSheetId();
7330
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7331
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7332
+ },
7256
7333
  })
7257
7334
  .add("clear_column", {
7258
7335
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -7265,9 +7342,8 @@ colMenuRegistry
7265
7342
  action: HIDE_COLUMNS_ACTION,
7266
7343
  isVisible: (env) => {
7267
7344
  const sheetId = env.model.getters.getActiveSheetId();
7268
- const hiddenCols = env.model.getters.getHiddenColsGroups(sheetId).flat();
7269
- return (env.model.getters.getNumberCols(sheetId) >
7270
- hiddenCols.length + env.model.getters.getElementsFromSelection("COL").length);
7345
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7346
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7271
7347
  },
7272
7348
  separator: true,
7273
7349
  })
@@ -7341,6 +7417,11 @@ rowMenuRegistry
7341
7417
  name: REMOVE_ROWS_NAME,
7342
7418
  sequence: 70,
7343
7419
  action: REMOVE_ROWS_ACTION,
7420
+ isVisible: (env) => {
7421
+ const sheetId = env.model.getters.getActiveSheetId();
7422
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7423
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7424
+ },
7344
7425
  })
7345
7426
  .add("clear_row", {
7346
7427
  name: DELETE_CONTENT_ROWS_NAME,
@@ -7353,9 +7434,8 @@ rowMenuRegistry
7353
7434
  action: HIDE_ROWS_ACTION,
7354
7435
  isVisible: (env) => {
7355
7436
  const sheetId = env.model.getters.getActiveSheetId();
7356
- const hiddenRows = env.model.getters.getHiddenRowsGroups(sheetId).flat();
7357
- return (env.model.getters.getNumberRows(sheetId) >
7358
- hiddenRows.length + env.model.getters.getElementsFromSelection("ROW").length);
7437
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7438
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7359
7439
  },
7360
7440
  separator: true,
7361
7441
  })
@@ -16928,14 +17008,31 @@ class ContentEditableHelper {
16928
17008
  * */
16929
17009
  getCurrentSelection() {
16930
17010
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
16931
- let startSizeBefore = this.findSizeBeforeElement(startElement);
16932
- let endSizeBefore = this.findSizeBeforeElement(endElement);
17011
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
17012
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
16933
17013
  return {
16934
- start: startSizeBefore + startSelectionOffset,
16935
- end: endSizeBefore + endSelectionOffset,
17014
+ start: startSizeBefore,
17015
+ end: endSizeBefore,
16936
17016
  };
16937
17017
  }
16938
- findSizeBeforeElement(nodeToFind) {
17018
+ /**
17019
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
17020
+ * The selected node is either a Text node or an Element node.
17021
+ *
17022
+ * case 1 -Text node:
17023
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
17024
+ * content length of all previous nodes.
17025
+ *
17026
+ * case 2 - Element node:
17027
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
17028
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
17029
+ *
17030
+ * See the MDN documentation for more details.
17031
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
17032
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
17033
+ *
17034
+ */
17035
+ findSelectionIndex(nodeToFind, nodeOffset) {
16939
17036
  let it = this.iterateChildren(this.el);
16940
17037
  let usedCharacters = 0;
16941
17038
  let current = it.next();
@@ -16947,6 +17044,19 @@ class ContentEditableHelper {
16947
17044
  }
16948
17045
  current = it.next();
16949
17046
  }
17047
+ if (current.value === nodeToFind) {
17048
+ if (!current.value.hasChildNodes()) {
17049
+ usedCharacters += nodeOffset;
17050
+ }
17051
+ else {
17052
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
17053
+ for (const child of children) {
17054
+ if (!child.textContent)
17055
+ continue;
17056
+ usedCharacters += child.textContent.length;
17057
+ }
17058
+ }
17059
+ }
16950
17060
  return usedCharacters;
16951
17061
  }
16952
17062
  getStartAndEndSelection() {
@@ -23538,8 +23648,8 @@ class Grid extends Component {
23538
23648
  },
23539
23649
  "CTRL+END": () => {
23540
23650
  const sheetId = this.env.model.getters.getActiveSheetId();
23541
- const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", range(0, this.env.model.getters.getNumberCols(sheetId)).reverse());
23542
- const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", range(0, this.env.model.getters.getNumberRows(sheetId)).reverse());
23651
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
23652
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
23543
23653
  this.env.model.selection.selectCell(col, row);
23544
23654
  },
23545
23655
  "SHIFT+ ": () => {
@@ -23619,9 +23729,10 @@ class Grid extends Component {
23619
23729
  this.focus();
23620
23730
  }
23621
23731
  focus() {
23732
+ var _a;
23622
23733
  if (!this.env.model.getters.getSelectedFigureId() &&
23623
23734
  this.env.model.getters.getEditionMode() === "inactive") {
23624
- this.hiddenInput.el.focus();
23735
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
23625
23736
  }
23626
23737
  }
23627
23738
  get gridEl() {
@@ -23822,9 +23933,7 @@ class Grid extends Component {
23822
23933
  }
23823
23934
  this.menuState.isOpen = true;
23824
23935
  this.menuState.position = { x, y };
23825
- this.menuState.menuItems = registries$1[type]
23826
- .getAll()
23827
- .filter((item) => !item.isVisible || item.isVisible(this.env));
23936
+ this.menuState.menuItems = registries$1[type].getAll();
23828
23937
  }
23829
23938
  copy(cut, ev) {
23830
23939
  if (!this.gridEl.contains(document.activeElement)) {
@@ -27193,7 +27302,7 @@ function load(data, verboseImport) {
27193
27302
  }
27194
27303
  }
27195
27304
  }
27196
- data = JSON.parse(JSON.stringify(data));
27305
+ data = deepCopy(data);
27197
27306
  // apply migrations, if needed
27198
27307
  if ("version" in data) {
27199
27308
  if (data.version < CURRENT_VERSION) {
@@ -29853,7 +29962,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29853
29962
  allowDispatch(cmd) {
29854
29963
  switch (cmd.type) {
29855
29964
  case "HIDE_COLUMNS_ROWS": {
29856
- if (!this.hiddenHeaders[cmd.sheetId]) {
29965
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29857
29966
  return 27 /* CommandResult.InvalidSheetId */;
29858
29967
  }
29859
29968
  const hiddenGroup = cmd.dimension === "COL"
@@ -29866,6 +29975,14 @@ class HeaderVisibilityPlugin extends CorePlugin {
29866
29975
  ? 0 /* CommandResult.Success */
29867
29976
  : 66 /* CommandResult.TooManyHiddenElements */;
29868
29977
  }
29978
+ case "REMOVE_COLUMNS_ROWS":
29979
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29980
+ return 27 /* CommandResult.InvalidSheetId */;
29981
+ }
29982
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29983
+ return 8 /* CommandResult.NotEnoughElements */;
29984
+ }
29985
+ return 0 /* CommandResult.Success */;
29869
29986
  }
29870
29987
  return 0 /* CommandResult.Success */;
29871
29988
  }
@@ -29912,6 +30029,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29912
30029
  }
29913
30030
  return;
29914
30031
  }
30032
+ canRemoveHeaders(sheetId, dimension, elements) {
30033
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
30034
+ return !includesAll(elements, visibleHeaders);
30035
+ }
29915
30036
  isRowHiddenByUser(sheetId, index) {
29916
30037
  return this.hiddenHeaders[sheetId].ROW[index];
29917
30038
  }
@@ -29956,6 +30077,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29956
30077
  }
29957
30078
  return consecutiveIndexes;
29958
30079
  }
30080
+ getAllVisibleHeaders(sheetId, dimension) {
30081
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
30082
+ }
29959
30083
  import(data) {
29960
30084
  var _a, _b;
29961
30085
  for (let sheet of data.sheets) {
@@ -30003,6 +30127,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
30003
30127
  }
30004
30128
  }
30005
30129
  HeaderVisibilityPlugin.getters = [
30130
+ "canRemoveHeaders",
30006
30131
  "getHiddenColsGroups",
30007
30132
  "getHiddenRowsGroups",
30008
30133
  "isRowHiddenByUser",
@@ -31012,14 +31137,6 @@ class SheetPlugin extends CorePlugin {
31012
31137
  return this.orderedSheetIds.length > 1
31013
31138
  ? 0 /* CommandResult.Success */
31014
31139
  : 9 /* CommandResult.NotEnoughSheets */;
31015
- case "REMOVE_COLUMNS_ROWS": {
31016
- const length = cmd.dimension === "COL"
31017
- ? this.getNumberCols(cmd.sheetId)
31018
- : this.getNumberRows(cmd.sheetId);
31019
- return length > cmd.elements.length
31020
- ? 0 /* CommandResult.Success */
31021
- : 8 /* CommandResult.NotEnoughElements */;
31022
- }
31023
31140
  case "FREEZE_ROWS": {
31024
31141
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
31025
31142
  }
@@ -31490,7 +31607,7 @@ class SheetPlugin extends CorePlugin {
31490
31607
  duplicateSheet(fromId, toId) {
31491
31608
  const sheet = this.getSheet(fromId);
31492
31609
  const toName = this.getDuplicateSheetName(sheet.name);
31493
- const newSheet = JSON.parse(JSON.stringify(sheet));
31610
+ const newSheet = deepCopy(sheet);
31494
31611
  newSheet.id = toId;
31495
31612
  newSheet.name = toName;
31496
31613
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -31617,10 +31734,10 @@ class SheetPlugin extends CorePlugin {
31617
31734
  }
31618
31735
  }
31619
31736
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
31620
- for (let [index, row] of Object.entries(sheet.rows)) {
31621
- const rowIndex = parseInt(index, 10);
31737
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31738
+ const row = sheet.rows[rowIndex];
31622
31739
  for (let i in row.cells) {
31623
- const colIndex = parseInt(i, 10);
31740
+ const colIndex = Number(i);
31624
31741
  const cellId = row.cells[i];
31625
31742
  if (cellId) {
31626
31743
  if (colIndex === deletedColumn) {
@@ -31647,11 +31764,11 @@ class SheetPlugin extends CorePlugin {
31647
31764
  */
31648
31765
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
31649
31766
  const commands = [];
31650
- for (const [index, row] of Object.entries(sheet.rows)) {
31651
- const rowIndex = parseInt(index, 10);
31767
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31768
+ const row = sheet.rows[rowIndex];
31652
31769
  if (dimension !== "rows" || rowIndex >= addedElement) {
31653
31770
  for (let i in row.cells) {
31654
- const colIndex = parseInt(i, 10);
31771
+ const colIndex = Number(i);
31655
31772
  const cellId = row.cells[i];
31656
31773
  if (cellId) {
31657
31774
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -31681,11 +31798,11 @@ class SheetPlugin extends CorePlugin {
31681
31798
  */
31682
31799
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
31683
31800
  const numberRows = deleteToRow - deleteFromRow + 1;
31684
- for (let [index, row] of Object.entries(sheet.rows)) {
31685
- const rowIndex = parseInt(index, 10);
31801
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31802
+ const row = sheet.rows[rowIndex];
31686
31803
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
31687
31804
  for (let i in row.cells) {
31688
- const colIndex = parseInt(i, 10);
31805
+ const colIndex = Number(i);
31689
31806
  const cellId = row.cells[i];
31690
31807
  if (cellId) {
31691
31808
  this.dispatch("CLEAR_CELL", {
@@ -31698,7 +31815,7 @@ class SheetPlugin extends CorePlugin {
31698
31815
  }
31699
31816
  if (rowIndex > deleteToRow) {
31700
31817
  for (let i in row.cells) {
31701
- const colIndex = parseInt(i, 10);
31818
+ const colIndex = Number(i);
31702
31819
  const cellId = row.cells[i];
31703
31820
  if (cellId) {
31704
31821
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -31716,7 +31833,7 @@ class SheetPlugin extends CorePlugin {
31716
31833
  const rows = [];
31717
31834
  const cellsQueue = sheet.rows.map((row) => row.cells);
31718
31835
  for (let i in sheet.rows) {
31719
- if (parseInt(i, 10) === index) {
31836
+ if (Number(i) === index) {
31720
31837
  continue;
31721
31838
  }
31722
31839
  rows.push({
@@ -32859,13 +32976,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32859
32976
  return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
32860
32977
  }
32861
32978
  intersectZoneWithViewport(sheetId, zone) {
32862
- const colsRange = range(zone.left, zone.right + 1);
32863
- const rowsRange = range(zone.top, zone.bottom + 1);
32864
32979
  return {
32865
- left: this.getters.findVisibleHeader(sheetId, "COL", colsRange),
32866
- right: this.getters.findVisibleHeader(sheetId, "COL", colsRange.reverse()),
32867
- top: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange),
32868
- bottom: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange.reverse()),
32980
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
32981
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
32982
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
32983
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32869
32984
  };
32870
32985
  }
32871
32986
  updateFilter({ col, row, values, sheetId }) {
@@ -33069,48 +33184,66 @@ class InternalViewport {
33069
33184
  this.adjustPositionY(row);
33070
33185
  }
33071
33186
  }
33072
- adjustPositionX(col) {
33187
+ adjustPositionX(targetCol) {
33073
33188
  const sheetId = this.sheetId;
33074
- const { start, end } = this.getters.getColDimensions(sheetId, col);
33075
- while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
33076
- this.offsetX + this.offsetCorrectionX < start) {
33077
- this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
33078
- this.offsetScrollbarX = this.offsetX;
33079
- this.adjustViewportZoneX();
33080
- }
33081
- while (col < this.left) {
33082
- let leftCol;
33083
- for (leftCol = this.left; leftCol >= 0; leftCol--) {
33084
- if (!this.getters.isColHidden(sheetId, leftCol)) {
33189
+ const { end } = this.getters.getColDimensions(sheetId, targetCol);
33190
+ const maxCol = this.getters.getNumberCols(sheetId);
33191
+ if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
33192
+ for (let col = this.left; this.offsetX + this.offsetCorrectionX + this.viewportWidth < end; col++) {
33193
+ if (col > maxCol) {
33085
33194
  break;
33086
33195
  }
33196
+ if (this.getters.isColHidden(sheetId, col)) {
33197
+ continue;
33198
+ }
33199
+ this.offsetX = this.getters.getColDimensions(sheetId, col).end - this.offsetCorrectionX;
33200
+ this.offsetScrollbarX = this.offsetX;
33201
+ this.adjustViewportZoneX();
33202
+ }
33203
+ }
33204
+ else if (this.left > targetCol) {
33205
+ for (let col = this.left; col >= targetCol; col--) {
33206
+ if (col < 0) {
33207
+ break;
33208
+ }
33209
+ if (this.getters.isColHidden(sheetId, col)) {
33210
+ continue;
33211
+ }
33212
+ this.offsetX = this.getters.getColDimensions(sheetId, col).start - this.offsetCorrectionX;
33213
+ this.offsetScrollbarX = this.offsetX;
33214
+ this.adjustViewportZoneX();
33087
33215
  }
33088
- this.offsetX =
33089
- this.getters.getColDimensions(sheetId, leftCol - 1).start - this.offsetCorrectionX;
33090
- this.offsetScrollbarX = this.offsetX;
33091
- this.adjustViewportZoneX();
33092
33216
  }
33093
33217
  }
33094
- adjustPositionY(row) {
33218
+ adjustPositionY(targetRow) {
33095
33219
  const sheetId = this.sheetId;
33096
- while (this.getters.getRowDimensions(sheetId, row).end >
33097
- this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
33098
- this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
33099
- this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
33100
- this.offsetScrollbarY = this.offsetY;
33101
- this.adjustViewportZoneY();
33102
- }
33103
- while (row < this.top) {
33104
- let topRow;
33105
- for (topRow = this.top; topRow >= 0; topRow--) {
33106
- if (!this.getters.isRowHidden(sheetId, topRow)) {
33220
+ const { end } = this.getters.getRowDimensions(sheetId, targetRow);
33221
+ const maxRow = this.getters.getNumberRows(sheetId);
33222
+ if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
33223
+ for (let row = this.top; this.offsetY + this.viewportHeight + this.offsetCorrectionY < end; row++) {
33224
+ if (row > maxRow) {
33107
33225
  break;
33108
33226
  }
33227
+ if (this.getters.isRowHidden(sheetId, row)) {
33228
+ continue;
33229
+ }
33230
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).end - this.offsetCorrectionY;
33231
+ this.offsetScrollbarY = this.offsetY;
33232
+ this.adjustViewportZoneY();
33233
+ }
33234
+ }
33235
+ else if (this.top > targetRow) {
33236
+ for (let row = this.top; row >= targetRow; row--) {
33237
+ if (row < 0) {
33238
+ break;
33239
+ }
33240
+ if (this.getters.isRowHidden(sheetId, row)) {
33241
+ continue;
33242
+ }
33243
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).start - this.offsetCorrectionY;
33244
+ this.offsetScrollbarY = this.offsetY;
33245
+ this.adjustViewportZoneY();
33109
33246
  }
33110
- this.offsetY =
33111
- this.getters.getRowDimensions(sheetId, topRow - 1).start - this.offsetCorrectionY;
33112
- this.offsetScrollbarY = this.offsetY;
33113
- this.adjustViewportZoneY();
33114
33247
  }
33115
33248
  }
33116
33249
  setViewportOffset(offsetX, offsetY) {
@@ -34953,13 +35086,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34953
35086
  getNextVisibleCellPosition({ sheetId, col, row }) {
34954
35087
  return {
34955
35088
  sheetId,
34956
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
34957
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
35089
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
35090
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
34958
35091
  };
34959
35092
  }
34960
- findVisibleHeader(sheetId, dimension, indexes) {
34961
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
34962
- !this.isHeaderHidden(sheetId, dimension, index));
35093
+ /**
35094
+ * Find the first visible header in the range [`from` => `to`].
35095
+ *
35096
+ * Both `from` and `to` are inclusive.
35097
+ */
35098
+ findVisibleHeader(sheetId, dimension, from, to) {
35099
+ if (from <= to) {
35100
+ for (let i = from; i <= to; i++) {
35101
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35102
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35103
+ return i;
35104
+ }
35105
+ }
35106
+ }
35107
+ if (from > to) {
35108
+ for (let i = from; i >= to; i--) {
35109
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35110
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35111
+ return i;
35112
+ }
35113
+ }
35114
+ }
35115
+ return undefined;
34963
35116
  }
34964
35117
  findLastVisibleColRowIndex(sheetId, dimension, { last, first }) {
34965
35118
  const lastVisibleIndex = range(last, first, -1).find((index) => !this.isHeaderHidden(sheetId, dimension, index));
@@ -34967,7 +35120,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34967
35120
  }
34968
35121
  findFirstVisibleColRowIndex(sheetId, dimension) {
34969
35122
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
34970
- for (let i = 0; i < numberOfHeaders - 1; i++) {
35123
+ for (let i = 0; i < numberOfHeaders; i++) {
34971
35124
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
34972
35125
  return i;
34973
35126
  }
@@ -39194,7 +39347,7 @@ class BottomBar extends Component {
39194
39347
  }
39195
39348
  openContextMenu(x, y, registry, menuId) {
39196
39349
  this.menuState.isOpen = true;
39197
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
39350
+ this.menuState.menuItems = registry.getAll();
39198
39351
  this.menuState.position = { x, y };
39199
39352
  this.menuState.menuId = menuId;
39200
39353
  }
@@ -40001,7 +40154,7 @@ class TopBar extends Component {
40001
40154
  const { left, top, height } = ev.target.getBoundingClientRect();
40002
40155
  this.state.menuState.isOpen = true;
40003
40156
  this.state.menuState.position = { x: left, y: top + height };
40004
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
40157
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
40005
40158
  this.state.menuState.parentMenu = menu;
40006
40159
  this.isSelectingMenu = true;
40007
40160
  this.openedEl = ev.target;
@@ -40044,9 +40197,7 @@ class TopBar extends Component {
40044
40197
  this.style.align = this.style.align || cell.defaultAlign;
40045
40198
  this.fillColor = this.style.fillColor || "#ffffff";
40046
40199
  this.textColor = this.style.textColor || "#000000";
40047
- this.menus = topbarMenuRegistry
40048
- .getAll()
40049
- .filter((item) => !item.isVisible || item.isVisible(this.env));
40200
+ this.menus = topbarMenuRegistry.getAll();
40050
40201
  }
40051
40202
  getMenuName(menu) {
40052
40203
  return getMenuName(menu, this.env);
@@ -41760,8 +41911,8 @@ class SelectionStreamProcessor {
41760
41911
  }
41761
41912
  const { left, right, top, bottom } = zone;
41762
41913
  const sheetId = this.getters.getActiveSheetId();
41763
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
41764
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
41914
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
41915
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
41765
41916
  if (refRow === undefined || refCol === undefined) {
41766
41917
  return 17 /* CommandResult.SelectionOutOfBound */;
41767
41918
  }
@@ -41836,10 +41987,10 @@ class SelectionStreamProcessor {
41836
41987
  const { col: anchorCol, row: anchorRow } = anchor.cell;
41837
41988
  return {
41838
41989
  col: this.getters.isColHidden(sheetId, anchorCol)
41839
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
41990
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
41840
41991
  : anchorCol,
41841
41992
  row: this.getters.isRowHidden(sheetId, anchorRow)
41842
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
41993
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
41843
41994
  : anchorRow,
41844
41995
  };
41845
41996
  }
@@ -43520,6 +43671,8 @@ class Model extends EventBus {
43520
43671
  * Internal status of the model. Important for command handling coordination
43521
43672
  */
43522
43673
  this.status = 0 /* Status.Ready */;
43674
+ this.handlers = [];
43675
+ this.coreHandlers = [];
43523
43676
  /**
43524
43677
  * The dispatch method is the only entry point to manipulate data in the model.
43525
43678
  * This is through this method that commands are dispatched most of the time
@@ -43624,6 +43777,8 @@ class Model extends EventBus {
43624
43777
  this.uuidGenerator.setIsFastStrategy(true);
43625
43778
  // Initiate stream processor
43626
43779
  this.selection = new SelectionStreamProcessor(this.getters);
43780
+ this.coreHandlers.push(this.range);
43781
+ this.handlers.push(this.range);
43627
43782
  this.corePluginConfig = this.setupCorePluginConfig();
43628
43783
  this.uiPluginConfig = this.setupUiPluginConfig();
43629
43784
  // registering plugins
@@ -43632,15 +43787,23 @@ class Model extends EventBus {
43632
43787
  }
43633
43788
  Object.assign(this.getters, this.coreGetters);
43634
43789
  for (let Plugin of statefulUIPluginRegistry.getAll()) {
43635
- this.statefulUIPlugins.push(this.setupUiPlugin(Plugin));
43790
+ const plugin = this.setupUiPlugin(Plugin);
43791
+ this.statefulUIPlugins.push(plugin);
43792
+ this.handlers.push(plugin);
43636
43793
  }
43637
43794
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
43638
- this.coreViewsPlugins.push(this.setupUiPlugin(Plugin));
43795
+ const plugin = this.setupUiPlugin(Plugin);
43796
+ this.coreViewsPlugins.push(plugin);
43797
+ this.handlers.push(plugin);
43798
+ this.coreHandlers.push(plugin);
43639
43799
  }
43640
43800
  for (let Plugin of featurePluginRegistry.getAll()) {
43641
- this.featurePlugins.push(this.setupUiPlugin(Plugin));
43801
+ const plugin = this.setupUiPlugin(Plugin);
43802
+ this.featurePlugins.push(plugin);
43803
+ this.handlers.push(plugin);
43642
43804
  }
43643
43805
  this.uuidGenerator.setIsFastStrategy(false);
43806
+ this.handlers.push(this.history);
43644
43807
  // starting plugins
43645
43808
  this.dispatch("START");
43646
43809
  // Model should be the last permanent subscriber in the list since he should render
@@ -43662,12 +43825,6 @@ class Model extends EventBus {
43662
43825
  // by owl, since we do not rely on reactivity
43663
43826
  markRaw(this);
43664
43827
  }
43665
- get handlers() {
43666
- return [this.range, ...this.corePlugins, ...this.allUIPlugins, this.history];
43667
- }
43668
- get allUIPlugins() {
43669
- return [...this.statefulUIPlugins, ...this.coreViewsPlugins, ...this.featurePlugins];
43670
- }
43671
43828
  joinSession() {
43672
43829
  this.session.join(this.config.client);
43673
43830
  }
@@ -43709,6 +43866,8 @@ class Model extends EventBus {
43709
43866
  }
43710
43867
  plugin.import(data);
43711
43868
  this.corePlugins.push(plugin);
43869
+ this.coreHandlers.push(plugin);
43870
+ this.handlers.push(plugin);
43712
43871
  }
43713
43872
  onRemoteRevisionReceived({ commands }) {
43714
43873
  for (let command of commands) {
@@ -43851,7 +44010,7 @@ class Model extends EventBus {
43851
44010
  }
43852
44011
  }
43853
44012
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
43854
- data = JSON.parse(JSON.stringify(data));
44013
+ data = deepCopy(data);
43855
44014
  return data;
43856
44015
  }
43857
44016
  updateMode(mode) {
@@ -43879,7 +44038,7 @@ class Model extends EventBus {
43879
44038
  handler.exportForExcel(data);
43880
44039
  }
43881
44040
  }
43882
- data = JSON.parse(JSON.stringify(data));
44041
+ data = deepCopy(data);
43883
44042
  return getXLSX(data);
43884
44043
  }
43885
44044
  garbageCollectExternalResources() {
@@ -43992,6 +44151,6 @@ function addFunction(functionName, functionDescription) {
43992
44151
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
43993
44152
 
43994
44153
 
43995
- __info__.version = '16.1.7';
43996
- __info__.date = '2023-04-13T08:29:52.140Z';
43997
- __info__.hash = '3141a6d';
44154
+ __info__.version = '16.1.9';
44155
+ __info__.date = '2023-04-21T07:29:41.080Z';
44156
+ __info__.hash = '686bffa';