@odoo/o-spreadsheet 16.1.8 → 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.
@@ -1023,6 +1023,10 @@ function deepEquals(o1, o2) {
1023
1023
  }
1024
1024
  return true;
1025
1025
  }
1026
+ /** Check if the given array contains all the values of the other array. */
1027
+ function includesAll(arr, values) {
1028
+ return values.every((value) => arr.includes(value));
1029
+ }
1026
1030
  /**
1027
1031
  * Return an object with all the keys in the object that have a falsy value removed.
1028
1032
  */
@@ -1662,12 +1666,77 @@ const numberRepresentation = [];
1662
1666
  * - all digit stored in the decimal part of the number
1663
1667
  *
1664
1668
  * The 'maxDecimal' parameter allows to indicate the number of digits to not
1665
- * exceed in the decimal part, in which case digits are rounded
1669
+ * exceed in the decimal part, in which case digits are rounded.
1666
1670
  *
1667
- * Intl.Numberformat is used to properly handle all the roundings.
1668
- * e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
1669
1671
  **/
1670
1672
  function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
1673
+ const asString = value.toString();
1674
+ if (asString.includes("e"))
1675
+ return splitNumberIntl(value, maxDecimals);
1676
+ if (Number.isInteger(value)) {
1677
+ return { integerDigits: asString, decimalDigits: undefined };
1678
+ }
1679
+ const indexOfDot = asString.indexOf(".");
1680
+ let integerDigits = asString.substring(0, indexOfDot);
1681
+ let decimalDigits = asString.substring(indexOfDot + 1);
1682
+ if (maxDecimals === 0) {
1683
+ if (Number(decimalDigits[0]) >= 5) {
1684
+ integerDigits = (Number(integerDigits) + 1).toString();
1685
+ }
1686
+ return { integerDigits, decimalDigits: undefined };
1687
+ }
1688
+ if (decimalDigits.length > maxDecimals) {
1689
+ const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
1690
+ decimalDigits = roundedDecimalDigits;
1691
+ if (roundedIntegerDigits !== "0") {
1692
+ integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
1693
+ }
1694
+ }
1695
+ return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
1696
+ }
1697
+ /**
1698
+ * Return the given string minus the trailing "0" characters.
1699
+ *
1700
+ * @param numberString : a string of integers
1701
+ * @returns the numberString, minus the eventual zeroes at the end
1702
+ */
1703
+ function removeTrailingZeroes(numberString) {
1704
+ let i = numberString.length - 1;
1705
+ while (i >= 0 && numberString[i] === "0") {
1706
+ i--;
1707
+ }
1708
+ return numberString.slice(0, i + 1) || undefined;
1709
+ }
1710
+ /**
1711
+ * Limit the size of the decimal part of a number to the given number of digits.
1712
+ */
1713
+ function limitDecimalDigits(decimalDigits, maxDecimals) {
1714
+ let integerDigits = "0";
1715
+ let resultDecimalDigits = decimalDigits;
1716
+ // Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
1717
+ // but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
1718
+ let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
1719
+ const i = maxDecimals;
1720
+ if (Number(Number(decimalDigits[i]) < 5)) {
1721
+ return { integerDigits, decimalDigits: slicedDecimalDigits };
1722
+ }
1723
+ // round up
1724
+ const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
1725
+ if (slicedRoundedUp.length > slicedDecimalDigits.length) {
1726
+ integerDigits = (Number(integerDigits) + 1).toString();
1727
+ resultDecimalDigits = undefined;
1728
+ }
1729
+ else {
1730
+ resultDecimalDigits = slicedRoundedUp;
1731
+ }
1732
+ return { integerDigits, decimalDigits: resultDecimalDigits };
1733
+ }
1734
+ /**
1735
+ * Split numbers into decimal/integer digits using Intl.NumberFormat.
1736
+ * Supports numbers with a lot of digits that are transformed to scientific notation by
1737
+ * number.toString(), but is slow.
1738
+ */
1739
+ function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
1671
1740
  let formatter = numberRepresentation[maxDecimals];
