@odoo/o-spreadsheet 16.0.7 → 16.0.8

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.
@@ -1027,6 +1027,10 @@ function deepEquals(o1, o2) {
1027
1027
  }
1028
1028
  return true;
1029
1029
  }
1030
+ /** Check if the given array contains all the values of the other array. */
1031
+ function includesAll(arr, values) {
1032
+ return values.every((value) => arr.includes(value));
1033
+ }
1030
1034
  /**
1031
1035
  * Return an object with all the keys in the object that have a falsy value removed.
1032
1036
  */
@@ -1572,12 +1576,77 @@ const numberRepresentation = [];
1572
1576
  * - all digit stored in the decimal part of the number
1573
1577
  *
1574
1578
  * The 'maxDecimal' parameter allows to indicate the number of digits to not
1575
- * exceed in the decimal part, in which case digits are rounded
1579
+ * exceed in the decimal part, in which case digits are rounded.
1576
1580
  *
1577
- * Intl.Numberformat is used to properly handle all the roundings.
1578
- * e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
1579
1581
  **/
1580
1582
  function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
1583
+ const asString = value.toString();
1584
+ if (asString.includes("e"))
1585
+ return splitNumberIntl(value, maxDecimals);
1586
+ if (Number.isInteger(value)) {
1587
+ return { integerDigits: asString, decimalDigits: undefined };
1588
+ }
1589
+ const indexOfDot = asString.indexOf(".");
1590
+ let integerDigits = asString.substring(0, indexOfDot);
1591
+ let decimalDigits = asString.substring(indexOfDot + 1);
1592
+ if (maxDecimals === 0) {
1593
+ if (Number(decimalDigits[0]) >= 5) {
1594
+ integerDigits = (Number(integerDigits) + 1).toString();
1595
+ }
1596
+ return { integerDigits, decimalDigits: undefined };
1597
+ }
1598
+ if (decimalDigits.length > maxDecimals) {
1599
+ const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
1600
+ decimalDigits = roundedDecimalDigits;
1601
+ if (roundedIntegerDigits !== "0") {
1602
+ integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
1603
+ }
1604
+ }
1605
+ return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
1606
+ }
1607
+ /**
1608
+ * Return the given string minus the trailing "0" characters.
1609
+ *
1610
+ * @param numberString : a string of integers
1611
+ * @returns the numberString, minus the eventual zeroes at the end
1612
+ */
1613
+ function removeTrailingZeroes(numberString) {
1614
+ let i = numberString.length - 1;
1615
+ while (i >= 0 && numberString[i] === "0") {
1616
+ i--;
1617
+ }
1618
+ return numberString.slice(0, i + 1) || undefined;
1619
+ }
1620
+ /**
1621
+ * Limit the size of the decimal part of a number to the given number of digits.
1622
+ */
1623
+ function limitDecimalDigits(decimalDigits, maxDecimals) {
1624
+ let integerDigits = "0";
1625
+ let resultDecimalDigits = decimalDigits;
1626
+ // Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
1627
+ // but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
1628
+ let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
1629
+ const i = maxDecimals;
1630
+ if (Number(Number(decimalDigits[i]) < 5)) {
1631
+ return { integerDigits, decimalDigits: slicedDecimalDigits };
1632
+ }
1633
+ // round up
1634
+ const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
1635
+ if (slicedRoundedUp.length > slicedDecimalDigits.length) {
1636
+ integerDigits = (Number(integerDigits) + 1).toString();
1637
+ resultDecimalDigits = undefined;
1638
+ }
1639
+ else {
1640
+ resultDecimalDigits = slicedRoundedUp;
1641
+ }
1642
+ return { integerDigits, decimalDigits: resultDecimalDigits };
1643
+ }
1644
+ /**
1645
+ * Split numbers into decimal/integer digits using Intl.NumberFormat.
1646
+ * Supports numbers with a lot of digits that are transformed to scientific notation by
1647
+ * number.toString(), but is slow.
1648
+ */
1649
+ function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
1581
1650
  let formatter = numberRepresentation[maxDecimals];
