@odoo/o-spreadsheet 16.0.7 → 16.0.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
  */
@@ -1568,12 +1572,77 @@ const numberRepresentation = [];
1568
1572
  * - all digit stored in the decimal part of the number
1569
1573
  *
1570
1574
  * The 'maxDecimal' parameter allows to indicate the number of digits to not
1571
- * exceed in the decimal part, in which case digits are rounded
1575
+ * exceed in the decimal part, in which case digits are rounded.
1572
1576
  *
1573
- * Intl.Numberformat is used to properly handle all the roundings.
1574
- * e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
1575
1577
  **/
1576
1578
  function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
1579
+ const asString = value.toString();
1580
+ if (asString.includes("e"))
1581
+ return splitNumberIntl(value, maxDecimals);
1582
+ if (Number.isInteger(value)) {
1583
+ return { integerDigits: asString, decimalDigits: undefined };
1584
+ }
1585
+ const indexOfDot = asString.indexOf(".");
1586
+ let integerDigits = asString.substring(0, indexOfDot);
1587
+ let decimalDigits = asString.substring(indexOfDot + 1);
1588
+ if (maxDecimals === 0) {
1589
+ if (Number(decimalDigits[0]) >= 5) {
1590
+ integerDigits = (Number(integerDigits) + 1).toString();
1591
+ }
1592
+ return { integerDigits, decimalDigits: undefined };
1593
+ }
1594
+ if (decimalDigits.length > maxDecimals) {
1595
+ const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
1596
+ decimalDigits = roundedDecimalDigits;
1597
+ if (roundedIntegerDigits !== "0") {
1598
+ integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
1599
+ }
1600
+ }
1601
+ return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
1602
+ }
1603
+ /**
1604
+ * Return the given string minus the trailing "0" characters.
1605
+ *
1606
+ * @param numberString : a string of integers
1607
+ * @returns the numberString, minus the eventual zeroes at the end
1608
+ */
1609
+ function removeTrailingZeroes(numberString) {
1610
+ let i = numberString.length - 1;
1611
+ while (i >= 0 && numberString[i] === "0") {
1612
+ i--;
1613
+ }
1614
+ return numberString.slice(0, i + 1) || undefined;
1615
+ }
1616
+ /**
1617
+ * Limit the size of the decimal part of a number to the given number of digits.
1618
+ */
1619
+ function limitDecimalDigits(decimalDigits, maxDecimals) {
1620
+ let integerDigits = "0";
1621
+ let resultDecimalDigits = decimalDigits;
1622
+ // Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
1623
+ // but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
1624
+ let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
1625
+ const i = maxDecimals;
1626
+ if (Number(Number(decimalDigits[i]) < 5)) {
1627
+ return { integerDigits, decimalDigits: slicedDecimalDigits };
1628
+ }
1629
+ // round up
1630
+ const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
1631
+ if (slicedRoundedUp.length > slicedDecimalDigits.length) {
1632
+ integerDigits = (Number(integerDigits) + 1).toString();
1633
+ resultDecimalDigits = undefined;
1634
+ }
1635
+ else {
1636
+ resultDecimalDigits = slicedRoundedUp;
1637
+ }
1638
+ return { integerDigits, decimalDigits: resultDecimalDigits };
1639
+ }
1640
+ /**
1641
+ * Split numbers into decimal/integer digits using Intl.NumberFormat.
1642
+ * Supports numbers with a lot of digits that are transformed to scientific notation by
1643
+ * number.toString(), but is slow.
1644
+ */
1645
+ function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
1577
1646
  let formatter = numberRepresentation[maxDecimals];
1578
1647
  if (!formatter) {
1579
1648
  formatter = new Intl.NumberFormat("en-US", {
@@ -3200,6 +3269,8 @@ var CommandResult;
3200
3269
  CommandResult[CommandResult["InvalidSelectionStep"] = 82] = "InvalidSelectionStep";
3201
3270
  CommandResult[CommandResult["DuplicatedChartId"] = 83] = "DuplicatedChartId";
3202
3271
  CommandResult[CommandResult["ChartDoesNotExist"] = 84] = "ChartDoesNotExist";
3272
+ CommandResult[CommandResult["InvalidHeaderIndex"] = 85] = "InvalidHeaderIndex";
3273
+ CommandResult[CommandResult["InvalidQuantity"] = 86] = "InvalidQuantity";
3203
3274
  })(CommandResult || (CommandResult = {}));
3204
3275
 
3205
3276
  var DIRECTION;
@@ -3970,13 +4041,16 @@ class Menu extends Component {
3970
4041
  }
3971
4042
  });
