@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.
@@ -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
  }
@@ -35989,48 +36124,66 @@ class InternalViewport {
35989
36124
  this.adjustPositionY(row);
35990
36125
  }
35991
36126
  }
35992
- adjustPositionX(col) {
36127
+ adjustPositionX(targetCol) {
35993
36128
  const sheetId = this.sheetId;
35994
- const { start, end } = this.getters.getColDimensions(sheetId, col);
35995
- while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
35996
- this.offsetX + this.offsetCorrectionX < start) {
35997
- this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
35998
- this.offsetScrollbarX = this.offsetX;
35999
- this.adjustViewportZoneX();
36000
- }
36001
- while (col < this.left) {
36002
- let leftCol;
36003
- for (leftCol = this.left; leftCol >= 0; leftCol--) {
36004
- if (!this.getters.isColHidden(sheetId, leftCol)) {
36129
+ const { end } = this.getters.getColDimensions(sheetId, targetCol);
36130
+ const maxCol = this.getters.getNumberCols(sheetId);
36131
+ if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
36132
+ for (let col = this.left; this.offsetX + this.offsetCorrectionX + this.viewportWidth < end; col++) {
36133
+ if (col > maxCol) {
36134
+ break;
36135
+ }
36136
+ if (this.getters.isColHidden(sheetId, col)) {
36137
+ continue;
36138
+ }
36139
+ this.offsetX = this.getters.getColDimensions(sheetId, col).end - this.offsetCorrectionX;
36140
+ this.offsetScrollbarX = this.offsetX;
36141
+ this.adjustViewportZoneX();
36142
+ }
36143
+ }
36144
+ else if (this.left > targetCol) {
36145
+ for (let col = this.left; col >= targetCol; col--) {
36146
+ if (col < 0) {
36005
36147
  break;
36006
36148
  }
36149
+ if (this.getters.isColHidden(sheetId, col)) {
36150
+ continue;
36151
+ }
36152
+ this.offsetX = this.getters.getColDimensions(sheetId, col).start - this.offsetCorrectionX;
36153
+ this.offsetScrollbarX = this.offsetX;
36154
+ this.adjustViewportZoneX();
36007
36155
  }
36008
- this.offsetX =
36009
- this.getters.getColDimensions(sheetId, leftCol - 1).start - this.offsetCorrectionX;
36010
- this.offsetScrollbarX = this.offsetX;
36011
- this.adjustViewportZoneX();
36012
36156
  }
36013
36157
  }
36014
- adjustPositionY(row) {
36158
+ adjustPositionY(targetRow) {
36015
36159
  const sheetId = this.sheetId;
36016
- while (this.getters.getRowDimensions(sheetId, row).end >
36017
- this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
36018
- this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
36019
- this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
36020
- this.offsetScrollbarY = this.offsetY;
36021
- this.adjustViewportZoneY();
36022
- }
36023
- while (row < this.top) {
36024
- let topRow;
36025
- for (topRow = this.top; topRow >= 0; topRow--) {
36026
- if (!this.getters.isRowHidden(sheetId, topRow)) {
36160
+ const { end } = this.getters.getRowDimensions(sheetId, targetRow);
36161
+ const maxRow = this.getters.getNumberRows(sheetId);
36162
+ if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
36163
+ for (let row = this.top; this.offsetY + this.viewportHeight + this.offsetCorrectionY < end; row++) {
36164
+ if (row > maxRow) {
36165
+ break;
36166
+ }
36167
+ if (this.getters.isRowHidden(sheetId, row)) {
36168
+ continue;
36169
+ }
36170
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).end - this.offsetCorrectionY;
36171
+ this.offsetScrollbarY = this.offsetY;
36172
+ this.adjustViewportZoneY();
36173
+ }
36174
+ }
36175
+ else if (this.top > targetRow) {
36176
+ for (let row = this.top; row >= targetRow; row--) {
36177
+ if (row < 0) {
36027
36178
  break;
36028
36179
  }
36180
+ if (this.getters.isRowHidden(sheetId, row)) {
36181
+ continue;
36182
+ }
36183
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).start - this.offsetCorrectionY;
36184
+ this.offsetScrollbarY = this.offsetY;
36185
+ this.adjustViewportZoneY();
36029
36186
  }
36030
- this.offsetY =
36031
- this.getters.getRowDimensions(sheetId, topRow - 1).start - this.offsetCorrectionY;
36032
- this.offsetScrollbarY = this.offsetY;
36033
- this.adjustViewportZoneY();
36034
36187
  }
36035
36188
  }
36036
36189
  setViewportOffset(offsetX, offsetY) {
@@ -37524,7 +37677,7 @@ class BottomBar extends owl.Component {
37524
37677
  }
37525
37678
  openContextMenu(x, y, registry, menuId) {
37526
37679
  this.menuState.isOpen = true;
37527
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
37680
+ this.menuState.menuItems = registry.getAll();
37528
37681
  this.menuState.position = { x, y };
37529
37682
  this.menuState.menuId = menuId;
37530
37683
  }
@@ -38285,7 +38438,7 @@ class TopBar extends owl.Component {
38285
38438
  const { left, top, height } = ev.target.getBoundingClientRect();
38286
38439
  this.state.menuState.isOpen = true;
38287
38440
  this.state.menuState.position = { x: left, y: top + height };
38288
- 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);
38289
38442
  this.state.menuState.parentMenu = menu;
38290
38443
  this.isSelectingMenu = true;
38291
38444
  this.openedEl = ev.target;
@@ -38334,9 +38487,7 @@ class TopBar extends owl.Component {
38334
38487
  this.style.align = this.style.align || (cell === null || cell === void 0 ? void 0 : cell.defaultAlign);
38335
38488
  this.fillColor = this.style.fillColor || "#ffffff";
38336
38489
  this.textColor = this.style.textColor || "#000000";
38337
- this.menus = topbarMenuRegistry
38338
- .getAll()
38339
- .filter((item) => !item.isVisible || item.isVisible(this.env));
38490
+ this.menus = topbarMenuRegistry.getAll();
38340
38491
  }
38341
38492
  getMenuName(menu) {
38342
38493
  return getMenuName(menu, this.env);
@@ -40402,8 +40553,8 @@ class SelectionStreamProcessor {
40402
40553
  }
40403
40554
  const { left, right, top, bottom } = zone;
40404
40555
  const sheetId = this.getters.getActiveSheetId();
40405
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
40406
- 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);
40407
40558
  if (refRow === undefined || refCol === undefined) {
40408
40559
  return 17 /* CommandResult.SelectionOutOfBound */;
40409
40560
  }
@@ -40478,10 +40629,10 @@ class SelectionStreamProcessor {
40478
40629
  const { col: anchorCol, row: anchorRow } = anchor.cell;
40479
40630
  return {
40480
40631
  col: this.getters.isColHidden(sheetId, anchorCol)
40481
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
40632
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
40482
40633
  : anchorCol,
40483
40634
  row: this.getters.isRowHidden(sheetId, anchorRow)
40484
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
40635
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
40485
40636
  : anchorRow,
40486
40637
  };
40487
40638
  }
@@ -42121,6 +42272,8 @@ class Model extends EventBus {
42121
42272
  * Internal status of the model. Important for command handling coordination
42122
42273
  */
42123
42274
  this.status = 0 /* Status.Ready */;
42275
+ this.handlers = [];
42276
+ this.coreHandlers = [];
42124
42277
  /**
42125
42278
  * The dispatch method is the only entry point to manipulate data in the model.
42126
42279
  * This is through this method that commands are dispatched most of the time
@@ -42193,7 +42346,7 @@ class Model extends EventBus {
42193
42346
  const command = { type, ...payload };
42194
42347
  const previousStatus = this.status;
42195
42348
  this.status = 2 /* Status.RunningCore */;
42196
- const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers;
42349
+ const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers;
42197
42350
  this.dispatchToHandlers(handlers, command);
42198
42351
  this.status = previousStatus;
42199
42352
  return DispatchResult.Success;
@@ -42227,6 +42380,8 @@ class Model extends EventBus {
42227
42380
  this.uuidGenerator.setIsFastStrategy(true);
42228
42381
  // Initiate stream processor
42229
42382
  this.selection = new SelectionStreamProcessor(this.getters);
42383
+ this.coreHandlers.push(this.range);
42384
+ this.handlers.push(this.range);
42230
42385
  // registering plugins
42231
42386
  for (let Plugin of corePluginRegistry.getAll()) {
42232
42387
  this.setupCorePlugin(Plugin, workbookData);
@@ -42236,6 +42391,7 @@ class Model extends EventBus {
42236
42391
  this.setupUiPlugin(Plugin);
42237
42392
  }
42238
42393
  this.uuidGenerator.setIsFastStrategy(false);
42394
+ this.handlers.push(this.history);
42239
42395
  // starting plugins
42240
42396
  this.dispatch("START");
42241
42397
  // Model should be the last permanent subscriber in the list since he should render
@@ -42256,9 +42412,6 @@ class Model extends EventBus {
42256
42412
  // by owl, since we do not rely on reactivity
42257
42413
  owl.markRaw(this);
42258
42414
  }
42259
- get handlers() {
42260
- return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history];
42261
- }
42262
42415
  joinSession() {
42263
42416
  this.session.join(this.config.client);
42264
42417
  }
@@ -42277,6 +42430,7 @@ class Model extends EventBus {
42277
42430
  this.getters[name] = plugin[name].bind(plugin);
42278
42431
  }
42279
42432
  this.uiPlugins.push(plugin);
42433
+ this.handlers.push(plugin);
42280
42434
  const layers = Plugin.layers.map((l) => [plugin, l]);
42281
42435
  this.renderers.push(...layers);
42282
42436
  this.renderers.sort((p1, p2) => p1[1] - p2[1]);
@@ -42300,6 +42454,8 @@ class Model extends EventBus {
42300
42454
  }
42301
42455
  plugin.import(data);
42302
42456
  this.corePlugins.push(plugin);
42457
+ this.coreHandlers.push(plugin);
42458
+ this.handlers.push(plugin);
42303
42459
  }
42304
42460
  onRemoteRevisionReceived({ commands }) {
42305
42461
  for (let command of commands) {
@@ -42317,7 +42473,7 @@ class Model extends EventBus {
42317
42473
  return;
42318
42474
  }
42319
42475
  this.isReplayingCommand = true;
42320
- this.dispatchToHandlers([this.range, ...this.corePlugins], command);
42476
+ this.dispatchToHandlers(this.coreHandlers, command);
42321
42477
  this.isReplayingCommand = false;
42322
42478
  }), this.config.transportService, revisionId);
42323
42479
  return session;
@@ -42417,7 +42573,7 @@ class Model extends EventBus {
42417
42573
  }
42418
42574
  }
42419
42575
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
42420
- data = JSON.parse(JSON.stringify(data));
42576
+ data = deepCopy(data);
42421
42577
  return data;
42422
42578
  }
42423
42579
  updateMode(mode) {
@@ -42444,7 +42600,7 @@ class Model extends EventBus {
42444
42600
  handler.exportForExcel(data);
42445
42601
  }
42446
42602
  }
42447
- data = JSON.parse(JSON.stringify(data));
42603
+ data = deepCopy(data);
42448
42604
  return getXLSX(data);
42449
42605
  }
42450
42606
  }
@@ -42576,6 +42732,6 @@ exports.setTranslationMethod = setTranslationMethod;
42576
42732
  exports.tokenize = tokenize;
42577
42733
 
42578
42734
 
42579
- __info__.version = '16.0.6';
42580
- __info__.date = '2023-04-13T08:30:26.954Z';
42581
- __info__.hash = '8d60b5c';
42735
+ __info__.version = '16.0.8';
42736
+ __info__.date = '2023-04-21T07:29:33.894Z';
42737
+ __info__.hash = 'dbd7aa4';