@odoo/o-spreadsheet 16.1.7 → 16.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1023,6 +1023,10 @@ function deepEquals(o1, o2) {
1023
1023
  }
1024
1024
  return true;
1025
1025
  }
1026
+ /** Check if the given array contains all the values of the other array. */
1027
+ function includesAll(arr, values) {
1028
+ return values.every((value) => arr.includes(value));
1029
+ }
1026
1030
  /**
1027
1031
  * Return an object with all the keys in the object that have a falsy value removed.
1028
1032
  */
@@ -1662,12 +1666,77 @@ const numberRepresentation = [];
1662
1666
  * - all digit stored in the decimal part of the number
1663
1667
  *
1664
1668
  * The 'maxDecimal' parameter allows to indicate the number of digits to not
1665
- * exceed in the decimal part, in which case digits are rounded
1669
+ * exceed in the decimal part, in which case digits are rounded.
1666
1670
  *
1667
- * Intl.Numberformat is used to properly handle all the roundings.
1668
- * e.g. 1234.7 with format ### (<> maxDecimals=0) should become 1235, not 1234
1669
1671
  **/
1670
1672
  function splitNumber(value, maxDecimals = MAX_DECIMAL_PLACES) {
1673
+ const asString = value.toString();
1674
+ if (asString.includes("e"))
1675
+ return splitNumberIntl(value, maxDecimals);
1676
+ if (Number.isInteger(value)) {
1677
+ return { integerDigits: asString, decimalDigits: undefined };
1678
+ }
1679
+ const indexOfDot = asString.indexOf(".");
1680
+ let integerDigits = asString.substring(0, indexOfDot);
1681
+ let decimalDigits = asString.substring(indexOfDot + 1);
1682
+ if (maxDecimals === 0) {
1683
+ if (Number(decimalDigits[0]) >= 5) {
1684
+ integerDigits = (Number(integerDigits) + 1).toString();
1685
+ }
1686
+ return { integerDigits, decimalDigits: undefined };
1687
+ }
1688
+ if (decimalDigits.length > maxDecimals) {
1689
+ const { integerDigits: roundedIntegerDigits, decimalDigits: roundedDecimalDigits } = limitDecimalDigits(decimalDigits, maxDecimals);
1690
+ decimalDigits = roundedDecimalDigits;
1691
+ if (roundedIntegerDigits !== "0") {
1692
+ integerDigits = (Number(integerDigits) + Number(roundedIntegerDigits)).toString();
1693
+ }
1694
+ }
1695
+ return { integerDigits, decimalDigits: removeTrailingZeroes(decimalDigits || "") };
1696
+ }
1697
+ /**
1698
+ * Return the given string minus the trailing "0" characters.
1699
+ *
1700
+ * @param numberString : a string of integers
1701
+ * @returns the numberString, minus the eventual zeroes at the end
1702
+ */
1703
+ function removeTrailingZeroes(numberString) {
1704
+ let i = numberString.length - 1;
1705
+ while (i >= 0 && numberString[i] === "0") {
1706
+ i--;
1707
+ }
1708
+ return numberString.slice(0, i + 1) || undefined;
1709
+ }
1710
+ /**
1711
+ * Limit the size of the decimal part of a number to the given number of digits.
1712
+ */
1713
+ function limitDecimalDigits(decimalDigits, maxDecimals) {
1714
+ let integerDigits = "0";
1715
+ let resultDecimalDigits = decimalDigits;
1716
+ // Note : we'd want to simply use number.toFixed() to handle the max digits & rounding,
1717
+ // but it has very strange behaviour. Ex: 12.345.toFixed(2) => "12.35", but 1.345.toFixed(2) => "1.34"
1718
+ let slicedDecimalDigits = decimalDigits.slice(0, maxDecimals);
1719
+ const i = maxDecimals;
1720
+ if (Number(Number(decimalDigits[i]) < 5)) {
1721
+ return { integerDigits, decimalDigits: slicedDecimalDigits };
1722
+ }
1723
+ // round up
1724
+ const slicedRoundedUp = (Number(slicedDecimalDigits) + 1).toString();
1725
+ if (slicedRoundedUp.length > slicedDecimalDigits.length) {
1726
+ integerDigits = (Number(integerDigits) + 1).toString();
1727
+ resultDecimalDigits = undefined;
1728
+ }
1729
+ else {
1730
+ resultDecimalDigits = slicedRoundedUp;
1731
+ }
1732
+ return { integerDigits, decimalDigits: resultDecimalDigits };
1733
+ }
1734
+ /**
1735
+ * Split numbers into decimal/integer digits using Intl.NumberFormat.
1736
+ * Supports numbers with a lot of digits that are transformed to scientific notation by
1737
+ * number.toString(), but is slow.
1738
+ */
1739
+ function splitNumberIntl(value, maxDecimals = MAX_DECIMAL_PLACES) {
1671
1740
  let formatter = numberRepresentation[maxDecimals];
1672
1741
  if (!formatter) {
1673
1742
  formatter = new Intl.NumberFormat("en-US", {
@@ -5906,13 +5975,16 @@ class Menu extends owl.Component {
5906
5975
  }
5907
5976
  });
5908
5977
  }
5978
+ get visibleMenuItems() {
5979
+ return this.props.menuItems.filter((x) => x.isVisible(this.env));
5980
+ }
5909
5981
  get subMenuPosition() {
5910
5982
  const position = Object.assign({}, this.subMenu.position);
5911
5983
  position.y -= this.subMenu.scrollOffset || 0;
5912
5984
  return position;
5913
5985
  }
5914
5986
  get menuHeight() {
5915
- const menuItems = this.props.menuItems;
5987
+ const menuItems = this.visibleMenuItems;
5916
5988
  let menuItemsHeight = this.getMenuItemsHeight(menuItems);
5917
5989
  // We don't display separator at the end of a menu
5918
5990
  if (menuItems[menuItems.length - 1].separator) {
@@ -5954,7 +6026,7 @@ class Menu extends owl.Component {
5954
6026
  * and the menu item at a given index.
5955
6027
  */
5956
6028
  subMenuVerticalPosition(menuIndex) {
5957
- const menusAbove = this.props.menuItems.slice(0, menuIndex);
6029
+ const menusAbove = this.visibleMenuItems.slice(0, menuIndex);
5958
6030
  return this.position.y + this.getMenuItemsHeight(menusAbove) + MENU_VERTICAL_PADDING;
5959
6031
  }
5960
6032
  onExternalClick(ev) {
@@ -6126,7 +6198,7 @@ class ChartFigure extends owl.Component {
6126
6198
  openContextMenu(position) {
6127
6199
  const registry = this.getMenuItemRegistry();
6128
6200
  this.menuState.isOpen = true;
6129
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
6201
+ this.menuState.menuItems = registry.getAll();
6130
6202
  this.menuState.position = position;
6131
6203
  }
6132
6204
  get chartComponent() {
@@ -7257,6 +7329,11 @@ colMenuRegistry
7257
7329
  name: REMOVE_COLUMNS_NAME,
7258
7330
  sequence: 90,
7259
7331
  action: REMOVE_COLUMNS_ACTION,
7332
+ isVisible: (env) => {
7333
+ const sheetId = env.model.getters.getActiveSheetId();
7334
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7335
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7336
+ },
7260
7337
  })
7261
7338
  .add("clear_column", {
7262
7339
  name: DELETE_CONTENT_COLUMNS_NAME,
@@ -7269,9 +7346,8 @@ colMenuRegistry
7269
7346
  action: HIDE_COLUMNS_ACTION,
7270
7347
  isVisible: (env) => {
7271
7348
  const sheetId = env.model.getters.getActiveSheetId();
7272
- const hiddenCols = env.model.getters.getHiddenColsGroups(sheetId).flat();
7273
- return (env.model.getters.getNumberCols(sheetId) >
7274
- hiddenCols.length + env.model.getters.getElementsFromSelection("COL").length);
7349
+ const selectedCols = env.model.getters.getElementsFromSelection("COL");
7350
+ return env.model.getters.canRemoveHeaders(sheetId, "COL", selectedCols);
7275
7351
  },
7276
7352
  separator: true,
7277
7353
  })
@@ -7345,6 +7421,11 @@ rowMenuRegistry
7345
7421
  name: REMOVE_ROWS_NAME,
7346
7422
  sequence: 70,
7347
7423
  action: REMOVE_ROWS_ACTION,
7424
+ isVisible: (env) => {
7425
+ const sheetId = env.model.getters.getActiveSheetId();
7426
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7427
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7428
+ },
7348
7429
  })
7349
7430
  .add("clear_row", {
7350
7431
  name: DELETE_CONTENT_ROWS_NAME,
@@ -7357,9 +7438,8 @@ rowMenuRegistry
7357
7438
  action: HIDE_ROWS_ACTION,
7358
7439
  isVisible: (env) => {
7359
7440
  const sheetId = env.model.getters.getActiveSheetId();
7360
- const hiddenRows = env.model.getters.getHiddenRowsGroups(sheetId).flat();
7361
- return (env.model.getters.getNumberRows(sheetId) >
7362
- hiddenRows.length + env.model.getters.getElementsFromSelection("ROW").length);
7441
+ const selectedRows = env.model.getters.getElementsFromSelection("ROW");
7442
+ return env.model.getters.canRemoveHeaders(sheetId, "ROW", selectedRows);
7363
7443
  },
7364
7444
  separator: true,
7365
7445
  })
@@ -16932,14 +17012,31 @@ class ContentEditableHelper {
16932
17012
  * */
16933
17013
  getCurrentSelection() {
16934
17014
  let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
16935
- let startSizeBefore = this.findSizeBeforeElement(startElement);
16936
- let endSizeBefore = this.findSizeBeforeElement(endElement);
17015
+ let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
17016
+ let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
16937
17017
  return {
16938
- start: startSizeBefore + startSelectionOffset,
16939
- end: endSizeBefore + endSelectionOffset,
17018
+ start: startSizeBefore,
17019
+ end: endSizeBefore,
16940
17020
  };
16941
17021
  }
16942
- findSizeBeforeElement(nodeToFind) {
17022
+ /**
17023
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
17024
+ * The selected node is either a Text node or an Element node.
17025
+ *
17026
+ * case 1 -Text node:
17027
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
17028
+ * content length of all previous nodes.
17029
+ *
17030
+ * case 2 - Element node:
17031
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
17032
+ * all the bnodes prior to the selected node as well as the content of the child node before the offset.
17033
+ *
17034
+ * See the MDN documentation for more details.
17035
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
17036
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
17037
+ *
17038
+ */
17039
+ findSelectionIndex(nodeToFind, nodeOffset) {
16943
17040
  let it = this.iterateChildren(this.el);
16944
17041
  let usedCharacters = 0;
16945
17042
  let current = it.next();
@@ -16951,6 +17048,19 @@ class ContentEditableHelper {
16951
17048
  }
16952
17049
  current = it.next();
16953
17050
  }
17051
+ if (current.value === nodeToFind) {
17052
+ if (!current.value.hasChildNodes()) {
17053
+ usedCharacters += nodeOffset;
17054
+ }
17055
+ else {
17056
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
17057
+ for (const child of children) {
17058
+ if (!child.textContent)
17059
+ continue;
17060
+ usedCharacters += child.textContent.length;
17061
+ }
17062
+ }
17063
+ }
16954
17064
  return usedCharacters;
16955
17065
  }
16956
17066
  getStartAndEndSelection() {
@@ -23542,8 +23652,8 @@ class Grid extends owl.Component {
23542
23652
  },
23543
23653
  "CTRL+END": () => {
23544
23654
  const sheetId = this.env.model.getters.getActiveSheetId();
23545
- const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", range(0, this.env.model.getters.getNumberCols(sheetId)).reverse());
23546
- const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", range(0, this.env.model.getters.getNumberRows(sheetId)).reverse());
23655
+ const col = this.env.model.getters.findVisibleHeader(sheetId, "COL", this.env.model.getters.getNumberCols(sheetId) - 1, 0);
23656
+ const row = this.env.model.getters.findVisibleHeader(sheetId, "ROW", this.env.model.getters.getNumberRows(sheetId) - 1, 0);
23547
23657
  this.env.model.selection.selectCell(col, row);
23548
23658
  },
23549
23659
  "SHIFT+ ": () => {
@@ -23623,9 +23733,10 @@ class Grid extends owl.Component {
23623
23733
  this.focus();
23624
23734
  }
23625
23735
  focus() {
23736
+ var _a;
23626
23737
  if (!this.env.model.getters.getSelectedFigureId() &&
23627
23738
  this.env.model.getters.getEditionMode() === "inactive") {
23628
- this.hiddenInput.el.focus();
23739
+ (_a = this.hiddenInput.el) === null || _a === void 0 ? void 0 : _a.focus();
23629
23740
  }
23630
23741
  }
23631
23742
  get gridEl() {
@@ -23826,9 +23937,7 @@ class Grid extends owl.Component {
23826
23937
  }
23827
23938
  this.menuState.isOpen = true;
23828
23939
  this.menuState.position = { x, y };
23829
- this.menuState.menuItems = registries$1[type]
23830
- .getAll()
23831
- .filter((item) => !item.isVisible || item.isVisible(this.env));
23940
+ this.menuState.menuItems = registries$1[type].getAll();
23832
23941
  }
23833
23942
  copy(cut, ev) {
23834
23943
  if (!this.gridEl.contains(document.activeElement)) {
@@ -27197,7 +27306,7 @@ function load(data, verboseImport) {
27197
27306
  }
27198
27307
  }
27199
27308
  }
27200
- data = JSON.parse(JSON.stringify(data));
27309
+ data = deepCopy(data);
27201
27310
  // apply migrations, if needed
27202
27311
  if ("version" in data) {
27203
27312
  if (data.version < CURRENT_VERSION) {
@@ -29857,7 +29966,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
29857
29966
  allowDispatch(cmd) {
29858
29967
  switch (cmd.type) {
29859
29968
  case "HIDE_COLUMNS_ROWS": {
29860
- if (!this.hiddenHeaders[cmd.sheetId]) {
29969
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29861
29970
  return 27 /* CommandResult.InvalidSheetId */;
29862
29971
  }
29863
29972
  const hiddenGroup = cmd.dimension === "COL"
@@ -29870,6 +29979,14 @@ class HeaderVisibilityPlugin extends CorePlugin {
29870
29979
  ? 0 /* CommandResult.Success */
29871
29980
  : 66 /* CommandResult.TooManyHiddenElements */;
29872
29981
  }
29982
+ case "REMOVE_COLUMNS_ROWS":
29983
+ if (!this.getters.tryGetSheet(cmd.sheetId)) {
29984
+ return 27 /* CommandResult.InvalidSheetId */;
29985
+ }
29986
+ if (!this.canRemoveHeaders(cmd.sheetId, cmd.dimension, cmd.elements)) {
29987
+ return 8 /* CommandResult.NotEnoughElements */;
29988
+ }
29989
+ return 0 /* CommandResult.Success */;
29873
29990
  }
29874
29991
  return 0 /* CommandResult.Success */;
29875
29992
  }
@@ -29916,6 +30033,10 @@ class HeaderVisibilityPlugin extends CorePlugin {
29916
30033
  }
29917
30034
  return;
29918
30035
  }
30036
+ canRemoveHeaders(sheetId, dimension, elements) {
30037
+ const visibleHeaders = this.getAllVisibleHeaders(sheetId, dimension);
30038
+ return !includesAll(elements, visibleHeaders);
30039
+ }
29919
30040
  isRowHiddenByUser(sheetId, index) {
29920
30041
  return this.hiddenHeaders[sheetId].ROW[index];
29921
30042
  }
@@ -29960,6 +30081,9 @@ class HeaderVisibilityPlugin extends CorePlugin {
29960
30081
  }
29961
30082
  return consecutiveIndexes;
29962
30083
  }
30084
+ getAllVisibleHeaders(sheetId, dimension) {
30085
+ return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
30086
+ }
29963
30087
  import(data) {
29964
30088
  var _a, _b;
29965
30089
  for (let sheet of data.sheets) {
@@ -30007,6 +30131,7 @@ class HeaderVisibilityPlugin extends CorePlugin {
30007
30131
  }
30008
30132
  }
30009
30133
  HeaderVisibilityPlugin.getters = [
30134
+ "canRemoveHeaders",
30010
30135
  "getHiddenColsGroups",
30011
30136
  "getHiddenRowsGroups",
30012
30137
  "isRowHiddenByUser",
@@ -31016,14 +31141,6 @@ class SheetPlugin extends CorePlugin {
31016
31141
  return this.orderedSheetIds.length > 1
31017
31142
  ? 0 /* CommandResult.Success */
31018
31143
  : 9 /* CommandResult.NotEnoughSheets */;
31019
- case "REMOVE_COLUMNS_ROWS": {
31020
- const length = cmd.dimension === "COL"
31021
- ? this.getNumberCols(cmd.sheetId)
31022
- : this.getNumberRows(cmd.sheetId);
31023
- return length > cmd.elements.length
31024
- ? 0 /* CommandResult.Success */
31025
- : 8 /* CommandResult.NotEnoughElements */;
31026
- }
31027
31144
  case "FREEZE_ROWS": {
31028
31145
  return this.checkValidations(cmd, this.checkRowFreezeQuantity, this.checkRowFreezeOverlapMerge);
31029
31146
  }
@@ -31494,7 +31611,7 @@ class SheetPlugin extends CorePlugin {
31494
31611
  duplicateSheet(fromId, toId) {
31495
31612
  const sheet = this.getSheet(fromId);
31496
31613
  const toName = this.getDuplicateSheetName(sheet.name);
31497
- const newSheet = JSON.parse(JSON.stringify(sheet));
31614
+ const newSheet = deepCopy(sheet);
31498
31615
  newSheet.id = toId;
31499
31616
  newSheet.name = toName;
31500
31617
  for (let col = 0; col <= newSheet.numberOfCols; col++) {
@@ -31621,10 +31738,10 @@ class SheetPlugin extends CorePlugin {
31621
31738
  }
31622
31739
  }
31623
31740
  moveCellOnColumnsDeletion(sheet, deletedColumn) {
31624
- for (let [index, row] of Object.entries(sheet.rows)) {
31625
- const rowIndex = parseInt(index, 10);
31741
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31742
+ const row = sheet.rows[rowIndex];
31626
31743
  for (let i in row.cells) {
31627
- const colIndex = parseInt(i, 10);
31744
+ const colIndex = Number(i);
31628
31745
  const cellId = row.cells[i];
31629
31746
  if (cellId) {
31630
31747
  if (colIndex === deletedColumn) {
@@ -31651,11 +31768,11 @@ class SheetPlugin extends CorePlugin {
31651
31768
  */
31652
31769
  moveCellsOnAddition(sheet, addedElement, quantity, dimension) {
31653
31770
  const commands = [];
31654
- for (const [index, row] of Object.entries(sheet.rows)) {
31655
- const rowIndex = parseInt(index, 10);
31771
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31772
+ const row = sheet.rows[rowIndex];
31656
31773
  if (dimension !== "rows" || rowIndex >= addedElement) {
31657
31774
  for (let i in row.cells) {
31658
- const colIndex = parseInt(i, 10);
31775
+ const colIndex = Number(i);
31659
31776
  const cellId = row.cells[i];
31660
31777
  if (cellId) {
31661
31778
  if (dimension === "rows" || colIndex >= addedElement) {
@@ -31685,11 +31802,11 @@ class SheetPlugin extends CorePlugin {
31685
31802
  */
31686
31803
  moveCellOnRowsDeletion(sheet, deleteFromRow, deleteToRow) {
31687
31804
  const numberRows = deleteToRow - deleteFromRow + 1;
31688
- for (let [index, row] of Object.entries(sheet.rows)) {
31689
- const rowIndex = parseInt(index, 10);
31805
+ for (let rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
31806
+ const row = sheet.rows[rowIndex];
31690
31807
  if (rowIndex >= deleteFromRow && rowIndex <= deleteToRow) {
31691
31808
  for (let i in row.cells) {
31692
- const colIndex = parseInt(i, 10);
31809
+ const colIndex = Number(i);
31693
31810
  const cellId = row.cells[i];
31694
31811
  if (cellId) {
31695
31812
  this.dispatch("CLEAR_CELL", {
@@ -31702,7 +31819,7 @@ class SheetPlugin extends CorePlugin {
31702
31819
  }
31703
31820
  if (rowIndex > deleteToRow) {
31704
31821
  for (let i in row.cells) {
31705
- const colIndex = parseInt(i, 10);
31822
+ const colIndex = Number(i);
31706
31823
  const cellId = row.cells[i];
31707
31824
  if (cellId) {
31708
31825
  this.dispatch("UPDATE_CELL_POSITION", {
@@ -31720,7 +31837,7 @@ class SheetPlugin extends CorePlugin {
31720
31837
  const rows = [];
31721
31838
  const cellsQueue = sheet.rows.map((row) => row.cells);
31722
31839
  for (let i in sheet.rows) {
31723
- if (parseInt(i, 10) === index) {
31840
+ if (Number(i) === index) {
31724
31841
  continue;
31725
31842
  }
31726
31843
  rows.push({
@@ -32863,13 +32980,11 @@ class FilterEvaluationPlugin extends UIPlugin {
32863
32980
  return Boolean(id && ((_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b.length));
32864
32981
  }
32865
32982
  intersectZoneWithViewport(sheetId, zone) {
32866
- const colsRange = range(zone.left, zone.right + 1);
32867
- const rowsRange = range(zone.top, zone.bottom + 1);
32868
32983
  return {
32869
- left: this.getters.findVisibleHeader(sheetId, "COL", colsRange),
32870
- right: this.getters.findVisibleHeader(sheetId, "COL", colsRange.reverse()),
32871
- top: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange),
32872
- bottom: this.getters.findVisibleHeader(sheetId, "ROW", rowsRange.reverse()),
32984
+ left: this.getters.findVisibleHeader(sheetId, "COL", zone.left, zone.right),
32985
+ right: this.getters.findVisibleHeader(sheetId, "COL", zone.right, zone.left),
32986
+ top: this.getters.findVisibleHeader(sheetId, "ROW", zone.top, zone.bottom),
32987
+ bottom: this.getters.findVisibleHeader(sheetId, "ROW", zone.bottom, zone.top),
32873
32988
  };
32874
32989
  }
32875
32990
  updateFilter({ col, row, values, sheetId }) {
@@ -33073,48 +33188,66 @@ class InternalViewport {
33073
33188
  this.adjustPositionY(row);
33074
33189
  }
33075
33190
  }
33076
- adjustPositionX(col) {
33191
+ adjustPositionX(targetCol) {
33077
33192
  const sheetId = this.sheetId;
33078
- const { start, end } = this.getters.getColDimensions(sheetId, col);
33079
- while (end > this.offsetX + this.offsetCorrectionX + this.viewportWidth &&
33080
- this.offsetX + this.offsetCorrectionX < start) {
33081
- this.offsetX = this.getters.getColDimensions(sheetId, this.left).end - this.offsetCorrectionX;
33082
- this.offsetScrollbarX = this.offsetX;
33083
- this.adjustViewportZoneX();
33084
- }
33085
- while (col < this.left) {
33086
- let leftCol;
33087
- for (leftCol = this.left; leftCol >= 0; leftCol--) {
33088
- if (!this.getters.isColHidden(sheetId, leftCol)) {
33193
+ const { end } = this.getters.getColDimensions(sheetId, targetCol);
33194
+ const maxCol = this.getters.getNumberCols(sheetId);
33195
+ if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
33196
+ for (let col = this.left; this.offsetX + this.offsetCorrectionX + this.viewportWidth < end; col++) {
33197
+ if (col > maxCol) {
33089
33198
  break;
33090
33199
  }
33200
+ if (this.getters.isColHidden(sheetId, col)) {
33201
+ continue;
33202
+ }
33203
+ this.offsetX = this.getters.getColDimensions(sheetId, col).end - this.offsetCorrectionX;
33204
+ this.offsetScrollbarX = this.offsetX;
33205
+ this.adjustViewportZoneX();
33206
+ }
33207
+ }
33208
+ else if (this.left > targetCol) {
33209
+ for (let col = this.left; col >= targetCol; col--) {
33210
+ if (col < 0) {
33211
+ break;
33212
+ }
33213
+ if (this.getters.isColHidden(sheetId, col)) {
33214
+ continue;
33215
+ }
33216
+ this.offsetX = this.getters.getColDimensions(sheetId, col).start - this.offsetCorrectionX;
33217
+ this.offsetScrollbarX = this.offsetX;
33218
+ this.adjustViewportZoneX();
33091
33219
  }
33092
- this.offsetX =
33093
- this.getters.getColDimensions(sheetId, leftCol - 1).start - this.offsetCorrectionX;
33094
- this.offsetScrollbarX = this.offsetX;
33095
- this.adjustViewportZoneX();
33096
33220
  }
33097
33221
  }
33098
- adjustPositionY(row) {
33222
+ adjustPositionY(targetRow) {
33099
33223
  const sheetId = this.sheetId;
33100
- while (this.getters.getRowDimensions(sheetId, row).end >
33101
- this.offsetY + this.viewportHeight + this.offsetCorrectionY &&
33102
- this.offsetY + this.offsetCorrectionY < this.getters.getRowDimensions(sheetId, row).start) {
33103
- this.offsetY = this.getters.getRowDimensions(sheetId, this.top).end - this.offsetCorrectionY;
33104
- this.offsetScrollbarY = this.offsetY;
33105
- this.adjustViewportZoneY();
33106
- }
33107
- while (row < this.top) {
33108
- let topRow;
33109
- for (topRow = this.top; topRow >= 0; topRow--) {
33110
- if (!this.getters.isRowHidden(sheetId, topRow)) {
33224
+ const { end } = this.getters.getRowDimensions(sheetId, targetRow);
33225
+ const maxRow = this.getters.getNumberRows(sheetId);
33226
+ if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
33227
+ for (let row = this.top; this.offsetY + this.viewportHeight + this.offsetCorrectionY < end; row++) {
33228
+ if (row > maxRow) {
33111
33229
  break;
33112
33230
  }
33231
+ if (this.getters.isRowHidden(sheetId, row)) {
33232
+ continue;
33233
+ }
33234
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).end - this.offsetCorrectionY;
33235
+ this.offsetScrollbarY = this.offsetY;
33236
+ this.adjustViewportZoneY();
33237
+ }
33238
+ }
33239
+ else if (this.top > targetRow) {
33240
+ for (let row = this.top; row >= targetRow; row--) {
33241
+ if (row < 0) {
33242
+ break;
33243
+ }
33244
+ if (this.getters.isRowHidden(sheetId, row)) {
33245
+ continue;
33246
+ }
33247
+ this.offsetY = this.getters.getRowDimensions(sheetId, row).start - this.offsetCorrectionY;
33248
+ this.offsetScrollbarY = this.offsetY;
33249
+ this.adjustViewportZoneY();
33113
33250
  }
33114
- this.offsetY =
33115
- this.getters.getRowDimensions(sheetId, topRow - 1).start - this.offsetCorrectionY;
33116
- this.offsetScrollbarY = this.offsetY;
33117
- this.adjustViewportZoneY();
33118
33251
  }
33119
33252
  }
33120
33253
  setViewportOffset(offsetX, offsetY) {
@@ -34957,13 +35090,33 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34957
35090
  getNextVisibleCellPosition({ sheetId, col, row }) {
34958
35091
  return {
34959
35092
  sheetId,
34960
- col: this.findVisibleHeader(sheetId, "COL", range(col, this.getters.getNumberCols(sheetId))),
34961
- row: this.findVisibleHeader(sheetId, "ROW", range(row, this.getters.getNumberRows(sheetId))),
35093
+ col: this.findVisibleHeader(sheetId, "COL", col, this.getters.getNumberCols(sheetId) - 1),
35094
+ row: this.findVisibleHeader(sheetId, "ROW", row, this.getters.getNumberRows(sheetId) - 1),
34962
35095
  };
34963
35096
  }
34964
- findVisibleHeader(sheetId, dimension, indexes) {
34965
- return indexes.find((index) => this.getters.doesHeaderExist(sheetId, dimension, index) &&
34966
- !this.isHeaderHidden(sheetId, dimension, index));
35097
+ /**
35098
+ * Find the first visible header in the range [`from` => `to`].
35099
+ *
35100
+ * Both `from` and `to` are inclusive.
35101
+ */
35102
+ findVisibleHeader(sheetId, dimension, from, to) {
35103
+ if (from <= to) {
35104
+ for (let i = from; i <= to; i++) {
35105
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35106
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35107
+ return i;
35108
+ }
35109
+ }
35110
+ }
35111
+ if (from > to) {
35112
+ for (let i = from; i >= to; i--) {
35113
+ if (this.getters.doesHeaderExist(sheetId, dimension, i) &&
35114
+ !this.isHeaderHidden(sheetId, dimension, i)) {
35115
+ return i;
35116
+ }
35117
+ }
35118
+ }
35119
+ return undefined;
34967
35120
  }
34968
35121
  findLastVisibleColRowIndex(sheetId, dimension, { last, first }) {
34969
35122
  const lastVisibleIndex = range(last, first, -1).find((index) => !this.isHeaderHidden(sheetId, dimension, index));
@@ -34971,7 +35124,7 @@ class HeaderVisibilityUIPlugin extends UIPlugin {
34971
35124
  }
34972
35125
  findFirstVisibleColRowIndex(sheetId, dimension) {
34973
35126
  const numberOfHeaders = this.getters.getNumberHeaders(sheetId, dimension);
34974
- for (let i = 0; i < numberOfHeaders - 1; i++) {
35127
+ for (let i = 0; i < numberOfHeaders; i++) {
34975
35128
  if (dimension === "COL" && !this.isColHidden(sheetId, i)) {
34976
35129
  return i;
34977
35130
  }
@@ -39198,7 +39351,7 @@ class BottomBar extends owl.Component {
39198
39351
  }
39199
39352
  openContextMenu(x, y, registry, menuId) {
39200
39353
  this.menuState.isOpen = true;
39201
- this.menuState.menuItems = registry.getAll().filter((x) => x.isVisible(this.env));
39354
+ this.menuState.menuItems = registry.getAll();
39202
39355
  this.menuState.position = { x, y };
39203
39356
  this.menuState.menuId = menuId;
39204
39357
  }
@@ -40005,7 +40158,7 @@ class TopBar extends owl.Component {
40005
40158
  const { left, top, height } = ev.target.getBoundingClientRect();
40006
40159
  this.state.menuState.isOpen = true;
40007
40160
  this.state.menuState.position = { x: left, y: top + height };
40008
- this.state.menuState.menuItems = getMenuChildren(menu, this.env).filter((item) => !item.isVisible || item.isVisible(this.env));
40161
+ this.state.menuState.menuItems = getMenuChildren(menu, this.env);
40009
40162
  this.state.menuState.parentMenu = menu;
40010
40163
  this.isSelectingMenu = true;
40011
40164
  this.openedEl = ev.target;
@@ -40048,9 +40201,7 @@ class TopBar extends owl.Component {
40048
40201
  this.style.align = this.style.align || cell.defaultAlign;
40049
40202
  this.fillColor = this.style.fillColor || "#ffffff";
40050
40203
  this.textColor = this.style.textColor || "#000000";
40051
- this.menus = topbarMenuRegistry
40052
- .getAll()
40053
- .filter((item) => !item.isVisible || item.isVisible(this.env));
40204
+ this.menus = topbarMenuRegistry.getAll();
40054
40205
  }
40055
40206
  getMenuName(menu) {
40056
40207
  return getMenuName(menu, this.env);
@@ -41764,8 +41915,8 @@ class SelectionStreamProcessor {
41764
41915
  }
41765
41916
  const { left, right, top, bottom } = zone;
41766
41917
  const sheetId = this.getters.getActiveSheetId();
41767
- const refCol = this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1));
41768
- const refRow = this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1));
41918
+ const refCol = this.getters.findVisibleHeader(sheetId, "COL", left, right);
41919
+ const refRow = this.getters.findVisibleHeader(sheetId, "ROW", top, bottom);
41769
41920
  if (refRow === undefined || refCol === undefined) {
41770
41921
  return 17 /* CommandResult.SelectionOutOfBound */;
41771
41922
  }
@@ -41840,10 +41991,10 @@ class SelectionStreamProcessor {
41840
41991
  const { col: anchorCol, row: anchorRow } = anchor.cell;
41841
41992
  return {
41842
41993
  col: this.getters.isColHidden(sheetId, anchorCol)
41843
- ? this.getters.findVisibleHeader(sheetId, "COL", range(left, right + 1)) || anchorCol
41994
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
41844
41995
  : anchorCol,
41845
41996
  row: this.getters.isRowHidden(sheetId, anchorRow)
41846
- ? this.getters.findVisibleHeader(sheetId, "ROW", range(top, bottom + 1)) || anchorRow
41997
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
41847
41998
  : anchorRow,
41848
41999
  };
41849
42000
  }
@@ -43524,6 +43675,8 @@ class Model extends EventBus {
43524
43675
  * Internal status of the model. Important for command handling coordination
43525
43676
  */
43526
43677
  this.status = 0 /* Status.Ready */;
43678
+ this.handlers = [];
43679
+ this.coreHandlers = [];
43527
43680
  /**
43528
43681
  * The dispatch method is the only entry point to manipulate data in the model.
43529
43682
  * This is through this method that commands are dispatched most of the time
@@ -43628,6 +43781,8 @@ class Model extends EventBus {
43628
43781
  this.uuidGenerator.setIsFastStrategy(true);
43629
43782
  // Initiate stream processor
43630
43783
  this.selection = new SelectionStreamProcessor(this.getters);
43784
+ this.coreHandlers.push(this.range);
43785
+ this.handlers.push(this.range);
43631
43786
  this.corePluginConfig = this.setupCorePluginConfig();
43632
43787
  this.uiPluginConfig = this.setupUiPluginConfig();
43633
43788
  // registering plugins
@@ -43636,15 +43791,23 @@ class Model extends EventBus {
43636
43791
  }
43637
43792
  Object.assign(this.getters, this.coreGetters);
43638
43793
  for (let Plugin of statefulUIPluginRegistry.getAll()) {
43639
- this.statefulUIPlugins.push(this.setupUiPlugin(Plugin));
43794
+ const plugin = this.setupUiPlugin(Plugin);
43795
+ this.statefulUIPlugins.push(plugin);
43796
+ this.handlers.push(plugin);
43640
43797
  }
43641
43798
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
43642
- this.coreViewsPlugins.push(this.setupUiPlugin(Plugin));
43799
+ const plugin = this.setupUiPlugin(Plugin);
43800
+ this.coreViewsPlugins.push(plugin);
43801
+ this.handlers.push(plugin);
43802
+ this.coreHandlers.push(plugin);
43643
43803
  }
43644
43804
  for (let Plugin of featurePluginRegistry.getAll()) {
43645
- this.featurePlugins.push(this.setupUiPlugin(Plugin));
43805
+ const plugin = this.setupUiPlugin(Plugin);
43806
+ this.featurePlugins.push(plugin);
43807
+ this.handlers.push(plugin);
43646
43808
  }
43647
43809
  this.uuidGenerator.setIsFastStrategy(false);
43810
+ this.handlers.push(this.history);
43648
43811
  // starting plugins
43649
43812
  this.dispatch("START");
43650
43813
  // Model should be the last permanent subscriber in the list since he should render
@@ -43666,12 +43829,6 @@ class Model extends EventBus {
43666
43829
  // by owl, since we do not rely on reactivity
43667
43830
  owl.markRaw(this);
43668
43831
  }
43669
- get handlers() {
43670
- return [this.range, ...this.corePlugins, ...this.allUIPlugins, this.history];
43671
- }
43672
- get allUIPlugins() {
43673
- return [...this.statefulUIPlugins, ...this.coreViewsPlugins, ...this.featurePlugins];
43674
- }
43675
43832
  joinSession() {
43676
43833
  this.session.join(this.config.client);
43677
43834
  }
@@ -43713,6 +43870,8 @@ class Model extends EventBus {
43713
43870
  }
43714
43871
  plugin.import(data);
43715
43872
  this.corePlugins.push(plugin);
43873
+ this.coreHandlers.push(plugin);
43874
+ this.handlers.push(plugin);
43716
43875
  }
43717
43876
  onRemoteRevisionReceived({ commands }) {
43718
43877
  for (let command of commands) {
@@ -43855,7 +44014,7 @@ class Model extends EventBus {
43855
44014
  }
43856
44015
  }
43857
44016
  data.revisionId = this.session.getRevisionId() || DEFAULT_REVISION_ID;
43858
- data = JSON.parse(JSON.stringify(data));
44017
+ data = deepCopy(data);
43859
44018
  return data;
43860
44019
  }
43861
44020
  updateMode(mode) {
@@ -43883,7 +44042,7 @@ class Model extends EventBus {
43883
44042
  handler.exportForExcel(data);
43884
44043
  }
43885
44044
  }
43886
- data = JSON.parse(JSON.stringify(data));
44045
+ data = deepCopy(data);
43887
44046
  return getXLSX(data);
43888
44047
  }
43889
44048
  garbageCollectExternalResources() {
@@ -44024,6 +44183,6 @@ exports.setTranslationMethod = setTranslationMethod;
44024
44183
  exports.tokenize = tokenize;
44025
44184
 
44026
44185
 
44027
- __info__.version = '16.1.7';
44028
- __info__.date = '2023-04-13T08:29:52.140Z';
44029
- __info__.hash = '3141a6d';
44186
+ __info__.version = '16.1.9';
44187
+ __info__.date = '2023-04-21T07:29:41.080Z';
44188
+ __info__.hash = '686bffa';