3972
4043
  }
4044
+ get visibleMenuItems() {
4045
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
4046
+ }
3973
4047
  get subMenuPosition() {
3974
4048
  const position = Object.assign({}, this.subMenu.position);
3975
4049
  position.y -= this.subMenu.scrollOffset || 0;
3976
4050
  return position;
3977
4051
  }
3978
4052
  get menuHeight() {
3979
- return this.menuComponentHeight(this.props.menuItems);
4053
+ return this.menuComponentHeight(this.visibleMenuItems);
3980
4054
  }
3981
4055
  get subMenuHeight() {
3982
4056
  return this.menuComponentHeight(this.subMenu.menuItems);
@@ -4012,7 +4086,7 @@ class Menu extends Component {
4012
4086
  * and the menu item at a given index.
4013
4087
  */
4014
4088
  subMenuVerticalPosition(position) {
4015
- const menusAbove = this.props.menuItems.slice(0, position);
4089
+ const menusAbove = this.visibleMenuItems.slice(0, position);
4016
4090
  return this.menuComponentHeight(menusAbove) + this.position.y;
4017
4091
  }
4018
4092
  onExternalClick(ev) {
@@ -5545,6 +5619,11 @@ colMenuRegistry
5545
5619
  name: REMOVE_COLUMNS_NAME,
5546
5620
  sequence: 90,
5547
5621
  action: REMOVE_COLUMNS_ACTION,
5622
+ isVisible: (env) => {
5623
+ const sheetId = env.model.getters.getActiveSheetId();
5624
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5625
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5626
+ },
5548
5627
  })
5549
5628
  .add("clear_column", {
5550
5629
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -5557,9 +5636,8 @@ colMenuRegistry
5557
5636
  action: HIDE_COLUMNS_ACTION,
5558
5637
  isVisible: (env) => {
5559
5638
  const sheetId = env.model.getters.getActiveSheetId();
5560
- const hiddenCols = env.model.getters.getHiddenColsGroups(sheetId).flat();
5561
- return (env.model.getters.getNumberCols(sheetId) >
5562
- hiddenCols.length + env.model.getters.getElementsFromSelection("COL").length);
5639
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5640
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5563
5641
  },
5564
5642
  separator: true,
5565
5643
  })
@@ -5633,6 +5711,11 @@ rowMenuRegistry
5633
5711
  name: REMOVE_ROWS_NAME,
5634
5712
  sequence: 70,
5635
5713
  action: REMOVE_ROWS_ACTION,
5714
+ isVisible: (env) => {
5715
+ const sheetId = env.model.getters.getActiveSheetId();
5716
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5717
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5718
+ },
5636
5719
  })
5637
5720
  .add("clear_row", {
5638
5721
  name: DELETE_CONTENT_ROWS_NAME,
@@ -5645,9 +5728,8 @@ rowMenuRegistry
5645
5728
  action: HIDE_ROWS_ACTION,
5646
5729
  isVisible: (env) => {
5647
5730
  const sheetId = env.model.getters.getActiveSheetId();
5648
- const hiddenRows = env.model.getters.getHiddenRowsGroups(sheetId).flat();
5649
- return (env.model.getters.getNumberRows(sheetId) >
5650
- hiddenRows.length + env.model.getters.getElementsFromSelection("ROW").length);
5731
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5732
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5651
5733
  },
5652
5734
  separator: true,
5653
5735
  })