1582
1651
  if (!formatter) {
1583
1652
  formatter = new Intl.NumberFormat("en-US", {
@@ -3974,13 +4043,16 @@ class Menu extends owl.Component {
3974
4043
  }
3975
4044
  });
3976
4045
  }
4046
+ get visibleMenuItems() {
4047
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
4048
+ }
3977
4049
  get subMenuPosition() {
3978
4050
  const position = Object.assign({}, this.subMenu.position);
3979
4051
  position.y -= this.subMenu.scrollOffset || 0;
3980
4052
  return position;
3981
4053
  }
3982
4054
  get menuHeight() {
3983
- return this.menuComponentHeight(this.props.menuItems);
4055
+ return this.menuComponentHeight(this.visibleMenuItems);
3984
4056
  }
3985
4057
  get subMenuHeight() {
3986
4058
  return this.menuComponentHeight(this.subMenu.menuItems);
@@ -4016,7 +4088,7 @@ class Menu extends owl.Component {
4016
4088
  * and the menu item at a given index.
4017
4089
  */
4018
4090
  subMenuVerticalPosition(position) {
4019
- const menusAbove = this.props.menuItems.slice(0, position);
4091
+ const menusAbove = this.visibleMenuItems.slice(0, position);
4020
4092
  return this.menuComponentHeight(menusAbove) + this.position.y;
4021
4093
  }
4022
4094
  onExternalClick(ev) {
@@ -5549,6 +5621,11 @@ colMenuRegistry
5549
5621
  name: REMOVE_COLUMNS_NAME,
5550
5622
  sequence: 90,
5551
5623
  action: REMOVE_COLUMNS_ACTION,
5624
+ isVisible: (env) => {
5625
+ const sheetId = env.model.getters.getActiveSheetId();
5626
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5627
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5628
+ },
5552
5629
  })
5553
5630
  .add("clear_column", {
5554
5631
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -5561,9 +5638,8 @@ colMenuRegistry
5561
5638
  action: HIDE_COLUMNS_ACTION,
5562
5639
  isVisible: (env) => {
5563
5640
  const sheetId = env.model.getters.getActiveSheetId();
5564
- const hiddenCols = env.model.getters.getHiddenColsGroups(sheetId).flat();
5565
- return (env.model.getters.getNumberCols(sheetId) >
5566
- hiddenCols.length + env.model.getters.getElementsFromSelection("COL").length);
5641
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5642
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5567
5643
  },
5568
5644
  separator: true,
5569
5645
  })
@@ -5637,6 +5713,11 @@ rowMenuRegistry
5637
5713
  name: REMOVE_ROWS_NAME,
5638
5714
  sequence: 70,
5639
5715
  action: REMOVE_ROWS_ACTION,
5716
+ isVisible: (env) => {
5717
+ const sheetId = env.model.getters.getActiveSheetId();
5718
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5719
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5720
+ },
5640
5721
  })
5641
5722
  .add("clear_row", {
5642
5723
  name: DELETE_CONTENT_ROWS_NAME,
@@ -5649,9 +5730,8 @@ rowMenuRegistry
5649
5730
  action: HIDE_ROWS_ACTION,
5650
5731
  isVisible: (env) => {
5651
5732
  const sheetId = env.model.getters.getActiveSheetId();
5652
- const hiddenRows = env.model.getters.getHiddenRowsGroups(sheetId).flat();
5653
- return (env.model.getters.getNumberRows(sheetId) >
5654
- hiddenRows.length + env.model.getters.getElementsFromSelection("ROW").length);
5733
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5734
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5655
5735
  },
5656
5736
  separator: true,
5657
5737
  })