1672
1741
  if (!formatter) {
1673
1742
  formatter = new Intl.NumberFormat("en-US", {
@@ -5906,13 +5975,16 @@ class Menu extends owl.Component {
5906
5975
  }
5907
5976
  });
5908
5977
  }
5978
+ get visibleMenuItems() {
5979
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
5980
+ }
5909
5981
  get subMenuPosition() {
5910
5982
  const position = Object.assign({}, this.subMenu.position);
5911
5983
  position.y -= this.subMenu.scrollOffset || 0;
5912
5984
  return position;
5913
5985
  }
5914
5986
  get menuHeight() {
5915
- const menuItems = this.props.menuItems;
5987
+ const menuItems = this.visibleMenuItems;
5916
5988
  let menuItemsHeight = this.getMenuItemsHeight(menuItems);
5917
5989
  // We don't display separator at the end of a menu
5918
5990
  if (menuItems[menuItems.length - 1].separator) {
@@ -5954,7 +6026,7 @@ class Menu extends owl.Component {
5954
6026
  * and the menu item at a given index.
5955
6027
  */
5956
6028
  subMenuVerticalPosition(menuIndex) {
5957
- const menusAbove = this.props.menuItems.slice(0, menuIndex);
6029
+ const menusAbove = this.visibleMenuItems.slice(0, menuIndex);
5958
6030
  return this.position.y + this.getMenuItemsHeight(menusAbove) + MENU_VERTICAL_PADDING;
5959
6031
  }
5960
6032
  onExternalClick(ev) {
@@ -6126,7 +6198,7 @@ class ChartFigure extends owl.Component {
6126
6198
  openContextMenu(position) {
6127
6199
  const registry = this.getMenuItemRegistry();
6128
6200
  this.menuState.isOpen = true;
6129
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
6201
+ this.menuState.menuItems = registry.getAll();
6130
6202
  this.menuState.position = position;
6131
6203
  }
6132
6204
  get chartComponent() {
@@ -7257,6 +7329,11 @@ colMenuRegistry
7257
7329
  name: REMOVE_COLUMNS_NAME,
7258
7330
  sequence: 90,
7259
7331
  action: REMOVE_COLUMNS_ACTION,
7332
+ isVisible: (env) => {
7333
+ const sheetId = env.model.getters.getActiveSheetId();
7334
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7335
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7336
+ },
7260
7337
  })
7261
7338
  .add("clear_column", {
7262
7339
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -7269,9 +7346,8 @@ colMenuRegistry
7269
7346
  action: HIDE_COLUMNS_ACTION,
7270
7347
  isVisible: (env) => {
7271
7348
  const sheetId = env.model.getters.getActiveSheetId();
7272
- const hiddenCols = env.model.getters.getHiddenColsGroups(sheetId).flat();
7273
- return (env.model.getters.getNumberCols(sheetId) >
7274
- hiddenCols.length + env.model.getters.getElementsFromSelection("COL").length);
7349
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7350
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7275
7351
  },
7276
7352
  separator: true,
7277
7353
  })
@@ -7345,6 +7421,11 @@ rowMenuRegistry
7345
7421
  name: REMOVE_ROWS_NAME,
7346
7422
  sequence: 70,
7347
7423
  action: REMOVE_ROWS_ACTION,
7424
+ isVisible: (env) => {
7425
+ const sheetId = env.model.getters.getActiveSheetId();
7426
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7427
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7428
+ },
7348
7429
  })
7349
7430
  .add("clear_row", {
7350
7431
  name: DELETE_CONTENT_ROWS_NAME,
@@ -7357,9 +7438,8 @@ rowMenuRegistry
7357
7438
  action: HIDE_ROWS_ACTION,
7358
7439
  isVisible: (env) => {
7359
7440
  const sheetId = env.model.getters.getActiveSheetId();
7360
- const hiddenRows = env.model.getters.getHiddenRowsGroups(sheetId).flat();
7361
- return (env.model.getters.getNumberRows(sheetId) >
7362
- hiddenRows.length + env.model.getters.getElementsFromSelection("ROW").length);
7441
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7442
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7363
7443
  },
7364
7444
  separator: true,
7365
7445
  })
@@ -16932,14 +17012,31 @@ class ContentEditableHelper {
16932
17012
  * */
16933
17013
  getCurrentSelection() {
16934
17014
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
16935
- let startSizeBefore = this.findSizeBeforeElement(startElement);
16936
- let endSizeBefore = this.findSizeBeforeElement(endElement);
17015
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
17016
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
16937
17017
  return {
16938
- start: startSizeBefore + startSelectionOffset,
16939
- end: endSizeBefore + endSelectionOffset,
17018
+ start: startSizeBefore,
17019
+ end: endSizeBefore,
16940
17020
  };
16941
17021
  }
16942
- findSizeBeforeElement(nodeToFind) {
17022
+ /**
17023
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
17024
+ * The selected node is either a Text node or an Element node.
17025
+ *
17026
+ * case 1 -Text node:
17027
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
17028
+ * content length of all previous nodes.
17029
+ *
17030
+ * case 2 - Element node:
17031
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
17032
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
17033
+ *
17034
+ * See the MDN documentation for more details.
17035
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
17036
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
17037
+ *
17038
+ */
17039
+ findSelectionIndex(nodeToFind, nodeOffset) {
16943
17040
  let it = this.iterateChildren(this.el);
16944
17041
  let usedCharacters = 0;
16945
17042
  let current = it.next();
@@ -16951,6 +17048,19 @@ class ContentEditableHelper {
16951
17048
  }
16952
17049
  current = it.next();
16953
17050
  }
17051
+ if (current.value === nodeToFind) {
17052
+ if (!current.value.hasChildNodes()) {
17053
+ usedCharacters += nodeOffset;
17054
+ }
17055
+ else {
17056
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
17057
+ for (const child of children) {
17058
+ if (!child.textContent)
17059
+ continue;
17060
+ usedCharacters += child.textContent.length;
17061
+ }
17062
+ }
17063
+ }
16954
17064
  return usedCharacters;
16955
17065
  }
16956
17066
  getStartAndEndSelection() {
@@ -23542,8 +23652,8 @@ class Grid extends owl.Component {
23542
23652
  },
23543
23653
  "CTRL+END": () => {
23544
23654
  const sheetId = this.env.model.getters.getActiveSheetId();
23545
- const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", range(0, this.env.model.getters.getNumberCols(sheetId)).reverse());
23546
- const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", range(0, this.env.model.getters.getNumberRows(sheetId)).reverse());
23655
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
23656
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
23547
23657
  this.env.model.selection.selectCell(col, row);
23548
23658
  },
23549
23659
  "SHIFT+ ": () => {
@@ -23623,9 +23733,10 @@ class Grid extends owl.Component {
23623
23733
  this.focus();
23624
23734
  }
23625
23735
  focus() {
23736
+ var _a;
23626
23737
  if (!this.env.model.getters.getSelectedFigureId() &&
23627
23738
  this.env.model.getters.getEditionMode() === "inactive") {
23628
- this.hiddenInput.el.focus();
23739
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
23629
23740
  }
23630
23741
  }
23631
23742
  get gridEl() {
@@ -23826,9 +23937,7 @@ class Grid extends owl.Component {
23826
23937
  }
23827
23938
  this.menuState.isOpen = true;
23828
23939
  this.menuState.position = { x, y };
23829
- this.menuState.menuItems = registries$1[type]
23830
- .getAll()
23831
- .filter((item) => !item.isVisible || item.isVisible(this.env));
23940
+ this.menuState.menuItems = registries$1[type].getAll();
23832
23941
  }
23833
23942
  copy(cut, ev) {
23834
23943
  if (!this.gridEl.contains(document.activeElement)) {
@@ -27197,7 +27306,7 @@ function load(data, verboseImport) {
27197
27306
  }
27198
27307
  }
27199
27308
  }
27200
- data = JSON.parse(JSON.stringify(data));
27309
+ data = deepCopy(data);
27201
27310
  // apply migrations, if needed
27202
27311
  if ("version" in data) {
27203
27312
  if (data.version < CURRENT_VERSION) {
@@ -29857,7 +29966,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29857
29966
  allowDispatch(cmd) {
29858
29967
  switch (cmd.type) {
29859
29968
  case "HIDE_COLUMNS_ROWS": {
29860
- if (!this.hiddenHeaders[cmd.sheetId]) {
29969
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29861
29970
  return 27 /* CommandResult.InvalidSheetId */;
29862
29971
  }
29863
29972
  const hiddenGroup = cmd.dimension === "COL"
@@ -29870,6 +29979,14 @@ class HeaderVisibilityPlugin extends CorePlugin {
29870
29979
  ? 0 /* CommandResult.Success */
29871
29980
  : 66 /* CommandResult.TooManyHiddenElements */;
29872
29981
  }
29982
+ case "REMOVE_COLUMNS_ROWS":
29983
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29984
+ return 27 /* CommandResult.InvalidSheetId */;
29985
+ }
29986
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29987
+ return 8 /* CommandResult.NotEnoughElements */;
29988
+ }
29989
+ return 0 /* CommandResult.Success */;
29873
29990
  }
29874
29991
  return 0 /* CommandResult.Success */;
29875
29992
  }
@@ -29916,6 +30033,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29916
30033
  }
29917
30034
  return;
29918
30035
  }
30036
+ canRemoveHeaders(sheetId, dimension, elements) {
30037
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
30038
+ return !includesAll(elements, visibleHeaders);
30039
+ }
29919
30040
  isRowHiddenByUser(sheetId, index) {
29920
30041
  return this.hiddenHeaders[sheetId].ROW[index];
29921
30042
  }
@@ -29960,6 +30081,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29960
30081
  }
29961
30082
  return consecutiveIndexes;
29962
30083
  }
30084
+ getAllVisibleHeaders(sheetId, dimension) {
30085
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
30086
+ }
29963
30087
  import(data) {
29964
30088
  var _a, _b;
29965
30089
  for (let sheet of data.sheets) {
@@ -30007,6 +30131,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
30007
30131
  }
30008
30132
  }
30009
30133
  HeaderVisibilityPlugin.getters = [
30134
+ "canRemoveHeaders",
30010
30135
  "getHiddenColsGroups",
30011
30136
  "getHiddenRowsGroups",
30012
30137
  "isRowHiddenByUser",
@@ -31016,14 +31141,6 @@ class SheetPlugin extends CorePlugin {
31016
31141
  return this.orderedSheetIds.length > 1
31017
31142
  ? 0 /* CommandResult.Success */
31018
31143
  : 9 /* CommandResult.NotEnoughSheets */;
31019
- case "REMOVE_COLUMNS_ROWS": {
31020
- const length = cmd.dimension === "COL"
31021
- ? this.getNumberCols(cmd.sheetId)
31022
- : this.getNumberRows(cmd.sheetId);
31023
- return length > cmd.elements.length
31024
- ? 0 /* CommandResult.Success */
31025
- : 8 /* CommandResult.NotEnoughElements */;
31026
- }
31027
31144
  case "FREEZE_ROWS": {
31028
31145
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
31029
31146
  }
@@ -31494,7 +31611,7 @@ class SheetPlugin extends CorePlugin {
31494
31611
  duplicateSheet(fromId, toId) {
31495
31612
  const sheet = this.getSheet(fromId);
31496
31613
  const toName = this.getDuplicateSheetName(sheet.name);
31497
- const newSheet = JSON.parse(JSON.stringify(sheet));
31614
+ const newSheet = deepCopy(sheet);
31498
31615
  newSheet.id = toId;
31499
31616
  newSheet.name = toName;
31500
31617
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -31621,10 +31738,10 @@ class SheetPlugin extends CorePlugin {
31621
31738
  }
31622
31739
  }
31623
31740
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
31624
- for (let [index, row] of Object.entries(sheet.rows)) {
31625
- const rowIndex = parseInt(index, 10);
31741
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31742
+ const row = sheet.rows[rowIndex];
31626
31743
  for (let i in row.cells) {
31627
- const colIndex = parseInt(i, 10);
31744
+ const colIndex = Number(i);
31628
31745
  const cellId = row.cells[i];
31629
31746
  if (cellId) {
31630
31747
  if (colIndex === deletedColumn) {
@@ -31651,11 +31768,11 @@ class SheetPlugin extends CorePlugin {
31651
31768
  */
31652
31769
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
31653
31770
  const commands = [];
31654
- for (const [index, row] of Object.entries(sheet.rows)) {
31655
- const rowIndex = parseInt(index, 10);
31771
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31772
+ const row = sheet.rows[rowIndex];
31656
31773
  if (dimension !== "rows" || rowIndex >= addedElement) {
31657
31774
  for (let i in row.cells) {
31658
- const colIndex = parseInt(i, 10);
31775
+ const colIndex = Number(i);
31659
31776
  const cellId = row.cells[i];
31660
31777
  if (cellId) {
31661
31778
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -31685,11 +31802,11 @@ class SheetPlugin extends CorePlugin {
31685
31802
  */
31686
31803
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
31687
31804
  const numberRows = deleteToRow - deleteFromRow + 1;
31688
- for (let [index, row] of Object.entries(sheet.rows)) {
31689
- const rowIndex = parseInt(index, 10);
31805
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31806
+ const row = sheet.rows[rowIndex];
31690
31807
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
31691
31808
  for (let i in row.cells) {
31692
- const colIndex = parseInt(i, 10);
31809
+ const colIndex = Number(i);
31693
31810
  const cellId = row.cells[i];
31694
31811
  if (cellId) {
31695
31812
  this.dispatch("CLEAR_CELL", {
@@ -31702,7 +31819,7 @@ class SheetPlugin extends CorePlugin {
31702
31819
  }
31703
31820
  if (rowIndex > deleteToRow) {
31704
31821
  for (let i in row.cells) {
31705
- const colIndex = parseInt(i, 10);
31822
+ const colIndex = Number(i);
31706
31823
  const cellId = row.cells[i];
31707
31824
  if (cellId) {
31708
31825
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -31720,7 +31837,7 @@ class SheetPlugin extends CorePlugin {
31720
31837
  const rows = [];
31721
31838
  const cellsQueue = sheet.rows.map((row) => row.cells);
31722
31839
  for (let i in sheet.rows) {
31723
- if (parseInt(i, 10) === index) {
31840
+ if (Number(i) === index) {
31724
31841
  continue;
31725
31842
  }
31726
31843
  rows.push({
@@ -32863,13 +32980,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32863
32980
  return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
32864
32981
  }
32865
32982
  intersectZoneWithViewport(sheetId, zone) {
32866
- const colsRange = range(zone.left, zone.right + 1);
32867
- const rowsRange = range(zone.top, zone.bottom + 1);
32868
32983
  return {
32869
- left: this.getters.findVisibleHeader(sheetId, "COL", colsRange),
32870
- right: this.getters.findVisibleHeader(sheetId, "COL", colsRange.reverse()),
32871
- top: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange),
32872
- bottom: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange.reverse()),
32984
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
32985
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
32986
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
32987
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32873
32988
  };
32874
32989
  }
32875
32990
  updateFilter({ col, row, values, sheetId }) {
@@ -34975,13 +35090,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34975
35090
  getNextVisibleCellPosition({ sheetId, col, row }) {
34976
35091
  return {
34977
35092
  sheetId,
34978
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
34979
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
35093
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
35094
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
34980
35095
  };
34981
35096
  }
34982
- findVisibleHeader(sheetId, dimension, indexes) {
34983
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
34984
- !this.isHeaderHidden(sheetId, dimension, index));
35097
+ /**
35098
+ * Find the first visible header in the range [`from` => `to`].
35099
+ *
35100
+ * Both `from` and `to` are inclusive.
35101
+ */
35102
+ findVisibleHeader(sheetId, dimension, from, to) {
35103
+ if (from <= to) {
35104
+ for (let i = from; i <= to; i++) {
35105
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35106
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35107
+ return i;
35108
+ }
35109
+ }
35110
+ }
35111
+ if (from > to) {
35112
+ for (let i = from; i >= to; i--) {
35113
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35114
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35115
+ return i;
35116
+ }
35117
+ }
35118
+ }
35119
+ return undefined;
34985
35120
  }
34986
35121
  findLastVisibleColRowIndex(sheetId, dimension, { last, first }) {
34987
35122
  const lastVisibleIndex = range(last, first, -1).find((index) => !this.isHeaderHidden(sheetId, dimension, index));
@@ -34989,7 +35124,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34989
35124
  }
34990
35125
  findFirstVisibleColRowIndex(sheetId, dimension) {
34991
35126
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
34992
- for (let i = 0; i < numberOfHeaders - 1; i++) {
35127
+ for (let i = 0; i < numberOfHeaders; i++) {
34993
35128
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
34994
35129
  return i;
34995
35130
  }
@@ -39216,7 +39351,7 @@ class BottomBar extends owl.Component {
39216
39351
  }
39217
39352
  openContextMenu(x, y, registry, menuId) {
39218
39353
  this.menuState.isOpen = true;
39219
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
39354
+ this.menuState.menuItems = registry.getAll();
39220
39355
  this.menuState.position = { x, y };
39221
39356
  this.menuState.menuId = menuId;
39222
39357
  }
@@ -40023,7 +40158,7 @@ class TopBar extends owl.Component {
40023
40158
  const { left, top, height } = ev.target.getBoundingClientRect();
40024
40159
  this.state.menuState.isOpen = true;
40025
40160
  this.state.menuState.position = { x: left, y: top + height };
40026
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
40161
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
40027
40162
  this.state.menuState.parentMenu = menu;
40028
40163
  this.isSelectingMenu = true;
40029
40164
  this.openedEl = ev.target;
@@ -40066,9 +40201,7 @@ class TopBar extends owl.Component {
40066
40201
  this.style.align = this.style.align || cell.defaultAlign;
40067
40202
  this.fillColor = this.style.fillColor || "#ffffff";
40068
40203
  this.textColor = this.style.textColor || "#000000";
40069
- this.menus = topbarMenuRegistry
40070
- .getAll()
40071
- .filter((item) => !item.isVisible || item.isVisible(this.env));
40204
+ this.menus = topbarMenuRegistry.getAll();
40072
40205
  }
40073
40206
  getMenuName(menu) {
40074
40207
  return getMenuName(menu, this.env);
@@ -41782,8 +41915,8 @@ class SelectionStreamProcessor {
41782
41915
  }
41783
41916
  const { left, right, top, bottom } = zone;
41784
41917
  const sheetId = this.getters.getActiveSheetId();
41785
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
41786
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
41918
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
41919
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
41787
41920
  if (refRow === undefined || refCol === undefined) {
41788
41921
  return 17 /* CommandResult.SelectionOutOfBound */;
41789
41922
  }
@@ -41858,10 +41991,10 @@ class SelectionStreamProcessor {
41858
41991
  const { col: anchorCol, row: anchorRow } = anchor.cell;
41859
41992
  return {
41860
41993
  col: this.getters.isColHidden(sheetId, anchorCol)
41861
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
41994
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
41862
41995
  : anchorCol,
41863
41996
  row: this.getters.isRowHidden(sheetId, anchorRow)
41864
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
41997
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
41865
41998
  : anchorRow,
41866
41999
  };
41867
42000
  }
@@ -43542,6 +43675,8 @@ class Model extends EventBus {
43542
43675
  * Internal status of the model. Important for command handling coordination
43543
43676
  */
43544
43677
  this.status = 0 /* Status.Ready */;
43678
+ this.handlers = [];
43679
+ this.coreHandlers = [];
43545
43680
  /**
43546
43681
  * The dispatch method is the only entry point to manipulate data in the model.
43547
43682
  * This is through this method that commands are dispatched most of the time
@@ -43646,6 +43781,8 @@ class Model extends EventBus {
43646
43781
  this.uuidGenerator.setIsFastStrategy(true);
43647
43782
  // Initiate stream processor
43648
43783
  this.selection = new SelectionStreamProcessor(this.getters);
43784
+ this.coreHandlers.push(this.range);
43785
+ this.handlers.push(this.range);
43649
43786
  this.corePluginConfig = this.setupCorePluginConfig();
43650
43787
  this.uiPluginConfig = this.setupUiPluginConfig();
43651
43788
  // registering plugins
@@ -43654,15 +43791,23 @@ class Model extends EventBus {
43654
43791
  }
43655
43792
  Object.assign(this.getters, this.coreGetters);
43656
43793
  for (let Plugin of statefulUIPluginRegistry.getAll()) {
43657
- this.statefulUIPlugins.push(this.setupUiPlugin(Plugin));
43794
+ const plugin = this.setupUiPlugin(Plugin);
43795
+ this.statefulUIPlugins.push(plugin);
43796
+ this.handlers.push(plugin);
43658
43797
  }
43659
43798
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
43660
- this.coreViewsPlugins.push(this.setupUiPlugin(Plugin));
43799
+ const plugin = this.setupUiPlugin(Plugin);
43800
+ this.coreViewsPlugins.push(plugin);
43801
+ this.handlers.push(plugin);
43802
+ this.coreHandlers.push(plugin);
43661
43803
  }
43662
43804
  for (let Plugin of featurePluginRegistry.getAll()) {
43663
- this.featurePlugins.push(this.setupUiPlugin(Plugin));
43805
+ const plugin = this.setupUiPlugin(Plugin);
43806
+ this.featurePlugins.push(plugin);
43807
+ this.handlers.push(plugin);
43664
43808
  }
43665
43809
  this.uuidGenerator.setIsFastStrategy(false);
43810
+ this.handlers.push(this.history);
43666
43811
  // starting plugins
43667
43812
  this.dispatch("START");
43668
43813
  // Model should be the last permanent subscriber in the list since he should render
@@ -43684,12 +43829,6 @@ class Model extends EventBus {
43684
43829
  // by owl, since we do not rely on reactivity
43685
43830
  owl.markRaw(this);
43686
43831
  }
43687
- get handlers() {
43688
- return [this.range, ...this.corePlugins, ...this.allUIPlugins, this.history];
43689
- }
43690
- get allUIPlugins() {
43691
- return [...this.statefulUIPlugins, ...this.coreViewsPlugins, ...this.featurePlugins];
43692
- }
43693
43832
  joinSession() {
43694
43833
  this.session.join(this.config.client);
43695
43834
  }
@@ -43731,6 +43870,8 @@ class Model extends EventBus {
43731
43870
  }
43732
43871
  plugin.import(data);
43733
43872
  this.corePlugins.push(plugin);
43873
+ this.coreHandlers.push(plugin);
43874
+ this.handlers.push(plugin);
43734
43875
  }
43735
43876
  onRemoteRevisionReceived({ commands }) {
43736
43877
  for (let command of commands) {
@@ -43873,7 +44014,7 @@ class Model extends EventBus {
43873
44014
  }
43874
44015
  }
43875
44016
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
43876
- data = JSON.parse(JSON.stringify(data));
44017
+ data = deepCopy(data);
43877
44018
  return data;
43878
44019
  }
43879
44020
  updateMode(mode) {
@@ -43901,7 +44042,7 @@ class Model extends EventBus {
43901
44042
  handler.exportForExcel(data);
43902
44043
  }
43903
44044
  }
43904
- data = JSON.parse(JSON.stringify(data));
44045
+ data = deepCopy(data);
43905
44046
  return getXLSX(data);
43906
44047
  }
43907
44048
  garbageCollectExternalResources() {
@@ -44042,6 +44183,6 @@ exports.setTranslationMethod = setTranslationMethod;
44042
44183
  exports.tokenize = tokenize;
44043
44184
 
44044
44185
 
44045
- __info__.version = '16.1.8';
44046
- __info__.date = '2023-04-14T12:46:41.355Z';
44047
- __info__.hash = '4399770';
44186
+ __info__.version = '16.1.9';
44187
+ __info__.date = '2023-04-21T07:29:41.080Z';
44188
+ __info__.hash = '686bffa';