@@ -10309,7 +10391,7 @@ class ChartFigure extends Component {
10309
10391
  openContextMenu(position) {
10310
10392
  const registry = this.getMenuItemRegistry();
10311
10393
  this.menuState.isOpen = true;
10312
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
10394
+ this.menuState.menuItems = registry.getAll();
10313
10395
  this.menuState.position = position;
10314
10396
  }
10315
10397
  get chartComponent() {
@@ -17327,7 +17409,7 @@ const POW = {
17327
17409
  base (number) ${_lt("The number to raise to the exponent power.")}
17328
17410
  exponent (number) ${_lt("The exponent to raise base to.")}
17329
17411
  `),
17330
- returns: ["BOOLEAN"],
17412
+ returns: ["NUMBER"],
17331
17413
  compute: function (base, exponent) {
17332
17414
  return POWER.compute(base, exponent);
17333
17415
  },
@@ -19829,14 +19911,31 @@ class ContentEditableHelper {
19829
19911
  * */
19830
19912
  getCurrentSelection() {
19831
19913
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
19832
- let startSizeBefore = this.findSizeBeforeElement(startElement);
19833
- let endSizeBefore = this.findSizeBeforeElement(endElement);
19914
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
19915
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
19834
19916
  return {
19835
- start: startSizeBefore + startSelectionOffset,
19836
- end: endSizeBefore + endSelectionOffset,
19917
+ start: startSizeBefore,
19918
+ end: endSizeBefore,
19837
19919
  };
19838
19920
  }
19839
- findSizeBeforeElement(nodeToFind) {
19921
+ /**
19922
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
19923
+ * The selected node is either a Text node or an Element node.
19924
+ *
19925
+ * case 1 -Text node:
19926
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
19927
+ * content length of all previous nodes.
19928
+ *
19929
+ * case 2 - Element node:
19930
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
19931
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
19932
+ *
19933
+ * See the MDN documentation for more details.
19934
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
19935
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
19936
+ *
19937
+ */
19938
+ findSelectionIndex(nodeToFind, nodeOffset) {
19840
19939
  let it = this.iterateChildren(this.el);
19841
19940
  let usedCharacters = 0;
19842
19941
  let current = it.next();
@@ -19848,6 +19947,19 @@ class ContentEditableHelper {
19848
19947
  }
19849
19948
  current = it.next();
19850
19949
  }
19950
+ if (current.value === nodeToFind) {
19951
+ if (!current.value.hasChildNodes()) {
19952
+ usedCharacters += nodeOffset;
19953
+ }
19954
+ else {
19955
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
19956
+ for (const child of children) {
19957
+ if (!child.textContent)
19958
+ continue;
19959
+ usedCharacters += child.textContent.length;
19960
+ }
19961
+ }
19962
+ }
19851
19963
  return usedCharacters;
19852
19964
  }
19853
19965
  getStartAndEndSelection() {
@@ -22333,8 +22445,8 @@ class Grid extends Component {
22333
22445
  },
22334
22446
  "CTRL+END": () => {
22335
22447
  const sheetId = this.env.model.getters.getActiveSheetId();
22336
- const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", range(0, this.env.model.getters.getNumberCols(sheetId)).reverse());
22337
- const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", range(0, this.env.model.getters.getNumberRows(sheetId)).reverse());
22448
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
22449
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
22338
22450
  this.env.model.selection.selectCell(col, row);
22339
22451
  },
22340
22452
  "SHIFT+ ": () => {
@@ -22410,9 +22522,10 @@ class Grid extends Component {
22410
22522
  this.focus();
22411
22523
  }
22412
22524
  focus() {
22525
+ var _a;
22413
22526
  if (!this.env.model.getters.getSelectedFigureId() &&
22414
22527
  this.env.model.getters.getEditionMode() === "inactive") {
22415
- this.hiddenInput.el.focus();
22528
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
22416
22529
  }
22417
22530
  }
22418
22531
  get gridEl() {
@@ -22616,9 +22729,7 @@ class Grid extends Component {
22616
22729
  this.closeOpenedPopover();
22617
22730
  this.menuState.isOpen = true;
22618
22731
  this.menuState.position = { x, y };
22619
- this.menuState.menuItems = registries$1[type]
22620
- .getAll()
22621
- .filter((item) => !item.isVisible || item.isVisible(this.env));
22732
+ this.menuState.menuItems = registries$1[type].getAll();
22622
22733
  }
22623
22734
  copy(cut, ev) {
22624
22735
  if (!this.gridEl.contains(document.activeElement)) {
@@ -24242,7 +24353,7 @@ function extractStyle(cell, data) {
24242
24353
  if (cell.style) {
24243
24354
  style = data.styles[cell.style];
24244
24355
  }
24245
- const format = cell.format ? data.formats[cell.format] : undefined;
24356
+ const format = extractFormat(cell, data);
24246
24357
  const exportedBorder = {};
24247
24358
  if (cell.border) {
24248
24359
  const border = data.borders[cell.border];
@@ -24276,6 +24387,22 @@ function extractStyle(cell, data) {
24276
24387
  styles.font["italic"] = !!(style === null || style === void 0 ? void 0 : style.italic) || undefined;
24277
24388
  return styles;
24278
24389
  }
24390
+ function extractFormat(cell, data) {
24391
+ if (cell.format) {
24392
+ return data.formats[cell.format];
24393
+ }
24394
+ if (cell.isFormula) {
24395
+ const tokens = tokenize(cell.content || "");
24396
+ const functions = functionRegistry.content;
24397
+ const isExported = tokens
24398
+ .filter((tk) => tk.type === "FUNCTION")
24399
+ .every((tk) => functions[tk.value.toUpperCase()].isExported);
24400
+ if (!isExported) {
24401
+ return cell.computedFormat;
24402
+ }
24403
+ }
24404
+ return undefined;
24405
+ }
24279
24406
  function normalizeStyle(construct, styles) {
24280
24407
  const { id: fontId } = pushElement(styles["font"], construct.fonts);
24281
24408
  const { id: fillId } = pushElement(styles["fill"], construct.fills);
@@ -26356,7 +26483,7 @@ function load(data, verboseImport) {
26356
26483
  }
26357
26484
  }
26358
26485
  }
26359
- data = JSON.parse(JSON.stringify(data));
26486
+ data = deepCopy(data);
26360
26487
  // apply migrations, if needed
26361
26488
  if ("version" in data) {
26362
26489
  if (data.version < CURRENT_VERSION) {
@@ -27498,6 +27625,9 @@ class CellPlugin extends CorePlugin {
27498
27625
  const exportedCellData = sheet.cells[xc];
27499
27626
  exportedCellData.value = cell.evaluated.value;
27500
27627
  exportedCellData.isFormula = cell.isFormula();
27628
+ if (cell.format !== cell.evaluated.format) {
27629
+ exportedCellData.computedFormat = cell.evaluated.format;
27630
+ }
27501
27631
  }
27502
27632
  }
27503
27633
  }
@@ -28941,7 +29071,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
28941
29071
  allowDispatch(cmd) {
28942
29072
  switch (cmd.type) {
28943
29073
  case "HIDE_COLUMNS_ROWS": {
28944
- if (!this.hiddenHeaders[cmd.sheetId]) {
29074
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
28945
29075
  return 27 /* CommandResult.InvalidSheetId */;
28946
29076
  }
28947
29077
  const hiddenGroup = cmd.dimension === "COL"
@@ -28950,10 +29080,25 @@ class HeaderVisibilityPlugin extends CorePlugin {
28950
29080
  const elements = cmd.dimension === "COL"
28951
29081
  ? this.getters.getNumberCols(cmd.sheetId)
28952
29082
  : this.getters.getNumberRows(cmd.sheetId);
28953
- return (hiddenGroup || []).flat().concat(cmd.elements).length < elements
28954
- ? 0 /* CommandResult.Success */
28955
- : 65 /* CommandResult.TooManyHiddenElements */;
29083
+ const hiddenElements = new Set((hiddenGroup || []).flat().concat(cmd.elements));
29084
+ if (hiddenElements.size >= elements) {
29085
+ return 65 /* CommandResult.TooManyHiddenElements */;
29086
+ }
29087
+ else if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
29088
+ return 85 /* CommandResult.InvalidHeaderIndex */;
29089
+ }
29090
+ else {
29091
+ return 0 /* CommandResult.Success */;
29092
+ }
28956
29093
  }
29094
+ case "REMOVE_COLUMNS_ROWS":
29095
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29096
+ return 27 /* CommandResult.InvalidSheetId */;
29097
+ }
29098
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29099
+ return 8 /* CommandResult.NotEnoughElements */;
29100
+ }
29101
+ return 0 /* CommandResult.Success */;
28957
29102
  }
28958
29103
  return 0 /* CommandResult.Success */;
28959
29104
  }
@@ -29000,6 +29145,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29000
29145
  }
29001
29146
  return;
29002
29147
  }
29148
+ canRemoveHeaders(sheetId, dimension, elements) {
29149
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
29150
+ return !includesAll(elements, visibleHeaders);
29151
+ }
29003
29152
  isRowHiddenByUser(sheetId, index) {
29004
29153
  return this.hiddenHeaders[sheetId].ROW[index];
29005
29154
  }
@@ -29044,6 +29193,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29044
29193
  }
29045
29194
  return consecutiveIndexes;
29046
29195
  }
29196
+ getAllVisibleHeaders(sheetId, dimension) {
29197
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
29198
+ }
29047
29199
  import(data) {
29048
29200
  var _a, _b;
29049
29201
  for (let sheet of data.sheets) {
@@ -29091,6 +29243,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29091
29243
  }
29092
29244
  }
29093
29245
  HeaderVisibilityPlugin.getters = [
29246
+ "canRemoveHeaders",
29094
29247
  "getHiddenColsGroups",
29095
29248
  "getHiddenRowsGroups",
29096
29249
  "isRowHiddenByUser",
@@ -29591,13 +29744,27 @@ class SheetPlugin extends CorePlugin {
29591
29744
  return this.orderedSheetIds.length > 1
29592
29745
  ? 0 /* CommandResult.Success */
29593
29746
  : 9 /* CommandResult.NotEnoughSheets */;
29747
+ case "ADD_COLUMNS_ROWS":
29748
+ const elements = cmd.dimension === "COL"
29749
+ ? this.getNumberCols(cmd.sheetId)
29750
+ : this.getNumberRows(cmd.sheetId);
29751
+ if (cmd.base < 0 || cmd.base > elements) {
29752
+ return 85 /* CommandResult.InvalidHeaderIndex */;
29753
+ }
29754
+ else if (cmd.quantity <= 0) {
29755
+ return 86 /* CommandResult.InvalidQuantity */;
29756
+ }
29757
+ return 0 /* CommandResult.Success */;
29594
29758
  case "REMOVE_COLUMNS_ROWS": {
29595
- const length = cmd.dimension === "COL"
29759
+ const elements = cmd.dimension === "COL"
29596
29760
  ? this.getNumberCols(cmd.sheetId)
29597
29761
  : this.getNumberRows(cmd.sheetId);
29598
- return length > cmd.elements.length
29599
- ? 0 /* CommandResult.Success */
29600
- : 8 /* CommandResult.NotEnoughElements */;
29762
+ if (Math.min(...cmd.elements) < 0 || Math.max(...cmd.elements) > elements) {
29763
+ return 85 /* CommandResult.InvalidHeaderIndex */;
29764
+ }
29765
+ else {
29766
+ return 0 /* CommandResult.Success */;
29767
+ }
29601
29768
  }
29602
29769
  case "FREEZE_ROWS": {
29603
29770
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
@@ -30082,7 +30249,7 @@ class SheetPlugin extends CorePlugin {
30082
30249
  duplicateSheet(fromId, toId) {
30083
30250
  const sheet = this.getSheet(fromId);
30084
30251
  const toName = this.getDuplicateSheetName(sheet.name);
30085
- const newSheet = JSON.parse(JSON.stringify(sheet));
30252
+ const newSheet = deepCopy(sheet);
30086
30253
  newSheet.id = toId;
30087
30254
  newSheet.name = toName;
30088
30255
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -30209,10 +30376,10 @@ class SheetPlugin extends CorePlugin {
30209
30376
  }
30210
30377
  }
30211
30378
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
30212
- for (let [index, row] of Object.entries(sheet.rows)) {
30213
- const rowIndex = parseInt(index, 10);
30379
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30380
+ const row = sheet.rows[rowIndex];
30214
30381
  for (let i in row.cells) {
30215
- const colIndex = parseInt(i, 10);
30382
+ const colIndex = Number(i);
30216
30383
  const cellId = row.cells[i];
30217
30384
  if (cellId) {
30218
30385
  if (colIndex === deletedColumn) {
@@ -30239,11 +30406,11 @@ class SheetPlugin extends CorePlugin {
30239
30406
  */
30240
30407
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
30241
30408
  const commands = [];
30242
- for (const [index, row] of Object.entries(sheet.rows)) {
30243
- const rowIndex = parseInt(index, 10);
30409
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30410
+ const row = sheet.rows[rowIndex];
30244
30411
  if (dimension !== "rows" || rowIndex >= addedElement) {
30245
30412
  for (let i in row.cells) {
30246
- const colIndex = parseInt(i, 10);
30413
+ const colIndex = Number(i);
30247
30414
  const cellId = row.cells[i];
30248
30415
  if (cellId) {
30249
30416
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -30273,11 +30440,11 @@ class SheetPlugin extends CorePlugin {
30273
30440
  */
30274
30441
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
30275
30442
  const numberRows = deleteToRow - deleteFromRow + 1;
30276
- for (let [index, row] of Object.entries(sheet.rows)) {
30277
- const rowIndex = parseInt(index, 10);
30443
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30444
+ const row = sheet.rows[rowIndex];
30278
30445
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
30279
30446
  for (let i in row.cells) {
30280
- const colIndex = parseInt(i, 10);
30447
+ const colIndex = Number(i);
30281
30448
  const cellId = row.cells[i];
30282
30449
  if (cellId) {
30283
30450
  this.dispatch("CLEAR_CELL", {
@@ -30290,7 +30457,7 @@ class SheetPlugin extends CorePlugin {
30290
30457
  }
30291
30458
  if (rowIndex > deleteToRow) {
30292
30459
  for (let i in row.cells) {
30293
- const colIndex = parseInt(i, 10);
30460
+ const colIndex = Number(i);
30294
30461
  const cellId = row.cells[i];
30295
30462
  if (cellId) {
30296
30463
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -30308,7 +30475,7 @@ class SheetPlugin extends CorePlugin {
30308
30475
  const rows = [];
30309
30476
  const cellsQueue = sheet.rows.map((row) => row.cells);
30310
30477
  for (let i in sheet.rows) {
30311
- if (parseInt(i, 10) === index) {
30478
+ if (Number(i) === index) {
30312
30479
  continue;
30313
30480
  }
30314
30481
  rows.push({
@@ -32944,13 +33111,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32944
33111
  return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
32945
33112
  }
32946
33113
  intersectZoneWithViewport(sheetId, zone) {
32947
- const colsRange = range(zone.left, zone.right + 1);
32948
- const rowsRange = range(zone.top, zone.bottom + 1);
32949
33114
  return {
32950
- left: this.getters.findVisibleHeader(sheetId, "COL", colsRange),
32951
- right: this.getters.findVisibleHeader(sheetId, "COL", colsRange.reverse()),
32952
- top: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange),
32953
- bottom: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange.reverse()),
33115
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
33116
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
33117
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
33118
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32954
33119
  };
32955
33120
  }
32956
33121
  updateFilter({ col, row, values, sheetId }) {
@@ -33392,13 +33557,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33392
33557
  }
33393
33558
  getNextVisibleCellPosition(sheetId, col, row) {
33394
33559
  return {
33395
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
33396
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
33560
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
33561
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
33397
33562
  };
33398
33563
  }
33399
- findVisibleHeader(sheetId, dimension, indexes) {
33400
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
33401
- !this.isHeaderHidden(sheetId, dimension, index));
33564
+ /**
33565
+ * Find the first visible header in the range [`from` => `to`].
33566
+ *
33567
+ * Both `from` and `to` are inclusive.
33568
+ */
33569
+ findVisibleHeader(sheetId, dimension, from, to) {
33570
+ if (from <= to) {
33571
+ for (let i = from; i <= to; i++) {
33572
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33573
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33574
+ return i;
33575
+ }
33576
+ }
33577
+ }
33578
+ if (from > to) {
33579
+ for (let i = from; i >= to; i--) {
33580
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33581
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33582
+ return i;
33583
+ }
33584
+ }
33585
+ }
33586
+ return undefined;
33402
33587
  }
33403
33588
  findLastVisibleColRowIndex(sheetId, dimension, indexes) {
33404
33589
  let lastIndex;
@@ -33411,7 +33596,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33411
33596
  }
33412
33597
  findFirstVisibleColRowIndex(sheetId, dimension) {
33413
33598
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
33414
- for (let i = 0; i < numberOfHeaders - 1; i++) {
33599
+ for (let i = 0; i < numberOfHeaders; i++) {
33415
33600
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
33416
33601
  return i;
33417
33602
  }
@@ -37538,7 +37723,7 @@ class BottomBar extends Component {
37538
37723
  }
37539
37724
  openContextMenu(x, y, registry, menuId) {
37540
37725
  this.menuState.isOpen = true;
37541
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
37726
+ this.menuState.menuItems = registry.getAll();
37542
37727
  this.menuState.position = { x, y };
37543
37728
  this.menuState.menuId = menuId;
37544
37729
  }
@@ -38299,7 +38484,7 @@ class TopBar extends Component {
38299
38484
  const { left, top, height } = ev.target.getBoundingClientRect();
38300
38485
  this.state.menuState.isOpen = true;
38301
38486
  this.state.menuState.position = { x: left, y: top + height };
38302
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
38487
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
38303
38488
  this.state.menuState.parentMenu = menu;
38304
38489
  this.isSelectingMenu = true;
38305
38490
  this.openedEl = ev.target;
@@ -38348,9 +38533,7 @@ class TopBar extends Component {
38348
38533
  this.style.align = this.style.align || (cell === null || cell === void 0 ? void 0 : cell.defaultAlign);
38349
38534
  this.fillColor = this.style.fillColor || "#ffffff";
38350
38535
  this.textColor = this.style.textColor || "#000000";
38351
- this.menus = topbarMenuRegistry
38352
- .getAll()
38353
- .filter((item) => !item.isVisible || item.isVisible(this.env));
38536
+ this.menus = topbarMenuRegistry.getAll();
38354
38537
  }
38355
38538
  getMenuName(menu) {
38356
38539
  return getMenuName(menu, this.env);
@@ -40416,8 +40599,8 @@ class SelectionStreamProcessor {
40416
40599
  }
40417
40600
  const { left, right, top, bottom } = zone;
40418
40601
  const sheetId = this.getters.getActiveSheetId();
40419
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
40420
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
40602
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
40603
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
40421
40604
  if (refRow === undefined || refCol === undefined) {
40422
40605
  return 17 /* CommandResult.SelectionOutOfBound */;
40423
40606
  }
@@ -40492,10 +40675,10 @@ class SelectionStreamProcessor {
40492
40675
  const { col: anchorCol, row: anchorRow } = anchor.cell;
40493
40676
  return {
40494
40677
  col: this.getters.isColHidden(sheetId, anchorCol)
40495
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
40678
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
40496
40679
  : anchorCol,
40497
40680
  row: this.getters.isRowHidden(sheetId, anchorRow)
40498
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
40681
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
40499
40682
  : anchorRow,
40500
40683
  };
40501
40684
  }
@@ -41030,12 +41213,9 @@ function addFormula(cell) {
41030
41213
  else {
41031
41214
  // Shouldn't we always output the value then ?
41032
41215
  const value = cell.value;
41033
- // what if value = 0? Is this condition correct?
41034
- if (value) {
41035
- const type = getCellType(value);
41036
- attrs.push(["t", type]);
41037
- node = escapeXml /*xml*/ `<v>${value}</v>`;
41038
- }
41216
+ const type = getCellType(value);
41217
+ attrs.push(["t", type]);
41218
+ node = escapeXml /*xml*/ `<v>${value}</v>`;
41039
41219
  return { attrs, node };
41040
41220
  }
41041
41221
  }
@@ -42135,6 +42315,8 @@ class Model extends EventBus {
42135
42315
  * Internal status of the model. Important for command handling coordination
42136
42316
  */
42137
42317
  this.status = 0 /* Status.Ready */;
42318
+ this.handlers = [];
42319
+ this.coreHandlers = [];
42138
42320
  /**
42139
42321
  * The dispatch method is the only entry point to manipulate data in the model.
42140
42322
  * This is through this method that commands are dispatched most of the time
@@ -42207,7 +42389,7 @@ class Model extends EventBus {
42207
42389
  const command = { type, ...payload };
42208
42390
  const previousStatus = this.status;
42209
42391
  this.status = 2 /* Status.RunningCore */;
42210
- const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers;
42392
+ const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
42211
42393
  this.dispatchToHandlers(handlers, command);
42212
42394
  this.status = previousStatus;
42213
42395
  return DispatchResult.Success;
@@ -42241,6 +42423,8 @@ class Model extends EventBus {
42241
42423
  this.uuidGenerator.setIsFastStrategy(true);
42242
42424
  // Initiate stream processor
42243
42425
  this.selection = new SelectionStreamProcessor(this.getters);
42426
+ this.coreHandlers.push(this.range);
42427
+ this.handlers.push(this.range);
42244
42428
  // registering plugins
42245
42429
  for (let Plugin of corePluginRegistry.getAll()) {
42246
42430
  this.setupCorePlugin(Plugin, workbookData);
@@ -42250,6 +42434,7 @@ class Model extends EventBus {
42250
42434
  this.setupUiPlugin(Plugin);
42251
42435
  }
42252
42436
  this.uuidGenerator.setIsFastStrategy(false);
42437
+ this.handlers.push(this.history);
42253
42438
  // starting plugins
42254
42439
  this.dispatch("START");
42255
42440
  // Model should be the last permanent subscriber in the list since he should render
@@ -42270,9 +42455,6 @@ class Model extends EventBus {
42270
42455
  // by owl, since we do not rely on reactivity
42271
42456
  markRaw(this);
42272
42457
  }
42273
- get handlers() {
42274
- return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history];
42275
- }
42276
42458
  joinSession() {
42277
42459
  this.session.join(this.config.client);
42278
42460
  }
@@ -42291,6 +42473,7 @@ class Model extends EventBus {
42291
42473
  this.getters[name] = plugin[name].bind(plugin);
42292
42474
  }
42293
42475
  this.uiPlugins.push(plugin);
42476
+ this.handlers.push(plugin);
42294
42477
  const layers = Plugin.layers.map((l) => [plugin, l]);
42295
42478
  this.renderers.push(...layers);
42296
42479
  this.renderers.sort((p1, p2) => p1[1] - p2[1]);
@@ -42314,6 +42497,8 @@ class Model extends EventBus {
42314
42497
  }
42315
42498
  plugin.import(data);
42316
42499
  this.corePlugins.push(plugin);
42500
+ this.coreHandlers.push(plugin);
42501
+ this.handlers.push(plugin);
42317
42502
  }
42318
42503
  onRemoteRevisionReceived({ commands }) {
42319
42504
  for (let command of commands) {
@@ -42331,7 +42516,7 @@ class Model extends EventBus {
42331
42516
  return;
42332
42517
  }
42333
42518
  this.isReplayingCommand = true;
42334
- this.dispatchToHandlers([this.range, ...this.corePlugins], command);
42519
+ this.dispatchToHandlers(this.coreHandlers, command);
42335
42520
  this.isReplayingCommand = false;
42336
42521
  }), this.config.transportService, revisionId);
42337
42522
  return session;
@@ -42431,7 +42616,7 @@ class Model extends EventBus {
42431
42616
  }
42432
42617
  }
42433
42618
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
42434
- data = JSON.parse(JSON.stringify(data));
42619
+ data = deepCopy(data);
42435
42620
  return data;
42436
42621
  }
42437
42622
  updateMode(mode) {
@@ -42458,7 +42643,7 @@ class Model extends EventBus {
42458
42643
  handler.exportForExcel(data);
42459
42644
  }
42460
42645
  }
42461
- data = JSON.parse(JSON.stringify(data));
42646
+ data = deepCopy(data);
42462
42647
  return getXLSX(data);
42463
42648
  }
42464
42649
  }
@@ -42562,6 +42747,6 @@ function addFunction(functionName, functionDescription) {
42562
42747
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, cellTypes, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
42563
42748
 
42564
42749
 
42565
- __info__.version = '16.0.7';
42566
- __info__.date = '2023-04-14T12:46:24.076Z';
42567
- __info__.hash = '2b4831d';
42750
+ __info__.version = '16.0.9';
42751
+ __info__.date = '2023-04-28T12:25:41.572Z';
42752
+ __info__.hash = 'e06ca83';