@odoo/o-spreadsheet 16.1.8 → 16.1.10

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", {
@@ -3555,6 +3624,8 @@ var CommandResult;
3555
3624
  CommandResult[CommandResult["InvalidSelectionStep"] = 83] = "InvalidSelectionStep";
3556
3625
  CommandResult[CommandResult["DuplicatedChartId"] = 84] = "DuplicatedChartId";
3557
3626
  CommandResult[CommandResult["ChartDoesNotExist"] = 85] = "ChartDoesNotExist";
3627
+ CommandResult[CommandResult["InvalidHeaderIndex"] = 86] = "InvalidHeaderIndex";
3628
+ CommandResult[CommandResult["InvalidQuantity"] = 87] = "InvalidQuantity";
3558
3629
  })(CommandResult || (CommandResult = {}));
3559
3630
 
3560
3631
  var DIRECTION;
@@ -5902,13 +5973,16 @@ class Menu extends Component {
5902
5973
  }
5903
5974
  });
5904
5975
  }
5976
+ get visibleMenuItems() {
5977
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
5978
+ }
5905
5979
  get subMenuPosition() {
5906
5980
  const position = Object.assign({}, this.subMenu.position);
5907
5981
  position.y -= this.subMenu.scrollOffset || 0;
5908
5982
  return position;
5909
5983
  }
5910
5984
  get menuHeight() {
5911
- const menuItems = this.props.menuItems;
5985
+ const menuItems = this.visibleMenuItems;
5912
5986
  let menuItemsHeight = this.getMenuItemsHeight(menuItems);
5913
5987
  // We don't display separator at the end of a menu
5914
5988
  if (menuItems[menuItems.length - 1].separator) {
@@ -5950,7 +6024,7 @@ class Menu extends Component {
5950
6024
  * and the menu item at a given index.
5951
6025
  */
5952
6026
  subMenuVerticalPosition(menuIndex) {
5953
- const menusAbove = this.props.menuItems.slice(0, menuIndex);
6027
+ const menusAbove = this.visibleMenuItems.slice(0, menuIndex);
5954
6028
  return this.position.y + this.getMenuItemsHeight(menusAbove) + MENU_VERTICAL_PADDING;
5955
6029
  }
5956
6030
  onExternalClick(ev) {
@@ -6122,7 +6196,7 @@ class ChartFigure extends Component {
6122
6196
  openContextMenu(position) {
6123
6197
  const registry = this.getMenuItemRegistry();
6124
6198
  this.menuState.isOpen = true;
6125
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
6199
+ this.menuState.menuItems = registry.getAll();
6126
6200
  this.menuState.position = position;
6127
6201
  }
6128
6202
  get chartComponent() {
@@ -7253,6 +7327,11 @@ colMenuRegistry
7253
7327
  name: REMOVE_COLUMNS_NAME,
7254
7328
  sequence: 90,
7255
7329
  action: REMOVE_COLUMNS_ACTION,
7330
+ isVisible: (env) => {
7331
+ const sheetId = env.model.getters.getActiveSheetId();
7332
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7333
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7334
+ },
7256
7335
  })
7257
7336
  .add("clear_column", {
7258
7337
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -7265,9 +7344,8 @@ colMenuRegistry
7265
7344
  action: HIDE_COLUMNS_ACTION,
7266
7345
  isVisible: (env) => {
7267
7346
  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);
7347
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7348
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7271
7349
  },
7272
7350
  separator: true,
7273
7351
  })
@@ -7341,6 +7419,11 @@ rowMenuRegistry
7341
7419
  name: REMOVE_ROWS_NAME,
7342
7420
  sequence: 70,
7343
7421
  action: REMOVE_ROWS_ACTION,
7422
+ isVisible: (env) => {
7423
+ const sheetId = env.model.getters.getActiveSheetId();
7424
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7425
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7426
+ },
7344
7427
  })
7345
7428
  .add("clear_row", {
7346
7429
  name: DELETE_CONTENT_ROWS_NAME,
@@ -7353,9 +7436,8 @@ rowMenuRegistry
7353
7436
  action: HIDE_ROWS_ACTION,
7354
7437
  isVisible: (env) => {
7355
7438
  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);
7439
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7440
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7359
7441
  },
7360
7442
  separator: true,
7361
7443
  })
