@odoo/o-spreadsheet 16.0.6 → 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
  }
@@ -35985,48 +36120,66 @@ class InternalViewport {
35985
36120
  this.adjustPositionY(row);
35986
36121
  }
35987
36122
  }
35988
- adjustPositionX(col) {
36123
+ adjustPositionX(targetCol) {
35989
36124
  const sheetId = this.sheetId;
35990
- const { start, end } = this.getters.getColDimensions(sheetId, col);
35991
- while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
35992
- this.offsetX + this.offsetCorrectionX < start) {
35993
- this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
35994
- this.offsetScrollbarX = this.offsetX;
35995
- this.adjustViewportZoneX();
35996
- }
35997
- while (col < this.left) {
35998
- let leftCol;
35999
- for (leftCol = this.left; leftCol >= 0; leftCol--) {
36000
- if (!this.getters.isColHidden(sheetId, leftCol)) {
36125
+ const { end } = this.getters.getColDimensions(sheetId, targetCol);
36126
+ const maxCol = this.getters.getNumberCols(sheetId);
36127
+ if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
36128
+ for (let col = this.left; this.offsetX + this.offsetCorrectionX + this.viewportWidth < end; col++) {
36129
+ if (col > maxCol) {
36130
+ break;
36131
+ }
36132
+ if (this.getters.isColHidden(sheetId, col)) {
36133
+ continue;
36134
+ }
36135
+ this.offsetX = this.getters.getColDimensions(sheetId, col).end - this.offsetCorrectionX;
36136
+ this.offsetScrollbarX = this.offsetX;
36137
+ this.adjustViewportZoneX();
36138
+ }
36139
+ }
36140
+ else if (this.left > targetCol) {
36141
+ for (let col = this.left; col >= targetCol; col--) {
36142
+ if (col < 0) {
36001
36143
  break;
36002
36144
  }
36145
+ if (this.getters.isColHidden(sheetId, col)) {
36146
+ continue;
36147
+ }
36148
+ this.offsetX = this.getters.getColDimensions(sheetId, col).start - this.offsetCorrectionX;
36149
+ this.offsetScrollbarX = this.offsetX;
36150
+ this.adjustViewportZoneX();
36003
36151
  }
36004
- this.offsetX =
36005
- this.getters.getColDimensions(sheetId, leftCol - 1).start - this.offsetCorrectionX;
36006
- this.offsetScrollbarX = this.offsetX;
36007
- this.adjustViewportZoneX();
36008
36152
  }
36009
36153
  }
36010
- adjustPositionY(row) {
36154
+ adjustPositionY(targetRow) {
36011
36155
  const sheetId = this.sheetId;
36012
- while (this.getters.getRowDimensions(sheetId, row).end >
36013
- this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
36014
- this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
36015
- this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
36016
- this.offsetScrollbarY = this.offsetY;
36017
- this.adjustViewportZoneY();
36018
- }
36019
- while (row < this.top) {
36020
- let topRow;
36021
- for (topRow = this.top; topRow >= 0; topRow--) {
36022
- if (!this.getters.isRowHidden(sheetId, topRow)) {
36156
+ const { end } = this.getters.getRowDimensions(sheetId, targetRow);
36157
+ const maxRow = this.getters.getNumberRows(sheetId);
36158
+ if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
36159
+ for (let row = this.top; this.offsetY + this.viewportHeight + this.offsetCorrectionY < end; row++) {
36160
+ if (row > maxRow) {
36161
+ break;
36162
+ }
36163
+ if (this.getters.isRowHidden(sheetId, row)) {
36164
+ continue;
36165
+ }
36166
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).end - this.offsetCorrectionY;
36167
+ this.offsetScrollbarY = this.offsetY;
36168
+ this.adjustViewportZoneY();
36169
+ }
36170
+ }
36171
+ else if (this.top > targetRow) {
36172
+ for (let row = this.top; row >= targetRow; row--) {
36173
+ if (row < 0) {
36023
36174
  break;
36024
36175
  }
36176
+ if (this.getters.isRowHidden(sheetId, row)) {
36177
+ continue;
36178
+ }
36179
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).start - this.offsetCorrectionY;
36180
+ this.offsetScrollbarY = this.offsetY;
36181
+ this.adjustViewportZoneY();
36025
36182
  }
36026
- this.offsetY =
36027
- this.getters.getRowDimensions(sheetId, topRow - 1).start - this.offsetCorrectionY;
36028
- this.offsetScrollbarY = this.offsetY;
36029
- this.adjustViewportZoneY();
36030
36183
  }
36031
36184
  }
36032
36185
  setViewportOffset(offsetX, offsetY) {
@@ -37520,7 +37673,7 @@ class BottomBar extends Component {
37520
37673
  }
37521
37674
  openContextMenu(x, y, registry, menuId) {
37522
37675
  this.menuState.isOpen = true;
37523
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
37676
+ this.menuState.menuItems = registry.getAll();
37524
37677
  this.menuState.position = { x, y };
37525
37678
  this.menuState.menuId = menuId;
37526
37679
  }
@@ -38281,7 +38434,7 @@ class TopBar extends Component {
38281
38434
  const { left, top, height } = ev.target.getBoundingClientRect();
38282
38435
  this.state.menuState.isOpen = true;
38283
38436
  this.state.menuState.position = { x: left, y: top + height };
38284
- 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);
38285
38438
  this.state.menuState.parentMenu = menu;
38286
38439
  this.isSelectingMenu = true;
38287
38440
  this.openedEl = ev.target;
@@ -38330,9 +38483,7 @@ class TopBar extends Component {
38330
38483
  this.style.align = this.style.align || (cell === null || cell === void 0 ? void 0 : cell.defaultAlign);
38331
38484
  this.fillColor = this.style.fillColor || "#ffffff";
38332
38485
  this.textColor = this.style.textColor || "#000000";
38333
- this.menus = topbarMenuRegistry
38334
- .getAll()
38335
- .filter((item) => !item.isVisible || item.isVisible(this.env));
38486
+ this.menus = topbarMenuRegistry.getAll();
38336
38487
  }
38337
38488
  getMenuName(menu) {
38338
38489
  return getMenuName(menu, this.env);
@@ -40398,8 +40549,8 @@ class SelectionStreamProcessor {
40398
40549
  }
40399
40550
  const { left, right, top, bottom } = zone;
40400
40551
  const sheetId = this.getters.getActiveSheetId();
40401
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
40402
- 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);
40403
40554
  if (refRow === undefined || refCol === undefined) {
40404
40555
  return 17 /* CommandResult.SelectionOutOfBound */;
40405
40556
  }
@@ -40474,10 +40625,10 @@ class SelectionStreamProcessor {
40474
40625
  const { col: anchorCol, row: anchorRow } = anchor.cell;
40475
40626
  return {
40476
40627
  col: this.getters.isColHidden(sheetId, anchorCol)
40477
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
40628
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
40478
40629
  : anchorCol,
40479
40630
  row: this.getters.isRowHidden(sheetId, anchorRow)
40480
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
40631
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
40481
40632
  : anchorRow,
40482
40633
  };
40483
40634
  }
@@ -42117,6 +42268,8 @@ class Model extends EventBus {
42117
42268
  * Internal status of the model. Important for command handling coordination
42118
42269
  */
42119
42270
  this.status = 0 /* Status.Ready */;
42271
+ this.handlers = [];
42272
+ this.coreHandlers = [];
42120
42273
  /**
42121
42274
  * The dispatch method is the only entry point to manipulate data in the model.
42122
42275
  * This is through this method that commands are dispatched most of the time
@@ -42189,7 +42342,7 @@ class Model extends EventBus {
42189
42342
  const command = { type, ...payload };
42190
42343
  const previousStatus = this.status;
42191
42344
  this.status = 2 /* Status.RunningCore */;
42192
- const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers;
42345
+ const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
42193
42346
  this.dispatchToHandlers(handlers, command);
42194
42347
  this.status = previousStatus;
42195
42348
  return DispatchResult.Success;
@@ -42223,6 +42376,8 @@ class Model extends EventBus {
42223
42376
  this.uuidGenerator.setIsFastStrategy(true);
42224
42377
  // Initiate stream processor
42225
42378
  this.selection = new SelectionStreamProcessor(this.getters);
42379
+ this.coreHandlers.push(this.range);
42380
+ this.handlers.push(this.range);
42226
42381
  // registering plugins
42227
42382
  for (let Plugin of corePluginRegistry.getAll()) {
42228
42383
  this.setupCorePlugin(Plugin, workbookData);
@@ -42232,6 +42387,7 @@ class Model extends EventBus {
42232
42387
  this.setupUiPlugin(Plugin);
42233
42388
  }
42234
42389
  this.uuidGenerator.setIsFastStrategy(false);
42390
+ this.handlers.push(this.history);
42235
42391
  // starting plugins
42236
42392
  this.dispatch("START");
42237
42393
  // Model should be the last permanent subscriber in the list since he should render
@@ -42252,9 +42408,6 @@ class Model extends EventBus {
42252
42408
  // by owl, since we do not rely on reactivity
42253
42409
  markRaw(this);
42254
42410
  }
42255
- get handlers() {
42256
- return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history];
42257
- }
42258
42411
  joinSession() {
42259
42412
  this.session.join(this.config.client);
42260
42413
  }
@@ -42273,6 +42426,7 @@ class Model extends EventBus {
42273
42426
  this.getters[name] = plugin[name].bind(plugin);
42274
42427
  }
42275
42428
  this.uiPlugins.push(plugin);
42429
+ this.handlers.push(plugin);
42276
42430
  const layers = Plugin.layers.map((l) => [plugin, l]);
42277
42431
  this.renderers.push(...layers);
42278
42432
  this.renderers.sort((p1, p2) => p1[1] - p2[1]);
@@ -42296,6 +42450,8 @@ class Model extends EventBus {
42296
42450
  }
42297
42451
  plugin.import(data);
42298
42452
  this.corePlugins.push(plugin);
42453
+ this.coreHandlers.push(plugin);
42454
+ this.handlers.push(plugin);
42299
42455
  }
42300
42456
  onRemoteRevisionReceived({ commands }) {
42301
42457
  for (let command of commands) {
@@ -42313,7 +42469,7 @@ class Model extends EventBus {
42313
42469
  return;
42314
42470
  }
42315
42471
  this.isReplayingCommand = true;
42316
- this.dispatchToHandlers([this.range, ...this.corePlugins], command);
42472
+ this.dispatchToHandlers(this.coreHandlers, command);
42317
42473
  this.isReplayingCommand = false;
42318
42474
  }), this.config.transportService, revisionId);
42319
42475
  return session;
@@ -42413,7 +42569,7 @@ class Model extends EventBus {
42413
42569
  }
42414
42570
  }
42415
42571
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
42416
- data = JSON.parse(JSON.stringify(data));
42572
+ data = deepCopy(data);
42417
42573
  return data;
42418
42574
  }
42419
42575
  updateMode(mode) {
@@ -42440,7 +42596,7 @@ class Model extends EventBus {
42440
42596
  handler.exportForExcel(data);
42441
42597
  }
42442
42598
  }
42443
- data = JSON.parse(JSON.stringify(data));
42599
+ data = deepCopy(data);
42444
42600
  return getXLSX(data);
42445
42601
  }
42446
42602
  }
@@ -42544,6 +42700,6 @@ function addFunction(functionName, functionDescription) {
42544
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 };
42545
42701
 
42546
42702
 
42547
- __info__.version = '16.0.6';
42548
- __info__.date = '2023-04-13T08:30:26.954Z';
42549
- __info__.hash = '8d60b5c';
42703
+ __info__.version = '16.0.8';
42704
+ __info__.date = '2023-04-21T07:29:33.894Z';
42705
+ __info__.hash = 'dbd7aa4';