@@ -10313,7 +10393,7 @@ class ChartFigure extends owl.Component {
10313
10393
  openContextMenu(position) {
10314
10394
  const registry = this.getMenuItemRegistry();
10315
10395
  this.menuState.isOpen = true;
10316
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
10396
+ this.menuState.menuItems = registry.getAll();
10317
10397
  this.menuState.position = position;
10318
10398
  }
10319
10399
  get chartComponent() {
@@ -19833,14 +19913,31 @@ class ContentEditableHelper {
19833
19913
  * */
19834
19914
  getCurrentSelection() {
19835
19915
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
19836
- let startSizeBefore = this.findSizeBeforeElement(startElement);
19837
- let endSizeBefore = this.findSizeBeforeElement(endElement);
19916
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
19917
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
19838
19918
  return {
19839
- start: startSizeBefore + startSelectionOffset,
19840
- end: endSizeBefore + endSelectionOffset,
19919
+ start: startSizeBefore,
19920
+ end: endSizeBefore,
19841
19921
  };
19842
19922
  }
19843
- findSizeBeforeElement(nodeToFind) {
19923
+ /**
19924
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
19925
+ * The selected node is either a Text node or an Element node.
19926
+ *
19927
+ * case 1 -Text node:
19928
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
19929
+ * content length of all previous nodes.
19930
+ *
19931
+ * case 2 - Element node:
19932
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
19933
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
19934
+ *
19935
+ * See the MDN documentation for more details.
19936
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
19937
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
19938
+ *
19939
+ */
19940
+ findSelectionIndex(nodeToFind, nodeOffset) {
19844
19941
  let it = this.iterateChildren(this.el);
19845
19942
  let usedCharacters = 0;
19846
19943
  let current = it.next();
@@ -19852,6 +19949,19 @@ class ContentEditableHelper {
19852
19949
  }
19853
19950
  current = it.next();
19854
19951
  }
19952
+ if (current.value === nodeToFind) {
19953
+ if (!current.value.hasChildNodes()) {
19954
+ usedCharacters += nodeOffset;
19955
+ }
19956
+ else {
19957
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
19958
+ for (const child of children) {
19959
+ if (!child.textContent)
19960
+ continue;
19961
+ usedCharacters += child.textContent.length;
19962
+ }
19963
+ }
19964
+ }
19855
19965
  return usedCharacters;
19856
19966
  }
19857
19967
  getStartAndEndSelection() {
@@ -22337,8 +22447,8 @@ class Grid extends owl.Component {
22337
22447
  },
22338
22448
  "CTRL+END": () => {
22339
22449
  const sheetId = this.env.model.getters.getActiveSheetId();
22340
- const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", range(0, this.env.model.getters.getNumberCols(sheetId)).reverse());
22341
- const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", range(0, this.env.model.getters.getNumberRows(sheetId)).reverse());
22450
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
22451
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
22342
22452
  this.env.model.selection.selectCell(col, row);
22343
22453
  },
22344
22454
  "SHIFT+ ": () => {
@@ -22414,9 +22524,10 @@ class Grid extends owl.Component {
22414
22524
  this.focus();
22415
22525
  }
22416
22526
  focus() {
22527
+ var _a;
22417
22528
  if (!this.env.model.getters.getSelectedFigureId() &&
22418
22529
  this.env.model.getters.getEditionMode() === "inactive") {
22419
- this.hiddenInput.el.focus();
22530
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
22420
22531
  }
22421
22532
  }
22422
22533
  get gridEl() {
@@ -22620,9 +22731,7 @@ class Grid extends owl.Component {
22620
22731
  this.closeOpenedPopover();
22621
22732
  this.menuState.isOpen = true;
22622
22733
  this.menuState.position = { x, y };
22623
- this.menuState.menuItems = registries$1[type]
22624
- .getAll()
22625
- .filter((item) => !item.isVisible || item.isVisible(this.env));
22734
+ this.menuState.menuItems = registries$1[type].getAll();
22626
22735
  }
22627
22736
  copy(cut, ev) {
22628
22737
  if (!this.gridEl.contains(document.activeElement)) {
@@ -26360,7 +26469,7 @@ function load(data, verboseImport) {
26360
26469
  }
26361
26470
  }
26362
26471
  }
26363
- data = JSON.parse(JSON.stringify(data));
26472
+ data = deepCopy(data);
26364
26473
  // apply migrations, if needed
26365
26474
  if ("version" in data) {
26366
26475
  if (data.version < CURRENT_VERSION) {
@@ -28945,7 +29054,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
28945
29054
  allowDispatch(cmd) {
28946
29055
  switch (cmd.type) {
28947
29056
  case "HIDE_COLUMNS_ROWS": {
28948
- if (!this.hiddenHeaders[cmd.sheetId]) {
29057
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
28949
29058
  return 27 /* CommandResult.InvalidSheetId */;
28950
29059
  }
28951
29060
  const hiddenGroup = cmd.dimension === "COL"
@@ -28958,6 +29067,14 @@ class HeaderVisibilityPlugin extends CorePlugin {
28958
29067
  ? 0 /* CommandResult.Success */
28959
29068
  : 65 /* CommandResult.TooManyHiddenElements */;
28960
29069
  }
29070
+ case "REMOVE_COLUMNS_ROWS":
29071
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29072
+ return 27 /* CommandResult.InvalidSheetId */;
29073
+ }
29074
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29075
+ return 8 /* CommandResult.NotEnoughElements */;
29076
+ }
29077
+ return 0 /* CommandResult.Success */;
28961
29078
  }
28962
29079
  return 0 /* CommandResult.Success */;
28963
29080
  }
@@ -29004,6 +29121,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29004
29121
  }
29005
29122
  return;
29006
29123
  }
29124
+ canRemoveHeaders(sheetId, dimension, elements) {
29125
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
29126
+ return !includesAll(elements, visibleHeaders);
29127
+ }
29007
29128
  isRowHiddenByUser(sheetId, index) {
29008
29129
  return this.hiddenHeaders[sheetId].ROW[index];
29009
29130
  }
@@ -29048,6 +29169,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29048
29169
  }
29049
29170
  return consecutiveIndexes;
29050
29171
  }
29172
+ getAllVisibleHeaders(sheetId, dimension) {
29173
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
29174
+ }
29051
29175
  import(data) {
29052
29176
  var _a, _b;
29053
29177
  for (let sheet of data.sheets) {
@@ -29095,6 +29219,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29095
29219
  }
29096
29220
  }
29097
29221
  HeaderVisibilityPlugin.getters = [
29222
+ "canRemoveHeaders",
29098
29223
  "getHiddenColsGroups",
29099
29224
  "getHiddenRowsGroups",
29100
29225
  "isRowHiddenByUser",
@@ -29595,14 +29720,6 @@ class SheetPlugin extends CorePlugin {
29595
29720
  return this.orderedSheetIds.length > 1
29596
29721
  ? 0 /* CommandResult.Success */
29597
29722
  : 9 /* CommandResult.NotEnoughSheets */;
29598
- case "REMOVE_COLUMNS_ROWS": {
29599
- const length = cmd.dimension === "COL"
29600
- ? this.getNumberCols(cmd.sheetId)
29601
- : this.getNumberRows(cmd.sheetId);
29602
- return length > cmd.elements.length
29603
- ? 0 /* CommandResult.Success */
29604
- : 8 /* CommandResult.NotEnoughElements */;
29605
- }
29606
29723
  case "FREEZE_ROWS": {
29607
29724
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
29608
29725
  }
@@ -30086,7 +30203,7 @@ class SheetPlugin extends CorePlugin {
30086
30203
  duplicateSheet(fromId, toId) {
30087
30204
  const sheet = this.getSheet(fromId);
30088
30205
  const toName = this.getDuplicateSheetName(sheet.name);
30089
- const newSheet = JSON.parse(JSON.stringify(sheet));
30206
+ const newSheet = deepCopy(sheet);
30090
30207
  newSheet.id = toId;
30091
30208
  newSheet.name = toName;
30092
30209
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -30213,10 +30330,10 @@ class SheetPlugin extends CorePlugin {
30213
30330
  }
30214
30331
  }
30215
30332
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
30216
- for (let [index, row] of Object.entries(sheet.rows)) {
30217
- const rowIndex = parseInt(index, 10);
30333
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30334
+ const row = sheet.rows[rowIndex];
30218
30335
  for (let i in row.cells) {
30219
- const colIndex = parseInt(i, 10);
30336
+ const colIndex = Number(i);
30220
30337
  const cellId = row.cells[i];
30221
30338
  if (cellId) {
30222
30339
  if (colIndex === deletedColumn) {
@@ -30243,11 +30360,11 @@ class SheetPlugin extends CorePlugin {
30243
30360
  */
30244
30361
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
30245
30362
  const commands = [];
30246
- for (const [index, row] of Object.entries(sheet.rows)) {
30247
- const rowIndex = parseInt(index, 10);
30363
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30364
+ const row = sheet.rows[rowIndex];
30248
30365
  if (dimension !== "rows" || rowIndex >= addedElement) {
30249
30366
  for (let i in row.cells) {
30250
- const colIndex = parseInt(i, 10);
30367
+ const colIndex = Number(i);
30251
30368
  const cellId = row.cells[i];
30252
30369
  if (cellId) {
30253
30370
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -30277,11 +30394,11 @@ class SheetPlugin extends CorePlugin {
30277
30394
  */
30278
30395
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
30279
30396
  const numberRows = deleteToRow - deleteFromRow + 1;
30280
- for (let [index, row] of Object.entries(sheet.rows)) {
30281
- const rowIndex = parseInt(index, 10);
30397
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30398
+ const row = sheet.rows[rowIndex];
30282
30399
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
30283
30400
  for (let i in row.cells) {
30284
- const colIndex = parseInt(i, 10);
30401
+ const colIndex = Number(i);
30285
30402
  const cellId = row.cells[i];
30286
30403
  if (cellId) {
30287
30404
  this.dispatch("CLEAR_CELL", {
@@ -30294,7 +30411,7 @@ class SheetPlugin extends CorePlugin {
30294
30411
  }
30295
30412
  if (rowIndex > deleteToRow) {
30296
30413
  for (let i in row.cells) {
30297
- const colIndex = parseInt(i, 10);
30414
+ const colIndex = Number(i);
30298
30415
  const cellId = row.cells[i];
30299
30416
  if (cellId) {
30300
30417
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -30312,7 +30429,7 @@ class SheetPlugin extends CorePlugin {
30312
30429
  const rows = [];
30313
30430
  const cellsQueue = sheet.rows.map((row) => row.cells);
30314
30431
  for (let i in sheet.rows) {
30315
- if (parseInt(i, 10) === index) {
30432
+ if (Number(i) === index) {
30316
30433
  continue;
30317
30434
  }
30318
30435
  rows.push({
@@ -32948,13 +33065,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32948
33065
  return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
32949
33066
  }
32950
33067
  intersectZoneWithViewport(sheetId, zone) {
32951
- const colsRange = range(zone.left, zone.right + 1);
32952
- const rowsRange = range(zone.top, zone.bottom + 1);
32953
33068
  return {
32954
- left: this.getters.findVisibleHeader(sheetId, "COL", colsRange),
32955
- right: this.getters.findVisibleHeader(sheetId, "COL", colsRange.reverse()),
32956
- top: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange),
32957
- bottom: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange.reverse()),
33069
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
33070
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
33071
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
33072
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32958
33073
  };
32959
33074
  }
32960
33075
  updateFilter({ col, row, values, sheetId }) {
@@ -33396,13 +33511,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33396
33511
  }
33397
33512
  getNextVisibleCellPosition(sheetId, col, row) {
33398
33513
  return {
33399
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
33400
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
33514
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
33515
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
33401
33516
  };
33402
33517
  }
33403
- findVisibleHeader(sheetId, dimension, indexes) {
33404
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
33405
- !this.isHeaderHidden(sheetId, dimension, index));
33518
+ /**
33519
+ * Find the first visible header in the range [`from` => `to`].
33520
+ *
33521
+ * Both `from` and `to` are inclusive.
33522
+ */
33523
+ findVisibleHeader(sheetId, dimension, from, to) {
33524
+ if (from <= to) {
33525
+ for (let i = from; i <= to; i++) {
33526
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33527
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33528
+ return i;
33529
+ }
33530
+ }
33531
+ }
33532
+ if (from > to) {
33533
+ for (let i = from; i >= to; i--) {
33534
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33535
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33536
+ return i;
33537
+ }
33538
+ }
33539
+ }
33540
+ return undefined;
33406
33541
  }
33407
33542
  findLastVisibleColRowIndex(sheetId, dimension, indexes) {
33408
33543
  let lastIndex;
@@ -33415,7 +33550,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33415
33550
  }
33416
33551
  findFirstVisibleColRowIndex(sheetId, dimension) {
33417
33552
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
33418
- for (let i = 0; i < numberOfHeaders - 1; i++) {
33553
+ for (let i = 0; i < numberOfHeaders; i++) {
33419
33554
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
33420
33555
  return i;
33421
33556
  }
@@ -37542,7 +37677,7 @@ class BottomBar extends owl.Component {
37542
37677
  }
37543
37678
  openContextMenu(x, y, registry, menuId) {
37544
37679
  this.menuState.isOpen = true;
37545
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
37680
+ this.menuState.menuItems = registry.getAll();
37546
37681
  this.menuState.position = { x, y };
37547
37682
  this.menuState.menuId = menuId;
37548
37683
  }
@@ -38303,7 +38438,7 @@ class TopBar extends owl.Component {
38303
38438
  const { left, top, height } = ev.target.getBoundingClientRect();
38304
38439
  this.state.menuState.isOpen = true;
38305
38440
  this.state.menuState.position = { x: left, y: top + height };
38306
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
38441
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
38307
38442
  this.state.menuState.parentMenu = menu;
38308
38443
  this.isSelectingMenu = true;
38309
38444
  this.openedEl = ev.target;
@@ -38352,9 +38487,7 @@ class TopBar extends owl.Component {
38352
38487
  this.style.align = this.style.align || (cell === null || cell === void 0 ? void 0 : cell.defaultAlign);
38353
38488
  this.fillColor = this.style.fillColor || "#ffffff";
38354
38489
  this.textColor = this.style.textColor || "#000000";
38355
- this.menus = topbarMenuRegistry
38356
- .getAll()
38357
- .filter((item) => !item.isVisible || item.isVisible(this.env));
38490
+ this.menus = topbarMenuRegistry.getAll();
38358
38491
  }
38359
38492
  getMenuName(menu) {
38360
38493
  return getMenuName(menu, this.env);
@@ -40420,8 +40553,8 @@ class SelectionStreamProcessor {
40420
40553
  }
40421
40554
  const { left, right, top, bottom } = zone;
40422
40555
  const sheetId = this.getters.getActiveSheetId();
40423
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
40424
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
40556
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
40557
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
40425
40558
  if (refRow === undefined || refCol === undefined) {
40426
40559
  return 17 /* CommandResult.SelectionOutOfBound */;
40427
40560
  }
@@ -40496,10 +40629,10 @@ class SelectionStreamProcessor {
40496
40629
  const { col: anchorCol, row: anchorRow } = anchor.cell;
40497
40630
  return {
40498
40631
  col: this.getters.isColHidden(sheetId, anchorCol)
40499
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
40632
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
40500
40633
  : anchorCol,
40501
40634
  row: this.getters.isRowHidden(sheetId, anchorRow)
40502
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
40635
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
40503
40636
  : anchorRow,
40504
40637
  };
40505
40638
  }
@@ -42139,6 +42272,8 @@ class Model extends EventBus {
42139
42272
  * Internal status of the model. Important for command handling coordination
42140
42273
  */
42141
42274
  this.status = 0 /* Status.Ready */;
42275
+ this.handlers = [];
42276
+ this.coreHandlers = [];
42142
42277
  /**
42143
42278
  * The dispatch method is the only entry point to manipulate data in the model.
42144
42279
  * This is through this method that commands are dispatched most of the time
@@ -42211,7 +42346,7 @@ class Model extends EventBus {
42211
42346
  const command = { type, ...payload };
42212
42347
  const previousStatus = this.status;
42213
42348
  this.status = 2 /* Status.RunningCore */;
42214
- const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers;
42349
+ const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
42215
42350
  this.dispatchToHandlers(handlers, command);
42216
42351
  this.status = previousStatus;
42217
42352
  return DispatchResult.Success;
@@ -42245,6 +42380,8 @@ class Model extends EventBus {
42245
42380
  this.uuidGenerator.setIsFastStrategy(true);
42246
42381
  // Initiate stream processor
42247
42382
  this.selection = new SelectionStreamProcessor(this.getters);
42383
+ this.coreHandlers.push(this.range);
42384
+ this.handlers.push(this.range);
42248
42385
  // registering plugins
42249
42386
  for (let Plugin of corePluginRegistry.getAll()) {
42250
42387
  this.setupCorePlugin(Plugin, workbookData);
@@ -42254,6 +42391,7 @@ class Model extends EventBus {
42254
42391
  this.setupUiPlugin(Plugin);
42255
42392
  }
42256
42393
  this.uuidGenerator.setIsFastStrategy(false);
42394
+ this.handlers.push(this.history);
42257
42395
  // starting plugins
42258
42396
  this.dispatch("START");
42259
42397
  // Model should be the last permanent subscriber in the list since he should render
@@ -42274,9 +42412,6 @@ class Model extends EventBus {
42274
42412
  // by owl, since we do not rely on reactivity
42275
42413
  owl.markRaw(this);
42276
42414
  }
42277
- get handlers() {
42278
- return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history];
42279
- }
42280
42415
  joinSession() {
42281
42416
  this.session.join(this.config.client);
42282
42417
  }
@@ -42295,6 +42430,7 @@ class Model extends EventBus {
42295
42430
  this.getters[name] = plugin[name].bind(plugin);
42296
42431
  }
42297
42432
  this.uiPlugins.push(plugin);
42433
+ this.handlers.push(plugin);
42298
42434
  const layers = Plugin.layers.map((l) => [plugin, l]);
42299
42435
  this.renderers.push(...layers);
42300
42436
  this.renderers.sort((p1, p2) => p1[1] - p2[1]);
@@ -42318,6 +42454,8 @@ class Model extends EventBus {
42318
42454
  }
42319
42455
  plugin.import(data);
42320
42456
  this.corePlugins.push(plugin);
42457
+ this.coreHandlers.push(plugin);
42458
+ this.handlers.push(plugin);
42321
42459
  }
42322
42460
  onRemoteRevisionReceived({ commands }) {
42323
42461
  for (let command of commands) {
@@ -42335,7 +42473,7 @@ class Model extends EventBus {
42335
42473
  return;
42336
42474
  }
42337
42475
  this.isReplayingCommand = true;
42338
- this.dispatchToHandlers([this.range, ...this.corePlugins], command);
42476
+ this.dispatchToHandlers(this.coreHandlers, command);
42339
42477
  this.isReplayingCommand = false;
42340
42478
  }), this.config.transportService, revisionId);
42341
42479
  return session;
@@ -42435,7 +42573,7 @@ class Model extends EventBus {
42435
42573
  }
42436
42574
  }
42437
42575
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
42438
- data = JSON.parse(JSON.stringify(data));
42576
+ data = deepCopy(data);
42439
42577
  return data;
42440
42578
  }
42441
42579
  updateMode(mode) {
@@ -42462,7 +42600,7 @@ class Model extends EventBus {
42462
42600
  handler.exportForExcel(data);
42463
42601
  }
42464
42602
  }
42465
- data = JSON.parse(JSON.stringify(data));
42603
+ data = deepCopy(data);
42466
42604
  return getXLSX(data);
42467
42605
  }
42468
42606
  }
@@ -42594,6 +42732,6 @@ exports.setTranslationMethod = setTranslationMethod;
42594
42732
  exports.tokenize = tokenize;
42595
42733
 
42596
42734
 
42597
- __info__.version = '16.0.7';
42598
- __info__.date = '2023-04-14T12:46:24.076Z';
42599
- __info__.hash = '2b4831d';
42735
+ __info__.version = '16.0.8';
42736
+ __info__.date = '2023-04-21T07:29:33.894Z';
42737
+ __info__.hash = 'dbd7aa4';