@@ -14413,7 +14495,7 @@ const POW = {
14413
14495
  base (number) ${_lt("The number to raise to the exponent power.")}
14414
14496
  exponent (number) ${_lt("The exponent to raise base to.")}
14415
14497
  `),
14416
- returns: ["BOOLEAN"],
14498
+ returns: ["NUMBER"],
14417
14499
  compute: function (base, exponent) {
14418
14500
  return POWER.compute(base, exponent);
14419
14501
  },
@@ -16928,14 +17010,31 @@ class ContentEditableHelper {
16928
17010
  * */
16929
17011
  getCurrentSelection() {
16930
17012
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
16931
- let startSizeBefore = this.findSizeBeforeElement(startElement);
16932
- let endSizeBefore = this.findSizeBeforeElement(endElement);
17013
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
17014
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
16933
17015
  return {
16934
- start: startSizeBefore + startSelectionOffset,
16935
- end: endSizeBefore + endSelectionOffset,
17016
+ start: startSizeBefore,
17017
+ end: endSizeBefore,
16936
17018
  };
16937
17019
  }
16938
- findSizeBeforeElement(nodeToFind) {
17020
+ /**
17021
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
17022
+ * The selected node is either a Text node or an Element node.
17023
+ *
17024
+ * case 1 -Text node:
17025
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
17026
+ * content length of all previous nodes.
17027
+ *
17028
+ * case 2 - Element node:
17029
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
17030
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
17031
+ *
17032
+ * See the MDN documentation for more details.
17033
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
17034
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
17035
+ *
17036
+ */
17037
+ findSelectionIndex(nodeToFind, nodeOffset) {
16939
17038
  let it = this.iterateChildren(this.el);
16940
17039
  let usedCharacters = 0;
16941
17040
  let current = it.next();
@@ -16947,6 +17046,19 @@ class ContentEditableHelper {
16947
17046
  }
16948
17047
  current = it.next();
16949
17048
  }
17049
+ if (current.value === nodeToFind) {
17050
+ if (!current.value.hasChildNodes()) {
17051
+ usedCharacters += nodeOffset;
17052
+ }
17053
+ else {
17054
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
17055
+ for (const child of children) {
17056
+ if (!child.textContent)
17057
+ continue;
17058
+ usedCharacters += child.textContent.length;
17059
+ }
17060
+ }
17061
+ }
16950
17062
  return usedCharacters;
16951
17063
  }
16952
17064
  getStartAndEndSelection() {
@@ -23538,8 +23650,8 @@ class Grid extends Component {
23538
23650
  },
23539
23651
  "CTRL+END": () => {
23540
23652
  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());
23653
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
23654
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
23543
23655
  this.env.model.selection.selectCell(col, row);
23544
23656
  },
23545
23657
  "SHIFT+ ": () => {
@@ -23619,9 +23731,10 @@ class Grid extends Component {
23619
23731
  this.focus();
23620
23732
  }
23621
23733
  focus() {
23734
+ var _a;
23622
23735
  if (!this.env.model.getters.getSelectedFigureId() &&
23623
23736
  this.env.model.getters.getEditionMode() === "inactive") {
23624
- this.hiddenInput.el.focus();
23737
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
23625
23738
  }
23626
23739
  }
23627
23740
  get gridEl() {
@@ -23822,9 +23935,7 @@ class Grid extends Component {
23822
23935
  }
23823
23936
  this.menuState.isOpen = true;
23824
23937
  this.menuState.position = { x, y };
23825
- this.menuState.menuItems = registries$1[type]
23826
- .getAll()
23827
- .filter((item) => !item.isVisible || item.isVisible(this.env));
23938
+ this.menuState.menuItems = registries$1[type].getAll();
23828
23939
  }
23829
23940
  copy(cut, ev) {
23830
23941
  if (!this.gridEl.contains(document.activeElement)) {
@@ -25078,7 +25189,7 @@ function extractStyle(cell, data) {
25078
25189
  if (cell.style) {
25079
25190
  style = data.styles[cell.style];
25080
25191
  }
25081
- const format = cell.format ? data.formats[cell.format] : undefined;
25192
+ const format = extractFormat(cell, data);
25082
25193
  const exportedBorder = {};
25083
25194
  if (cell.border) {
25084
25195
  const border = data.borders[cell.border];
@@ -25112,6 +25223,22 @@ function extractStyle(cell, data) {
25112
25223
  styles.font["italic"] = !!(style === null || style === void 0 ? void 0 : style.italic) || undefined;
25113
25224
  return styles;
25114
25225
  }
25226
+ function extractFormat(cell, data) {
25227
+ if (cell.format) {
25228
+ return data.formats[cell.format];
25229
+ }
25230
+ if (cell.isFormula) {
25231
+ const tokens = tokenize(cell.content || "");
25232
+ const functions = functionRegistry.content;
25233
+ const isExported = tokens
25234
+ .filter((tk) => tk.type === "FUNCTION")
25235
+ .every((tk) => functions[tk.value.toUpperCase()].isExported);
25236
+ if (!isExported) {
25237
+ return cell.computedFormat;
25238
+ }
25239
+ }
25240
+ return undefined;
25241
+ }
25115
25242
  function normalizeStyle(construct, styles) {
25116
25243
  const { id: fontId } = pushElement(styles["font"], construct.fonts);
25117
25244
  const { id: fillId } = pushElement(styles["fill"], construct.fills);
@@ -27193,7 +27320,7 @@ function load(data, verboseImport) {
27193
27320
  }
27194
27321
  }
27195
27322
  }
27196
- data = JSON.parse(JSON.stringify(data));
27323
+ data = deepCopy(data);
27197
27324
  // apply migrations, if needed
27198
27325
  if ("version" in data) {
27199
27326
  if (data.version < CURRENT_VERSION) {
@@ -29853,7 +29980,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29853
29980
  allowDispatch(cmd) {
29854
29981
  switch (cmd.type) {
29855
29982
  case "HIDE_COLUMNS_ROWS": {
29856
- if (!this.hiddenHeaders[cmd.sheetId]) {
29983
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29857
29984
  return 27 /* CommandResult.InvalidSheetId */;
29858
29985
  }
29859
29986
  const hiddenGroup = cmd.dimension === "COL"
@@ -29862,10 +29989,25 @@ class HeaderVisibilityPlugin extends CorePlugin {
29862
29989
  const elements = cmd.dimension === "COL"
29863
29990
  ? this.getters.getNumberCols(cmd.sheetId)
29864
29991
  : this.getters.getNumberRows(cmd.sheetId);
29865
- return (hiddenGroup || []).flat().concat(cmd.elements).length < elements
29866
- ? 0 /* CommandResult.Success */
29867
- : 66 /* CommandResult.TooManyHiddenElements */;
29992
+ const hiddenElements = new Set((hiddenGroup || []).flat().concat(cmd.elements));
29993
+ if (hiddenElements.size >= elements) {
29994
+ return 66 /* CommandResult.TooManyHiddenElements */;
29995
+ }
29996
+ else if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
29997
+ return 86 /* CommandResult.InvalidHeaderIndex */;
29998
+ }
29999
+ else {
30000
+ return 0 /* CommandResult.Success */;
30001
+ }
29868
30002
  }
30003
+ case "REMOVE_COLUMNS_ROWS":
30004
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
30005
+ return 27 /* CommandResult.InvalidSheetId */;
30006
+ }
30007
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
30008
+ return 8 /* CommandResult.NotEnoughElements */;
30009
+ }
30010
+ return 0 /* CommandResult.Success */;
29869
30011
  }
29870
30012
  return 0 /* CommandResult.Success */;
29871
30013
  }
@@ -29912,6 +30054,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29912
30054
  }
29913
30055
  return;
29914
30056
  }
30057
+ canRemoveHeaders(sheetId, dimension, elements) {
30058
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
30059
+ return !includesAll(elements, visibleHeaders);
30060
+ }
29915
30061
  isRowHiddenByUser(sheetId, index) {
29916
30062
  return this.hiddenHeaders[sheetId].ROW[index];
29917
30063
  }
@@ -29956,6 +30102,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29956
30102
  }
29957
30103
  return consecutiveIndexes;
29958
30104
  }
30105
+ getAllVisibleHeaders(sheetId, dimension) {
30106
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
30107
+ }
29959
30108
  import(data) {
29960
30109
  var _a, _b;
29961
30110
  for (let sheet of data.sheets) {
@@ -30003,6 +30152,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
30003
30152
  }
30004
30153
  }
30005
30154
  HeaderVisibilityPlugin.getters = [
30155
+ "canRemoveHeaders",
30006
30156
  "getHiddenColsGroups",
30007
30157
  "getHiddenRowsGroups",
30008
30158
  "isRowHiddenByUser",
@@ -31012,13 +31162,27 @@ class SheetPlugin extends CorePlugin {
31012
31162
  return this.orderedSheetIds.length > 1
31013
31163
  ? 0 /* CommandResult.Success */
31014
31164
  : 9 /* CommandResult.NotEnoughSheets */;
31165
+ case "ADD_COLUMNS_ROWS":
31166
+ const elements = cmd.dimension === "COL"
31167
+ ? this.getNumberCols(cmd.sheetId)
31168
+ : this.getNumberRows(cmd.sheetId);
31169
+ if (cmd.base < 0 || cmd.base > elements) {
31170
+ return 86 /* CommandResult.InvalidHeaderIndex */;
31171
+ }
31172
+ else if (cmd.quantity <= 0) {
31173
+ return 87 /* CommandResult.InvalidQuantity */;
31174
+ }
31175
+ return 0 /* CommandResult.Success */;
31015
31176
  case "REMOVE_COLUMNS_ROWS": {
31016
- const length = cmd.dimension === "COL"
31177
+ const elements = cmd.dimension === "COL"
31017
31178
  ? this.getNumberCols(cmd.sheetId)
31018
31179
  : this.getNumberRows(cmd.sheetId);
31019
- return length > cmd.elements.length
31020
- ? 0 /* CommandResult.Success */
31021
- : 8 /* CommandResult.NotEnoughElements */;
31180
+ if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
31181
+ return 86 /* CommandResult.InvalidHeaderIndex */;
31182
+ }
31183
+ else {
31184
+ return 0 /* CommandResult.Success */;
31185
+ }
31022
31186
  }
31023
31187
  case "FREEZE_ROWS": {
31024
31188
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
@@ -31490,7 +31654,7 @@ class SheetPlugin extends CorePlugin {
31490
31654
  duplicateSheet(fromId, toId) {
31491
31655
  const sheet = this.getSheet(fromId);
31492
31656
  const toName = this.getDuplicateSheetName(sheet.name);
31493
- const newSheet = JSON.parse(JSON.stringify(sheet));
31657
+ const newSheet = deepCopy(sheet);
31494
31658
  newSheet.id = toId;
31495
31659
  newSheet.name = toName;
31496
31660
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -31617,10 +31781,10 @@ class SheetPlugin extends CorePlugin {
31617
31781
  }
31618
31782
  }
31619
31783
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
31620
- for (let [index, row] of Object.entries(sheet.rows)) {
31621
- const rowIndex = parseInt(index, 10);
31784
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31785
+ const row = sheet.rows[rowIndex];
31622
31786
  for (let i in row.cells) {
31623
- const colIndex = parseInt(i, 10);
31787
+ const colIndex = Number(i);
31624
31788
  const cellId = row.cells[i];
31625
31789
  if (cellId) {
31626
31790
  if (colIndex === deletedColumn) {
@@ -31647,11 +31811,11 @@ class SheetPlugin extends CorePlugin {
31647
31811
  */
31648
31812
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
31649
31813
  const commands = [];
31650
- for (const [index, row] of Object.entries(sheet.rows)) {
31651
- const rowIndex = parseInt(index, 10);
31814
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31815
+ const row = sheet.rows[rowIndex];
31652
31816
  if (dimension !== "rows" || rowIndex >= addedElement) {
31653
31817
  for (let i in row.cells) {
31654
- const colIndex = parseInt(i, 10);
31818
+ const colIndex = Number(i);
31655
31819
  const cellId = row.cells[i];
31656
31820
  if (cellId) {
31657
31821
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -31681,11 +31845,11 @@ class SheetPlugin extends CorePlugin {
31681
31845
  */
31682
31846
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
31683
31847
  const numberRows = deleteToRow - deleteFromRow + 1;
31684
- for (let [index, row] of Object.entries(sheet.rows)) {
31685
- const rowIndex = parseInt(index, 10);
31848
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31849
+ const row = sheet.rows[rowIndex];
31686
31850
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
31687
31851
  for (let i in row.cells) {
31688
- const colIndex = parseInt(i, 10);
31852
+ const colIndex = Number(i);
31689
31853
  const cellId = row.cells[i];
31690
31854
  if (cellId) {
31691
31855
  this.dispatch("CLEAR_CELL", {
@@ -31698,7 +31862,7 @@ class SheetPlugin extends CorePlugin {
31698
31862
  }
31699
31863
  if (rowIndex > deleteToRow) {
31700
31864
  for (let i in row.cells) {
31701
- const colIndex = parseInt(i, 10);
31865
+ const colIndex = Number(i);
31702
31866
  const cellId = row.cells[i];
31703
31867
  if (cellId) {
31704
31868
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -31716,7 +31880,7 @@ class SheetPlugin extends CorePlugin {
31716
31880
  const rows = [];
31717
31881
  const cellsQueue = sheet.rows.map((row) => row.cells);
31718
31882
  for (let i in sheet.rows) {
31719
- if (parseInt(i, 10) === index) {
31883
+ if (Number(i) === index) {
31720
31884
  continue;
31721
31885
  }
31722
31886
  rows.push({
@@ -32268,8 +32432,12 @@ class EvaluationPlugin extends UIPlugin {
32268
32432
  const cell = this.getters.getCell(position);
32269
32433
  if (cell) {
32270
32434
  const exportedCellData = sheet.cells[xc];
32271
- exportedCellData.value = this.getEvaluatedCell(position).value;
32435
+ const evaluatedCell = this.getEvaluatedCell(position);
32436
+ exportedCellData.value = evaluatedCell.value;
32272
32437
  exportedCellData.isFormula = cell.isFormula && !this.isBadExpression(cell.content);
32438
+ if (cell.format !== evaluatedCell.format) {
32439
+ exportedCellData.computedFormat = evaluatedCell.format;
32440
+ }
32273
32441
  }
32274
32442
  }
32275
32443
  }
@@ -32859,13 +33027,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32859
33027
  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
33028
  }
32861
33029
  intersectZoneWithViewport(sheetId, zone) {
32862
- const colsRange = range(zone.left, zone.right + 1);
32863
- const rowsRange = range(zone.top, zone.bottom + 1);
32864
33030
  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()),
33031
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
33032
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
33033
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
33034
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32869
33035
  };
32870
33036
  }
32871
33037
  updateFilter({ col, row, values, sheetId }) {
@@ -34971,13 +35137,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34971
35137
  getNextVisibleCellPosition({ sheetId, col, row }) {
34972
35138
  return {
34973
35139
  sheetId,
34974
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
34975
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
35140
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
35141
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
34976
35142
  };
34977
35143
  }
34978
- findVisibleHeader(sheetId, dimension, indexes) {
34979
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
34980
- !this.isHeaderHidden(sheetId, dimension, index));
35144
+ /**
35145
+ * Find the first visible header in the range [`from` => `to`].
35146
+ *
35147
+ * Both `from` and `to` are inclusive.
35148
+ */
35149
+ findVisibleHeader(sheetId, dimension, from, to) {
35150
+ if (from <= to) {
35151
+ for (let i = from; i <= to; i++) {
35152
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35153
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35154
+ return i;
35155
+ }
35156
+ }
35157
+ }
35158
+ if (from > to) {
35159
+ for (let i = from; i >= to; i--) {
35160
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35161
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35162
+ return i;
35163
+ }
35164
+ }
35165
+ }
35166
+ return undefined;
34981
35167
  }
34982
35168
  findLastVisibleColRowIndex(sheetId, dimension, { last, first }) {
34983
35169
  const lastVisibleIndex = range(last, first, -1).find((index) => !this.isHeaderHidden(sheetId, dimension, index));
@@ -34985,7 +35171,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34985
35171
  }
34986
35172
  findFirstVisibleColRowIndex(sheetId, dimension) {
34987
35173
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
34988
- for (let i = 0; i < numberOfHeaders - 1; i++) {
35174
+ for (let i = 0; i < numberOfHeaders; i++) {
34989
35175
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
34990
35176
  return i;
34991
35177
  }
@@ -39212,7 +39398,7 @@ class BottomBar extends Component {
39212
39398
  }
39213
39399
  openContextMenu(x, y, registry, menuId) {
39214
39400
  this.menuState.isOpen = true;
39215
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
39401
+ this.menuState.menuItems = registry.getAll();
39216
39402
  this.menuState.position = { x, y };
39217
39403
  this.menuState.menuId = menuId;
39218
39404
  }
@@ -40019,7 +40205,7 @@ class TopBar extends Component {
40019
40205
  const { left, top, height } = ev.target.getBoundingClientRect();
40020
40206
  this.state.menuState.isOpen = true;
40021
40207
  this.state.menuState.position = { x: left, y: top + height };
40022
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
40208
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
40023
40209
  this.state.menuState.parentMenu = menu;
40024
40210
  this.isSelectingMenu = true;
40025
40211
  this.openedEl = ev.target;
@@ -40062,9 +40248,7 @@ class TopBar extends Component {
40062
40248
  this.style.align = this.style.align || cell.defaultAlign;
40063
40249
  this.fillColor = this.style.fillColor || "#ffffff";
40064
40250
  this.textColor = this.style.textColor || "#000000";
40065
- this.menus = topbarMenuRegistry
40066
- .getAll()
40067
- .filter((item) => !item.isVisible || item.isVisible(this.env));
40251
+ this.menus = topbarMenuRegistry.getAll();
40068
40252
  }
40069
40253
  getMenuName(menu) {
40070
40254
  return getMenuName(menu, this.env);
@@ -41778,8 +41962,8 @@ class SelectionStreamProcessor {
41778
41962
  }
41779
41963
  const { left, right, top, bottom } = zone;
41780
41964
  const sheetId = this.getters.getActiveSheetId();
41781
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
41782
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
41965
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
41966
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
41783
41967
  if (refRow === undefined || refCol === undefined) {
41784
41968
  return 17 /* CommandResult.SelectionOutOfBound */;
41785
41969
  }
@@ -41854,10 +42038,10 @@ class SelectionStreamProcessor {
41854
42038
  const { col: anchorCol, row: anchorRow } = anchor.cell;
41855
42039
  return {
41856
42040
  col: this.getters.isColHidden(sheetId, anchorCol)
41857
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
42041
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
41858
42042
  : anchorCol,
41859
42043
  row: this.getters.isRowHidden(sheetId, anchorRow)
41860
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
42044
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
41861
42045
  : anchorRow,
41862
42046
  };
41863
42047
  }
@@ -42431,12 +42615,9 @@ function addFormula(cell) {
42431
42615
  else {
42432
42616
  // Shouldn't we always output the value then ?
42433
42617
  const value = cell.value;
42434
- // what if value = 0? Is this condition correct?
42435
- if (value) {
42436
- const type = getCellType(value);
42437
- attrs.push(["t", type]);
42438
- node = escapeXml /*xml*/ `<v>${value}</v>`;
42439
- }
42618
+ const type = getCellType(value);
42619
+ attrs.push(["t", type]);
42620
+ node = escapeXml /*xml*/ `<v>${value}</v>`;
42440
42621
  return { attrs, node };
42441
42622
  }
42442
42623
  }
@@ -43538,6 +43719,8 @@ class Model extends EventBus {
43538
43719
  * Internal status of the model. Important for command handling coordination
43539
43720
  */
43540
43721
  this.status = 0 /* Status.Ready */;
43722
+ this.handlers = [];
43723
+ this.coreHandlers = [];
43541
43724
  /**
43542
43725
  * The dispatch method is the only entry point to manipulate data in the model.
43543
43726
  * This is through this method that commands are dispatched most of the time
@@ -43642,6 +43825,8 @@ class Model extends EventBus {
43642
43825
  this.uuidGenerator.setIsFastStrategy(true);
43643
43826
  // Initiate stream processor
43644
43827
  this.selection = new SelectionStreamProcessor(this.getters);
43828
+ this.coreHandlers.push(this.range);
43829
+ this.handlers.push(this.range);
43645
43830
  this.corePluginConfig = this.setupCorePluginConfig();
43646
43831
  this.uiPluginConfig = this.setupUiPluginConfig();
43647
43832
  // registering plugins
@@ -43650,15 +43835,23 @@ class Model extends EventBus {
43650
43835
  }
43651
43836
  Object.assign(this.getters, this.coreGetters);
43652
43837
  for (let Plugin of statefulUIPluginRegistry.getAll()) {
43653
- this.statefulUIPlugins.push(this.setupUiPlugin(Plugin));
43838
+ const plugin = this.setupUiPlugin(Plugin);
43839
+ this.statefulUIPlugins.push(plugin);
43840
+ this.handlers.push(plugin);
43654
43841
  }
43655
43842
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
43656
- this.coreViewsPlugins.push(this.setupUiPlugin(Plugin));
43843
+ const plugin = this.setupUiPlugin(Plugin);
43844
+ this.coreViewsPlugins.push(plugin);
43845
+ this.handlers.push(plugin);
43846
+ this.coreHandlers.push(plugin);
43657
43847
  }
43658
43848
  for (let Plugin of featurePluginRegistry.getAll()) {
43659
- this.featurePlugins.push(this.setupUiPlugin(Plugin));
43849
+ const plugin = this.setupUiPlugin(Plugin);
43850
+ this.featurePlugins.push(plugin);
43851
+ this.handlers.push(plugin);
43660
43852
  }
43661
43853
  this.uuidGenerator.setIsFastStrategy(false);
43854
+ this.handlers.push(this.history);
43662
43855
  // starting plugins
43663
43856
  this.dispatch("START");
43664
43857
  // Model should be the last permanent subscriber in the list since he should render
@@ -43680,12 +43873,6 @@ class Model extends EventBus {
43680
43873
  // by owl, since we do not rely on reactivity
43681
43874
  markRaw(this);
43682
43875
  }
43683
- get handlers() {
43684
- return [this.range, ...this.corePlugins, ...this.allUIPlugins, this.history];
43685
- }
43686
- get allUIPlugins() {
43687
- return [...this.statefulUIPlugins, ...this.coreViewsPlugins, ...this.featurePlugins];
43688
- }
43689
43876
  joinSession() {
43690
43877
  this.session.join(this.config.client);
43691
43878
  }
@@ -43727,6 +43914,8 @@ class Model extends EventBus {
43727
43914
  }
43728
43915
  plugin.import(data);
43729
43916
  this.corePlugins.push(plugin);
43917
+ this.coreHandlers.push(plugin);
43918
+ this.handlers.push(plugin);
43730
43919
  }
43731
43920
  onRemoteRevisionReceived({ commands }) {
43732
43921
  for (let command of commands) {
@@ -43869,7 +44058,7 @@ class Model extends EventBus {
43869
44058
  }
43870
44059
  }
43871
44060
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
43872
- data = JSON.parse(JSON.stringify(data));
44061
+ data = deepCopy(data);
43873
44062
  return data;
43874
44063
  }
43875
44064
  updateMode(mode) {
@@ -43897,7 +44086,7 @@ class Model extends EventBus {
43897
44086
  handler.exportForExcel(data);
43898
44087
  }
43899
44088
  }
43900
- data = JSON.parse(JSON.stringify(data));
44089
+ data = deepCopy(data);
43901
44090
  return getXLSX(data);
43902
44091
  }
43903
44092
  garbageCollectExternalResources() {
@@ -44010,6 +44199,6 @@ function addFunction(functionName, functionDescription) {
44010
44199
  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 };
44011
44200
 
44012
44201
 
44013
- __info__.version = '16.1.8';
44014
- __info__.date = '2023-04-14T12:46:41.355Z';
44015
- __info__.hash = '4399770';
44202
+ __info__.version = '16.1.10';
44203
+ __info__.date = '2023-04-28T12:24:14.323Z';
44204
+ __info__.hash = '9632927';