@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.
@@ -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", {
@@ -3970,13 +4039,16 @@ class Menu extends Component {
3970
4039
  }
3971
4040
  });
3972
4041
  }
4042
+ get visibleMenuItems() {
4043
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
4044
+ }
3973
4045
  get subMenuPosition() {
3974
4046
  const position = Object.assign({}, this.subMenu.position);
3975
4047
  position.y -= this.subMenu.scrollOffset || 0;
3976
4048
  return position;
3977
4049
  }
3978
4050
  get menuHeight() {
3979
- return this.menuComponentHeight(this.props.menuItems);
4051
+ return this.menuComponentHeight(this.visibleMenuItems);
3980
4052
  }
3981
4053
  get subMenuHeight() {
3982
4054
  return this.menuComponentHeight(this.subMenu.menuItems);
@@ -4012,7 +4084,7 @@ class Menu extends Component {
4012
4084
  * and the menu item at a given index.
4013
4085
  */
4014
4086
  subMenuVerticalPosition(position) {
4015
- const menusAbove = this.props.menuItems.slice(0, position);
4087
+ const menusAbove = this.visibleMenuItems.slice(0, position);
4016
4088
  return this.menuComponentHeight(menusAbove) + this.position.y;
4017
4089
  }
4018
4090
  onExternalClick(ev) {
@@ -5545,6 +5617,11 @@ colMenuRegistry
5545
5617
  name: REMOVE_COLUMNS_NAME,
5546
5618
  sequence: 90,
5547
5619
  action: REMOVE_COLUMNS_ACTION,
5620
+ isVisible: (env) => {
5621
+ const sheetId = env.model.getters.getActiveSheetId();
5622
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5623
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5624
+ },
5548
5625
  })
5549
5626
  .add("clear_column", {
5550
5627
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -5557,9 +5634,8 @@ colMenuRegistry
5557
5634
  action: HIDE_COLUMNS_ACTION,
5558
5635
  isVisible: (env) => {
5559
5636
  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);
5637
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
5638
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
5563
5639
  },
5564
5640
  separator: true,
5565
5641
  })
@@ -5633,6 +5709,11 @@ rowMenuRegistry
5633
5709
  name: REMOVE_ROWS_NAME,
5634
5710
  sequence: 70,
5635
5711
  action: REMOVE_ROWS_ACTION,
5712
+ isVisible: (env) => {
5713
+ const sheetId = env.model.getters.getActiveSheetId();
5714
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5715
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5716
+ },
5636
5717
  })
5637
5718
  .add("clear_row", {
5638
5719
  name: DELETE_CONTENT_ROWS_NAME,
@@ -5645,9 +5726,8 @@ rowMenuRegistry
5645
5726
  action: HIDE_ROWS_ACTION,
5646
5727
  isVisible: (env) => {
5647
5728
  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);
5729
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
5730
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
5651
5731
  },
5652
5732
  separator: true,
5653
5733
  })
@@ -10309,7 +10389,7 @@ class ChartFigure extends Component {
10309
10389
  openContextMenu(position) {
10310
10390
  const registry = this.getMenuItemRegistry();
10311
10391
  this.menuState.isOpen = true;
10312
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
10392
+ this.menuState.menuItems = registry.getAll();
10313
10393
  this.menuState.position = position;
10314
10394
  }
10315
10395
  get chartComponent() {
@@ -19829,14 +19909,31 @@ class ContentEditableHelper {
19829
19909
  * */
19830
19910
  getCurrentSelection() {
19831
19911
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
19832
- let startSizeBefore = this.findSizeBeforeElement(startElement);
19833
- let endSizeBefore = this.findSizeBeforeElement(endElement);
19912
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
19913
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
19834
19914
  return {
19835
- start: startSizeBefore + startSelectionOffset,
19836
- end: endSizeBefore + endSelectionOffset,
19915
+ start: startSizeBefore,
19916
+ end: endSizeBefore,
19837
19917
  };
19838
19918
  }
19839
- findSizeBeforeElement(nodeToFind) {
19919
+ /**
19920
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
19921
+ * The selected node is either a Text node or an Element node.
19922
+ *
19923
+ * case 1 -Text node:
19924
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
19925
+ * content length of all previous nodes.
19926
+ *
19927
+ * case 2 - Element node:
19928
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
19929
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
19930
+ *
19931
+ * See the MDN documentation for more details.
19932
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
19933
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
19934
+ *
19935
+ */
19936
+ findSelectionIndex(nodeToFind, nodeOffset) {
19840
19937
  let it = this.iterateChildren(this.el);
19841
19938
  let usedCharacters = 0;
19842
19939
  let current = it.next();
@@ -19848,6 +19945,19 @@ class ContentEditableHelper {
19848
19945
  }
19849
19946
  current = it.next();
19850
19947
  }
19948
+ if (current.value === nodeToFind) {
19949
+ if (!current.value.hasChildNodes()) {
19950
+ usedCharacters += nodeOffset;
19951
+ }
19952
+ else {
19953
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
19954
+ for (const child of children) {
19955
+ if (!child.textContent)
19956
+ continue;
19957
+ usedCharacters += child.textContent.length;
19958
+ }
19959
+ }
19960
+ }
19851
19961
  return usedCharacters;
19852
19962
  }
19853
19963
  getStartAndEndSelection() {
@@ -22333,8 +22443,8 @@ class Grid extends Component {
22333
22443
  },
22334
22444
  "CTRL+END": () => {
22335
22445
  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());
22446
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
22447
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
22338
22448
  this.env.model.selection.selectCell(col, row);
22339
22449
  },
22340
22450
  "SHIFT+ ": () => {
@@ -22410,9 +22520,10 @@ class Grid extends Component {
22410
22520
  this.focus();
22411
22521
  }
22412
22522
  focus() {
22523
+ var _a;
22413
22524
  if (!this.env.model.getters.getSelectedFigureId() &&
22414
22525
  this.env.model.getters.getEditionMode() === "inactive") {
22415
- this.hiddenInput.el.focus();
22526
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
22416
22527
  }
22417
22528
  }
22418
22529
  get gridEl() {
@@ -22616,9 +22727,7 @@ class Grid extends Component {
22616
22727
  this.closeOpenedPopover();
22617
22728
  this.menuState.isOpen = true;
22618
22729
  this.menuState.position = { x, y };
22619
- this.menuState.menuItems = registries$1[type]
22620
- .getAll()
22621
- .filter((item) => !item.isVisible || item.isVisible(this.env));
22730
+ this.menuState.menuItems = registries$1[type].getAll();
22622
22731
  }
22623
22732
  copy(cut, ev) {
22624
22733
  if (!this.gridEl.contains(document.activeElement)) {
@@ -26356,7 +26465,7 @@ function load(data, verboseImport) {
26356
26465
  }
26357
26466
  }
26358
26467
  }
26359
- data = JSON.parse(JSON.stringify(data));
26468
+ data = deepCopy(data);
26360
26469
  // apply migrations, if needed
26361
26470
  if ("version" in data) {
26362
26471
  if (data.version < CURRENT_VERSION) {
@@ -28941,7 +29050,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
28941
29050
  allowDispatch(cmd) {
28942
29051
  switch (cmd.type) {
28943
29052
  case "HIDE_COLUMNS_ROWS": {
28944
- if (!this.hiddenHeaders[cmd.sheetId]) {
29053
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
28945
29054
  return 27 /* CommandResult.InvalidSheetId */;
28946
29055
  }
28947
29056
  const hiddenGroup = cmd.dimension === "COL"
@@ -28954,6 +29063,14 @@ class HeaderVisibilityPlugin extends CorePlugin {
28954
29063
  ? 0 /* CommandResult.Success */
28955
29064
  : 65 /* CommandResult.TooManyHiddenElements */;
28956
29065
  }
29066
+ case "REMOVE_COLUMNS_ROWS":
29067
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29068
+ return 27 /* CommandResult.InvalidSheetId */;
29069
+ }
29070
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29071
+ return 8 /* CommandResult.NotEnoughElements */;
29072
+ }
29073
+ return 0 /* CommandResult.Success */;
28957
29074
  }
28958
29075
  return 0 /* CommandResult.Success */;
28959
29076
  }
@@ -29000,6 +29117,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29000
29117
  }
29001
29118
  return;
29002
29119
  }
29120
+ canRemoveHeaders(sheetId, dimension, elements) {
29121
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
29122
+ return !includesAll(elements, visibleHeaders);
29123
+ }
29003
29124
  isRowHiddenByUser(sheetId, index) {
29004
29125
  return this.hiddenHeaders[sheetId].ROW[index];
29005
29126
  }
@@ -29044,6 +29165,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29044
29165
  }
29045
29166
  return consecutiveIndexes;
29046
29167
  }
29168
+ getAllVisibleHeaders(sheetId, dimension) {
29169
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
29170
+ }
29047
29171
  import(data) {
29048
29172
  var _a, _b;
29049
29173
  for (let sheet of data.sheets) {
@@ -29091,6 +29215,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29091
29215
  }
29092
29216
  }
29093
29217
  HeaderVisibilityPlugin.getters = [
29218
+ "canRemoveHeaders",
29094
29219
  "getHiddenColsGroups",
29095
29220
  "getHiddenRowsGroups",
29096
29221
  "isRowHiddenByUser",
@@ -29591,14 +29716,6 @@ class SheetPlugin extends CorePlugin {
29591
29716
  return this.orderedSheetIds.length > 1
29592
29717
  ? 0 /* CommandResult.Success */
29593
29718
  : 9 /* CommandResult.NotEnoughSheets */;
29594
- case "REMOVE_COLUMNS_ROWS": {
29595
- const length = cmd.dimension === "COL"
29596
- ? this.getNumberCols(cmd.sheetId)
29597
- : this.getNumberRows(cmd.sheetId);
29598
- return length > cmd.elements.length
29599
- ? 0 /* CommandResult.Success */
29600
- : 8 /* CommandResult.NotEnoughElements */;
29601
- }
29602
29719
  case "FREEZE_ROWS": {
29603
29720
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
29604
29721
  }
@@ -30082,7 +30199,7 @@ class SheetPlugin extends CorePlugin {
30082
30199
  duplicateSheet(fromId, toId) {
30083
30200
  const sheet = this.getSheet(fromId);
30084
30201
  const toName = this.getDuplicateSheetName(sheet.name);
30085
- const newSheet = JSON.parse(JSON.stringify(sheet));
30202
+ const newSheet = deepCopy(sheet);
30086
30203
  newSheet.id = toId;
30087
30204
  newSheet.name = toName;
30088
30205
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -30209,10 +30326,10 @@ class SheetPlugin extends CorePlugin {
30209
30326
  }
30210
30327
  }
30211
30328
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
30212
- for (let [index, row] of Object.entries(sheet.rows)) {
30213
- const rowIndex = parseInt(index, 10);
30329
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30330
+ const row = sheet.rows[rowIndex];
30214
30331
  for (let i in row.cells) {
30215
- const colIndex = parseInt(i, 10);
30332
+ const colIndex = Number(i);
30216
30333
  const cellId = row.cells[i];
30217
30334
  if (cellId) {
30218
30335
  if (colIndex === deletedColumn) {
@@ -30239,11 +30356,11 @@ class SheetPlugin extends CorePlugin {
30239
30356
  */
30240
30357
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
30241
30358
  const commands = [];
30242
- for (const [index, row] of Object.entries(sheet.rows)) {
30243
- const rowIndex = parseInt(index, 10);
30359
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30360
+ const row = sheet.rows[rowIndex];
30244
30361
  if (dimension !== "rows" || rowIndex >= addedElement) {
30245
30362
  for (let i in row.cells) {
30246
- const colIndex = parseInt(i, 10);
30363
+ const colIndex = Number(i);
30247
30364
  const cellId = row.cells[i];
30248
30365
  if (cellId) {
30249
30366
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -30273,11 +30390,11 @@ class SheetPlugin extends CorePlugin {
30273
30390
  */
30274
30391
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
30275
30392
  const numberRows = deleteToRow - deleteFromRow + 1;
30276
- for (let [index, row] of Object.entries(sheet.rows)) {
30277
- const rowIndex = parseInt(index, 10);
30393
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
30394
+ const row = sheet.rows[rowIndex];
30278
30395
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
30279
30396
  for (let i in row.cells) {
30280
- const colIndex = parseInt(i, 10);
30397
+ const colIndex = Number(i);
30281
30398
  const cellId = row.cells[i];
30282
30399
  if (cellId) {
30283
30400
  this.dispatch("CLEAR_CELL", {
@@ -30290,7 +30407,7 @@ class SheetPlugin extends CorePlugin {
30290
30407
  }
30291
30408
  if (rowIndex > deleteToRow) {
30292
30409
  for (let i in row.cells) {
30293
- const colIndex = parseInt(i, 10);
30410
+ const colIndex = Number(i);
30294
30411
  const cellId = row.cells[i];
30295
30412
  if (cellId) {
30296
30413
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -30308,7 +30425,7 @@ class SheetPlugin extends CorePlugin {
30308
30425
  const rows = [];
30309
30426
  const cellsQueue = sheet.rows.map((row) => row.cells);
30310
30427
  for (let i in sheet.rows) {
30311
- if (parseInt(i, 10) === index) {
30428
+ if (Number(i) === index) {
30312
30429
  continue;
30313
30430
  }
30314
30431
  rows.push({
@@ -32944,13 +33061,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32944
33061
  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
33062
  }
32946
33063
  intersectZoneWithViewport(sheetId, zone) {
32947
- const colsRange = range(zone.left, zone.right + 1);
32948
- const rowsRange = range(zone.top, zone.bottom + 1);
32949
33064
  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()),
33065
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
33066
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
33067
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
33068
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32954
33069
  };
32955
33070
  }
32956
33071
  updateFilter({ col, row, values, sheetId }) {
@@ -33392,13 +33507,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33392
33507
  }
33393
33508
  getNextVisibleCellPosition(sheetId, col, row) {
33394
33509
  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))),
33510
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
33511
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
33397
33512
  };
33398
33513
  }
33399
- findVisibleHeader(sheetId, dimension, indexes) {
33400
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
33401
- !this.isHeaderHidden(sheetId, dimension, index));
33514
+ /**
33515
+ * Find the first visible header in the range [`from` => `to`].
33516
+ *
33517
+ * Both `from` and `to` are inclusive.
33518
+ */
33519
+ findVisibleHeader(sheetId, dimension, from, to) {
33520
+ if (from <= to) {
33521
+ for (let i = from; i <= to; i++) {
33522
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33523
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33524
+ return i;
33525
+ }
33526
+ }
33527
+ }
33528
+ if (from > to) {
33529
+ for (let i = from; i >= to; i--) {
33530
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
33531
+ !this.isHeaderHidden(sheetId, dimension, i)) {
33532
+ return i;
33533
+ }
33534
+ }
33535
+ }
33536
+ return undefined;
33402
33537
  }
33403
33538
  findLastVisibleColRowIndex(sheetId, dimension, indexes) {
33404
33539
  let lastIndex;
@@ -33411,7 +33546,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
33411
33546
  }
33412
33547
  findFirstVisibleColRowIndex(sheetId, dimension) {
33413
33548
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
33414
- for (let i = 0; i < numberOfHeaders - 1; i++) {
33549
+ for (let i = 0; i < numberOfHeaders; i++) {
33415
33550
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
33416
33551
  return i;
33417
33552
  }
@@ -37538,7 +37673,7 @@ class BottomBar extends Component {
37538
37673
  }
37539
37674
  openContextMenu(x, y, registry, menuId) {
37540
37675
  this.menuState.isOpen = true;
37541
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
37676
+ this.menuState.menuItems = registry.getAll();
37542
37677
  this.menuState.position = { x, y };
37543
37678
  this.menuState.menuId = menuId;
37544
37679
  }
@@ -38299,7 +38434,7 @@ class TopBar extends Component {
38299
38434
  const { left, top, height } = ev.target.getBoundingClientRect();
38300
38435
  this.state.menuState.isOpen = true;
38301
38436
  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));
38437
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
38303
38438
  this.state.menuState.parentMenu = menu;
38304
38439
  this.isSelectingMenu = true;
38305
38440
  this.openedEl = ev.target;
@@ -38348,9 +38483,7 @@ class TopBar extends Component {
38348
38483
  this.style.align = this.style.align || (cell === null || cell === void 0 ? void 0 : cell.defaultAlign);
38349
38484
  this.fillColor = this.style.fillColor || "#ffffff";
38350
38485
  this.textColor = this.style.textColor || "#000000";
38351
- this.menus = topbarMenuRegistry
38352
- .getAll()
38353
- .filter((item) => !item.isVisible || item.isVisible(this.env));
38486
+ this.menus = topbarMenuRegistry.getAll();
38354
38487
  }
38355
38488
  getMenuName(menu) {
38356
38489
  return getMenuName(menu, this.env);
@@ -40416,8 +40549,8 @@ class SelectionStreamProcessor {
40416
40549
  }
40417
40550
  const { left, right, top, bottom } = zone;
40418
40551
  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));
40552
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
40553
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
40421
40554
  if (refRow === undefined || refCol === undefined) {
40422
40555
  return 17 /* CommandResult.SelectionOutOfBound */;
40423
40556
  }
@@ -40492,10 +40625,10 @@ class SelectionStreamProcessor {
40492
40625
  const { col: anchorCol, row: anchorRow } = anchor.cell;
40493
40626
  return {
40494
40627
  col: this.getters.isColHidden(sheetId, anchorCol)
40495
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
40628
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
40496
40629
  : anchorCol,
40497
40630
  row: this.getters.isRowHidden(sheetId, anchorRow)
40498
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
40631
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
40499
40632
  : anchorRow,
40500
40633
  };
40501
40634
  }
@@ -42135,6 +42268,8 @@ class Model extends EventBus {
42135
42268
  * Internal status of the model. Important for command handling coordination
42136
42269
  */
42137
42270
  this.status = 0 /* Status.Ready */;
42271
+ this.handlers = [];
42272
+ this.coreHandlers = [];
42138
42273
  /**
42139
42274
  * The dispatch method is the only entry point to manipulate data in the model.
42140
42275
  * This is through this method that commands are dispatched most of the time
@@ -42207,7 +42342,7 @@ class Model extends EventBus {
42207
42342
  const command = { type, ...payload };
42208
42343
  const previousStatus = this.status;
42209
42344
  this.status = 2 /* Status.RunningCore */;
42210
- const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers;
42345
+ const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
42211
42346
  this.dispatchToHandlers(handlers, command);
42212
42347
  this.status = previousStatus;
42213
42348
  return DispatchResult.Success;
@@ -42241,6 +42376,8 @@ class Model extends EventBus {
42241
42376
  this.uuidGenerator.setIsFastStrategy(true);
42242
42377
  // Initiate stream processor
42243
42378
  this.selection = new SelectionStreamProcessor(this.getters);
42379
+ this.coreHandlers.push(this.range);
42380
+ this.handlers.push(this.range);
42244
42381
  // registering plugins
42245
42382
  for (let Plugin of corePluginRegistry.getAll()) {
42246
42383
  this.setupCorePlugin(Plugin, workbookData);
@@ -42250,6 +42387,7 @@ class Model extends EventBus {
42250
42387
  this.setupUiPlugin(Plugin);
42251
42388
  }
42252
42389
  this.uuidGenerator.setIsFastStrategy(false);
42390
+ this.handlers.push(this.history);
42253
42391
  // starting plugins
42254
42392
  this.dispatch("START");
42255
42393
  // Model should be the last permanent subscriber in the list since he should render
@@ -42270,9 +42408,6 @@ class Model extends EventBus {
42270
42408
  // by owl, since we do not rely on reactivity
42271
42409
  markRaw(this);
42272
42410
  }
42273
- get handlers() {
42274
- return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history];
42275
- }
42276
42411
  joinSession() {
42277
42412
  this.session.join(this.config.client);
42278
42413
  }
@@ -42291,6 +42426,7 @@ class Model extends EventBus {
42291
42426
  this.getters[name] = plugin[name].bind(plugin);
42292
42427
  }
42293
42428
  this.uiPlugins.push(plugin);
42429
+ this.handlers.push(plugin);
42294
42430
  const layers = Plugin.layers.map((l) => [plugin, l]);
42295
42431
  this.renderers.push(...layers);
42296
42432
  this.renderers.sort((p1, p2) => p1[1] - p2[1]);
@@ -42314,6 +42450,8 @@ class Model extends EventBus {
42314
42450
  }
42315
42451
  plugin.import(data);
42316
42452
  this.corePlugins.push(plugin);
42453
+ this.coreHandlers.push(plugin);
42454
+ this.handlers.push(plugin);
42317
42455
  }
42318
42456
  onRemoteRevisionReceived({ commands }) {
42319
42457
  for (let command of commands) {
@@ -42331,7 +42469,7 @@ class Model extends EventBus {
42331
42469
  return;
42332
42470
  }
42333
42471
  this.isReplayingCommand = true;
42334
- this.dispatchToHandlers([this.range, ...this.corePlugins], command);
42472
+ this.dispatchToHandlers(this.coreHandlers, command);
42335
42473
  this.isReplayingCommand = false;
42336
42474
  }), this.config.transportService, revisionId);
42337
42475
  return session;
@@ -42431,7 +42569,7 @@ class Model extends EventBus {
42431
42569
  }
42432
42570
  }
42433
42571
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
42434
- data = JSON.parse(JSON.stringify(data));
42572
+ data = deepCopy(data);
42435
42573
  return data;
42436
42574
  }
42437
42575
  updateMode(mode) {
@@ -42458,7 +42596,7 @@ class Model extends EventBus {
42458
42596
  handler.exportForExcel(data);
42459
42597
  }
42460
42598
  }
42461
- data = JSON.parse(JSON.stringify(data));
42599
+ data = deepCopy(data);
42462
42600
  return getXLSX(data);
42463
42601
  }
42464
42602
  }
@@ -42562,6 +42700,6 @@ function addFunction(functionName, functionDescription) {
42562
42700
  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
42701
 
42564
42702
 
42565
- __info__.version = '16.0.7';
42566
- __info__.date = '2023-04-14T12:46:24.076Z';
42567
- __info__.hash = '2b4831d';
42703
+ __info__.version = '16.0.8';
42704
+ __info__.date = '2023-04-21T07:29:33.894Z';
42705
+ __info__.hash = 'dbd7aa4';