@odoo/o-spreadsheet 17.2.34 → 17.2.36

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.2.34
7
- * @date 2025-01-31T07:58:03.953Z
8
- * @hash 0bffee9
6
+ * @version 17.2.36
7
+ * @date 2025-02-10T08:59:25.719Z
8
+ * @hash 5a942bd
9
9
  */
10
10
 
11
11
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw } from '@odoo/owl';
@@ -604,9 +604,16 @@ function deepEquals(o1, o2) {
604
604
  }
605
605
  return true;
606
606
  }
607
- /** Check if the given array contains all the values of the other array. */
607
+ /**
608
+ * Check if the given array contains all the values of the other array.
609
+ * It makes the assumption that both array do not contain duplicates.
610
+ */
608
611
  function includesAll(arr, values) {
609
- return values.every((value) => arr.includes(value));
612
+ if (arr.length < values.length) {
613
+ return false;
614
+ }
615
+ const set = new Set(arr);
616
+ return values.every((value) => set.has(value));
610
617
  }
611
618
  /**
612
619
  * Return an object with all the keys in the object that have a falsy value removed.
@@ -17751,23 +17758,24 @@ const HLOOKUP = {
17751
17758
  description: _t("Horizontal lookup"),
17752
17759
  args: [
17753
17760
  arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
17754
- arg("range (range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
17761
+ arg("range (any, range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
17755
17762
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
17756
17763
  arg(`is_sorted (boolean, default=${DEFAULT_IS_SORTED})`, _t("Indicates whether the row to be searched (the first row of the specified range) is sorted, in which case the closest match for search_key will be returned.")),
17757
17764
  ],
17758
17765
  returns: ["ANY"],
17759
17766
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
17760
17767
  const _index = Math.trunc(toNumber(index?.value, this.locale));
17761
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
17768
+ const _range = toMatrix(range);
17769
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
17762
17770
  if (searchKey && isEvaluationError(searchKey.value)) {
17763
17771
  return searchKey;
17764
17772
  }
17765
17773
  const getValueFromRange = (range, index) => range[index][0].value;
17766
17774
  const _isSorted = toBoolean(isSorted.value);
17767
17775
  const colIndex = _isSorted
17768
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
17769
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
17770
- const col = range[colIndex];
17776
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
17777
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
17778
+ const col = _range[colIndex];
17771
17779
  if (col === undefined) {
17772
17780
  return valueNotAvailable(searchKey);
17773
17781
  }
@@ -17876,36 +17884,38 @@ const LOOKUP = {
17876
17884
  description: _t("Look up a value."),
17877
17885
  args: [
17878
17886
  arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
17879
- arg("search_array (range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
17880
- arg("result_range (range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
17887
+ arg("search_array (any, range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
17888
+ arg("result_range (any, range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
17881
17889
  ],
17882
17890
  returns: ["ANY"],
17883
17891
  compute: function (searchKey, searchArray, resultRange) {
17884
- let nbCol = searchArray.length;
17885
- let nbRow = searchArray[0].length;
17892
+ const _searchArray = toMatrix(searchArray);
17893
+ const _resultRange = toMatrix(resultRange);
17894
+ let nbCol = _searchArray.length;
17895
+ let nbRow = _searchArray[0].length;
17886
17896
  const verticalSearch = nbRow >= nbCol;
17887
17897
  const getElement = verticalSearch
17888
17898
  ? (range, index) => range[0][index].value
17889
17899
  : (range, index) => range[index][0].value;
17890
17900
  const rangeLength = verticalSearch ? nbRow : nbCol;
17891
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
17901
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
17892
17902
  if (index === -1 ||
17893
- (verticalSearch && searchArray[0][index] === undefined) ||
17894
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
17903
+ (verticalSearch && _searchArray[0][index] === undefined) ||
17904
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
17895
17905
  return valueNotAvailable(searchKey);
17896
17906
  }
17897
- if (resultRange === undefined) {
17898
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
17907
+ if (_resultRange[0].length === 0) {
17908
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
17899
17909
  }
17900
- nbCol = resultRange.length;
17901
- nbRow = resultRange[0].length;
17910
+ nbCol = _resultRange.length;
17911
+ nbRow = _resultRange[0].length;
17902
17912
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
17903
17913
  if (nbCol > 1) {
17904
17914
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
17905
- return resultRange[index][0];
17915
+ return _resultRange[index][0];
17906
17916
  }
17907
17917
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
17908
- return resultRange[0][index];
17918
+ return _resultRange[0][index];
17909
17919
  },
17910
17920
  isExported: true,
17911
17921
  };
@@ -17923,28 +17933,29 @@ const MATCH = {
17923
17933
  returns: ["NUMBER"],
17924
17934
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
17925
17935
  let _searchType = toNumber(searchType, this.locale);
17926
- const nbCol = range.length;
17927
- const nbRow = range[0].length;
17936
+ const _range = toMatrix(range);
17937
+ const nbCol = _range.length;
17938
+ const nbRow = _range[0].length;
17928
17939
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
17929
17940
  let index = -1;
17930
17941
  const getElement = nbCol === 1
17931
- ? (range, index) => range[0][index].value
17932
- : (range, index) => range[index][0].value;
17933
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
17942
+ ? (_range, index) => _range[0][index].value
17943
+ : (_range, index) => _range[index][0].value;
17944
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
17934
17945
  _searchType = Math.sign(_searchType);
17935
17946
  switch (_searchType) {
17936
17947
  case 1:
17937
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
17948
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
17938
17949
  break;
17939
17950
  case 0:
17940
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
17951
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
17941
17952
  break;
17942
17953
  case -1:
17943
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
17954
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
17944
17955
  break;
17945
17956
  }
17946
- if ((nbCol === 1 && range[0][index] === undefined) ||
17947
- (nbCol !== 1 && range[index] === undefined)) {
17957
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
17958
+ (nbCol !== 1 && _range[index] === undefined)) {
17948
17959
  return valueNotAvailable(searchKey);
17949
17960
  }
17950
17961
  return index + 1;
@@ -17995,16 +18006,17 @@ const VLOOKUP = {
17995
18006
  returns: ["ANY"],
17996
18007
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
17997
18008
  const _index = Math.trunc(toNumber(index?.value, this.locale));
17998
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18009
+ const _range = toMatrix(range);
18010
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
17999
18011
  if (searchKey && isEvaluationError(searchKey.value)) {
18000
18012
  return searchKey;
18001
18013
  }
18002
18014
  const getValueFromRange = (range, index) => range[0][index].value;
18003
18015
  const _isSorted = toBoolean(isSorted.value);
18004
18016
  const rowIndex = _isSorted
18005
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18006
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
18007
- const value = range[_index - 1][rowIndex];
18017
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18018
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18019
+ const value = _range[_index - 1][rowIndex];
18008
18020
  if (value === undefined) {
18009
18021
  return valueNotAvailable(searchKey);
18010
18022
  }
@@ -18042,30 +18054,32 @@ const XLOOKUP = {
18042
18054
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18043
18055
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18044
18056
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18045
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18057
+ const _lookupRange = toMatrix(lookupRange);
18058
+ const _returnRange = toMatrix(returnRange);
18059
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18046
18060
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18047
18061
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18048
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18062
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18049
18063
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18050
18064
  assert(() => lookupDirection === "col"
18051
- ? returnRange[0].length === lookupRange[0].length
18052
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18065
+ ? _returnRange[0].length === _lookupRange[0].length
18066
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18053
18067
  if (searchKey && isEvaluationError(searchKey.value)) {
18054
18068
  return [[searchKey]];
18055
18069
  }
18056
18070
  const getElement = lookupDirection === "col"
18057
18071
  ? (range, index) => range[0][index].value
18058
18072
  : (range, index) => range[index][0].value;
18059
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18073
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18060
18074
  const mode = MATCH_MODE[_matchMode];
18061
18075
  const reverseSearch = _searchMode === -1;
18062
18076
  const index = _searchMode === 2 || _searchMode === -2
18063
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18064
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18077
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18078
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18065
18079
  if (index !== -1) {
18066
18080
  return lookupDirection === "col"
18067
- ? returnRange.map((col) => [col[index]])
18068
- : [returnRange[index]];
18081
+ ? _returnRange.map((col) => [col[index]])
18082
+ : [_returnRange[index]];
18069
18083
  }
18070
18084
  if (defaultValue === undefined) {
18071
18085
  return valueNotAvailable(searchKey);
@@ -34695,11 +34709,10 @@ class GridRenderer {
34695
34709
  switch (layer) {
34696
34710
  case "Background":
34697
34711
  this.drawGlobalBackground(renderingContext);
34698
- for (const zone of this.getters.getAllActiveViewportsZones()) {
34712
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
34699
34713
  const { ctx } = renderingContext;
34700
34714
  ctx.save();
34701
34715
  ctx.beginPath();
34702
- const rect = this.getters.getVisibleRect(zone);
34703
34716
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
34704
34717
  ctx.clip();
34705
34718
  const boxes = this.getGridBoxes(zone);
@@ -34969,10 +34982,8 @@ class GridRenderer {
34969
34982
  const { ctx, thinLineWidth } = renderingContext;
34970
34983
  const visibleCols = this.getters.getSheetViewVisibleCols();
34971
34984
  const left = visibleCols[0];
34972
- const right = visibleCols[visibleCols.length - 1];
34973
34985
  const visibleRows = this.getters.getSheetViewVisibleRows();
34974
34986
  const top = visibleRows[0];
34975
- const bottom = visibleRows[visibleRows.length - 1];
34976
34987
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
34977
34988
  const selection = this.getters.getSelectedZones();
34978
34989
  const selectedCols = getZonesCols(selection);
@@ -34988,7 +34999,7 @@ class GridRenderer {
34988
34999
  ctx.lineWidth = thinLineWidth;
34989
35000
  ctx.strokeStyle = "#333";
34990
35001
  // Columns headers background
34991
- for (let col = left; col <= right; col++) {
35002
+ for (const col of visibleCols) {
34992
35003
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
34993
35004
  const { x, width } = this.getters.getVisibleRect(colZone);
34994
35005
  const isColActive = activeCols.has(col);
@@ -35005,7 +35016,7 @@ class GridRenderer {
35005
35016
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
35006
35017
  }
35007
35018
  // Rows headers background
35008
- for (let row = top; row <= bottom; row++) {
35019
+ for (const row of visibleRows) {
35009
35020
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
35010
35021
  const { y, height } = this.getters.getVisibleRect(rowZone);
35011
35022
  const isRowActive = activeRows.has(row);
@@ -35031,21 +35042,21 @@ class GridRenderer {
35031
35042
  ctx.stroke();
35032
35043
  ctx.beginPath();
35033
35044
  // column text + separator
35034
- for (const i of visibleCols) {
35035
- const colSize = this.getters.getColSize(sheetId, i);
35036
- const colName = numberToLetters(i);
35037
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
35038
- let colStart = this.getHeaderOffset("COL", left, i);
35045
+ for (const col of visibleCols) {
35046
+ const colSize = this.getters.getColSize(sheetId, col);
35047
+ const colName = numberToLetters(col);
35048
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
35049
+ let colStart = this.getHeaderOffset("COL", left, col);
35039
35050
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
35040
35051
  ctx.moveTo(colStart + colSize, 0);
35041
35052
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
35042
35053
  }
35043
35054
  // row text + separator
35044
- for (const i of visibleRows) {
35045
- const rowSize = this.getters.getRowSize(sheetId, i);
35046
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
35047
- let rowStart = this.getHeaderOffset("ROW", top, i);
35048
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
35055
+ for (const row of visibleRows) {
35056
+ const rowSize = this.getters.getRowSize(sheetId, row);
35057
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
35058
+ let rowStart = this.getHeaderOffset("ROW", top, row);
35059
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
35049
35060
  ctx.moveTo(0, rowStart + rowSize);
35050
35061
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
35051
35062
  }
@@ -35337,6 +35348,9 @@ function useGridDrawing(refName, model, canvasSize) {
35337
35348
  canvas.width = width * dpr;
35338
35349
  canvas.height = height * dpr;
35339
35350
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
35351
+ if (width === 0 || height === 0) {
35352
+ return;
35353
+ }
35340
35354
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
35341
35355
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
35342
35356
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -40894,7 +40908,7 @@ class BordersPlugin extends CorePlugin {
40894
40908
  getCommonSides(border1, border2) {
40895
40909
  const commonBorder = {};
40896
40910
  for (let side of ["top", "bottom", "left", "right"]) {
40897
- if (border1[side] && border1[side] === border2[side]) {
40911
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
40898
40912
  commonBorder[side] = border1[side];
40899
40913
  }
40900
40914
  }
@@ -53805,8 +53819,17 @@ class InternalViewport {
53805
53819
  this.getters = getters;
53806
53820
  this.sheetId = sheetId;
53807
53821
  this.boundaries = boundaries;
53808
- this.viewportWidth = sizeInGrid.width;
53809
- this.viewportHeight = sizeInGrid.height;
53822
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
53823
+ throw new Error("Viewport size cannot be negative");
53824
+ }
53825
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
53826
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
53827
+ this.top = boundaries.top;
53828
+ this.bottom = boundaries.bottom;
53829
+ this.left = boundaries.left;
53830
+ this.right = boundaries.right;
53831
+ this.offsetX = offsets.x;
53832
+ this.offsetY = offsets.y;
53810
53833
  this.offsetScrollbarX = offsets.x;
53811
53834
  this.offsetScrollbarY = offsets.y;
53812
53835
  this.canScrollVertically = options.canScrollVertically;
@@ -53849,9 +53872,9 @@ class InternalViewport {
53849
53872
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
53850
53873
  );
53851
53874
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
53852
- }
53853
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
53854
- height += FOOTER_HEIGHT;
53875
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
53876
+ height += FOOTER_HEIGHT;
53877
+ }
53855
53878
  }
53856
53879
  return { width, height };
53857
53880
  }
@@ -53988,6 +54011,9 @@ class InternalViewport {
53988
54011
  !this.getters.isRowHidden(this.sheetId, row));
53989
54012
  }
53990
54013
  searchHeaderIndex(dimension, position, startIndex = 0) {
54014
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
54015
+ return -1;
54016
+ }
53991
54017
  const sheetId = this.sheetId;
53992
54018
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
53993
54019
  // using a binary search:
@@ -54024,7 +54050,7 @@ class InternalViewport {
54024
54050
  this.adjustViewportZoneY();
54025
54051
  }
54026
54052
  /** Corrects the viewport's horizontal offset based on the current structure
54027
- * To make sure that at least on column is visible inside the viewport.
54053
+ * To make sure that at least one column is visible inside the viewport.
54028
54054
  */
54029
54055
  adjustViewportOffsetX() {
54030
54056
  if (this.canScrollHorizontally) {
@@ -54036,7 +54062,7 @@ class InternalViewport {
54036
54062
  this.adjustViewportZoneX();
54037
54063
  }
54038
54064
  /** Corrects the viewport's vertical offset based on the current structure
54039
- * To make sure that at least on row is visible inside the viewport.
54065
+ * To make sure that at least one row is visible inside the viewport.
54040
54066
  */
54041
54067
  adjustViewportOffsetY() {
54042
54068
  if (this.canScrollVertically) {
@@ -54053,11 +54079,14 @@ class InternalViewport {
54053
54079
  const sheetId = this.sheetId;
54054
54080
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
54055
54081
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
54082
+ if (!this.viewportWidth) {
54083
+ return;
54084
+ }
54056
54085
  if (this.left === -1) {
54057
54086
  this.left = this.boundaries.left;
54058
54087
  }
54059
54088
  if (this.right === -1) {
54060
- this.right = this.getters.getNumberCols(sheetId) - 1;
54089
+ this.right = this.boundaries.right;
54061
54090
  }
54062
54091
  this.offsetX =
54063
54092
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -54069,11 +54098,14 @@ class InternalViewport {
54069
54098
  const sheetId = this.sheetId;
54070
54099
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
54071
54100
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
54101
+ if (!this.viewportHeight) {
54102
+ return;
54103
+ }
54072
54104
  if (this.top === -1) {
54073
54105
  this.top = this.boundaries.top;
54074
54106
  }
54075
54107
  if (this.bottom === -1) {
54076
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
54108
+ this.bottom = this.boundaries.bottom;
54077
54109
  }
54078
54110
  this.offsetY =
54079
54111
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -54147,7 +54179,7 @@ class SheetViewPlugin extends UIPlugin {
54147
54179
  "isPositionVisible",
54148
54180
  "getColDimensionsInViewport",
54149
54181
  "getRowDimensionsInViewport",
54150
- "getAllActiveViewportsZones",
54182
+ "getAllActiveViewportsZonesAndRect",
54151
54183
  "getRect",
54152
54184
  ];
54153
54185
  viewports = {};
@@ -54381,12 +54413,12 @@ class SheetViewPlugin extends UIPlugin {
54381
54413
  const sheetId = this.getters.getActiveSheetId();
54382
54414
  const viewports = this.getSubViewports(sheetId);
54383
54415
  //TODO ake another commit to eimprove this
54384
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
54416
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
54385
54417
  }
54386
54418
  getSheetViewVisibleRows() {
54387
54419
  const sheetId = this.getters.getActiveSheetId();
54388
54420
  const viewports = this.getSubViewports(sheetId);
54389
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
54421
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
54390
54422
  }
54391
54423
  /**
54392
54424
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -54429,19 +54461,19 @@ class SheetViewPlugin extends UIPlugin {
54429
54461
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
54430
54462
  };
54431
54463
  }
54432
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
54433
- const sheetId = this.getters.getActiveSheetId();
54434
- const visibleCols = this.getters.getSheetViewVisibleCols();
54435
- const visibleRows = this.getters.getSheetViewVisibleRows();
54436
- if (index < referenceIndex) {
54437
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
54464
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
54465
+ if (targetHeaderIndex < referenceHeaderIndex) {
54466
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
54438
54467
  }
54468
+ const sheetId = this.getters.getActiveSheetId();
54469
+ const visibleHeaders = dimension === "COL"
54470
+ ? this.getters.getSheetViewVisibleCols()
54471
+ : this.getters.getSheetViewVisibleRows();
54472
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
54473
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
54474
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
54439
54475
  let offset = 0;
54440
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
54441
- for (let i = referenceIndex; i < index; i++) {
54442
- if (!visibleIndexes.includes(i)) {
54443
- continue;
54444
- }
54476
+ for (const i of relevantIndexes) {
54445
54477
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
54446
54478
  }
54447
54479
  return offset;
@@ -54488,7 +54520,7 @@ class SheetViewPlugin extends UIPlugin {
54488
54520
  }
54489
54521
  return { canEdgeScroll, direction, delay };
54490
54522
  }
54491
- getEdgeScrollRow(y, previousY, tartingY) {
54523
+ getEdgeScrollRow(y, previousY, startingY) {
54492
54524
  let canEdgeScroll = false;
54493
54525
  let direction = 0;
54494
54526
  let delay = 0;
@@ -54509,7 +54541,7 @@ class SheetViewPlugin extends UIPlugin {
54509
54541
  delay = scrollDelay(y - height);
54510
54542
  direction = 1;
54511
54543
  }
54512
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
54544
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
54513
54545
  // 2
54514
54546
  canEdgeScroll = true;
54515
54547
  delay = scrollDelay(offsetCorrectionY - y);
@@ -54535,13 +54567,7 @@ class SheetViewPlugin extends UIPlugin {
54535
54567
  */
54536
54568
  getVisibleRectWithoutHeaders(zone) {
54537
54569
  const sheetId = this.getters.getActiveSheetId();
54538
- const viewportRects = this.getSubViewports(sheetId)
54539
- .map((viewport) => viewport.getVisibleRect(zone))
54540
- .filter(isDefined$1);
54541
- if (viewportRects.length === 0) {
54542
- return { x: 0, y: 0, width: 0, height: 0 };
54543
- }
54544
- return this.recomposeRect(viewportRects);
54570
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
54545
54571
  }
54546
54572
  /**
54547
54573
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -54549,13 +54575,7 @@ class SheetViewPlugin extends UIPlugin {
54549
54575
  */
54550
54576
  getRect(zone) {
54551
54577
  const sheetId = this.getters.getActiveSheetId();
54552
- const viewportRects = this.getSubViewports(sheetId)
54553
- .map((viewport) => viewport.getFullRect(zone))
54554
- .filter(isDefined$1);
54555
- if (viewportRects.length === 0) {
54556
- return { x: 0, y: 0, width: 0, height: 0 };
54557
- }
54558
- const rect = this.recomposeRect(viewportRects);
54578
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
54559
54579
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
54560
54580
  }
54561
54581
  /**
@@ -54600,9 +54620,18 @@ class SheetViewPlugin extends UIPlugin {
54600
54620
  end: start + (isRowHidden ? 0 : size),
54601
54621
  };
54602
54622
  }
54603
- getAllActiveViewportsZones() {
54623
+ getAllActiveViewportsZonesAndRect() {
54604
54624
  const sheetId = this.getters.getActiveSheetId();
54605
- return this.getSubViewports(sheetId);
54625
+ return this.getSubViewports(sheetId).map((viewport) => {
54626
+ return {
54627
+ zone: viewport,
54628
+ rect: {
54629
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
54630
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
54631
+ ...viewport.getMaxSize(),
54632
+ },
54633
+ };
54634
+ });
54606
54635
  }
54607
54636
  // ---------------------------------------------------------------------------
54608
54637
  // Private
@@ -54655,12 +54684,11 @@ class SheetViewPlugin extends UIPlugin {
54655
54684
  }
54656
54685
  /** gets rid of deprecated sheetIds */
54657
54686
  cleanViewports() {
54658
- const sheetIds = this.getters.getSheetIds();
54659
- for (let sheetId of Object.keys(this.viewports)) {
54660
- if (!sheetIds.includes(sheetId)) {
54661
- delete this.viewports[sheetId];
54662
- }
54687
+ const newViewport = {};
54688
+ for (const sheetId of this.getters.getSheetIds()) {
54689
+ newViewport[sheetId] = this.viewports[sheetId];
54663
54690
  }
54691
+ this.viewports = newViewport;
54664
54692
  }
54665
54693
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
54666
54694
  this.sheetViewHeight = height;
@@ -54670,14 +54698,14 @@ class SheetViewPlugin extends UIPlugin {
54670
54698
  this.recomputeViewports();
54671
54699
  }
54672
54700
  recomputeViewports() {
54673
- for (let sheetId of Object.keys(this.viewports)) {
54701
+ for (const sheetId of this.getters.getSheetIds()) {
54674
54702
  this.resetViewports(sheetId);
54675
54703
  }
54676
54704
  }
54677
54705
  setSheetViewOffset(offsetX, offsetY) {
54678
54706
  const sheetId = this.getters.getActiveSheetId();
54679
54707
  const { maxOffsetX, maxOffsetY } = this.getMaximumSheetOffset();
54680
- Object.values(this.getSubViewports(sheetId)).forEach((viewport) => viewport.setViewportOffset(clip(offsetX, 0, maxOffsetX), clip(offsetY, 0, maxOffsetY)));
54708
+ this.getSubViewports(sheetId).forEach((viewport) => viewport.setViewportOffset(clip(offsetX, 0, maxOffsetX), clip(offsetY, 0, maxOffsetY)));
54681
54709
  }
54682
54710
  getViewportOffset(sheetId) {
54683
54711
  return {
@@ -54692,8 +54720,10 @@ class SheetViewPlugin extends UIPlugin {
54692
54720
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
54693
54721
  const nCols = this.getters.getNumberCols(sheetId);
54694
54722
  const nRows = this.getters.getNumberRows(sheetId);
54695
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
54696
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
54723
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
54724
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
54725
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
54726
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
54697
54727
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
54698
54728
  const canScrollHorizontally = xRatio < 1.0;
54699
54729
  const canScrollVertically = yRatio < 1.0;
@@ -54704,14 +54734,14 @@ class SheetViewPlugin extends UIPlugin {
54704
54734
  new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: 0, bottom: ySplit - 1 }, { width: colOffset, height: rowOffset }, { canScrollHorizontally: false, canScrollVertically: false }, { x: 0, y: 0 })) ||
54705
54735
  undefined,
54706
54736
  topRight: (ySplit &&
54707
- new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: this.sheetViewWidth - colOffset, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) ||
54737
+ new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: unfrozenWidth, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) ||
54708
54738
  undefined,
54709
54739
  bottomLeft: (xSplit &&
54710
- new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: this.sheetViewHeight - rowOffset }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) ||
54740
+ new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: unfrozenHeight }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) ||
54711
54741
  undefined,
54712
54742
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
54713
- width: this.sheetViewWidth - colOffset,
54714
- height: this.sheetViewHeight - rowOffset,
54743
+ width: unfrozenWidth,
54744
+ height: unfrozenHeight,
54715
54745
  }, { canScrollHorizontally, canScrollVertically }, {
54716
54746
  x: canScrollHorizontally ? previousOffset.x : 0,
54717
54747
  y: canScrollVertically ? previousOffset.y : 0,
@@ -54723,7 +54753,7 @@ class SheetViewPlugin extends UIPlugin {
54723
54753
  * Adjust the viewport such that the anchor position is visible
54724
54754
  */
54725
54755
  refreshViewport(sheetId, anchorPosition) {
54726
- Object.values(this.getSubViewports(sheetId)).forEach((viewport) => {
54756
+ this.getSubViewports(sheetId).forEach((viewport) => {
54727
54757
  viewport.adjustViewportZone();
54728
54758
  viewport.adjustPosition(anchorPosition);
54729
54759
  });
@@ -54788,12 +54818,26 @@ class SheetViewPlugin extends UIPlugin {
54788
54818
  const height = this.sheetViewHeight + this.gridOffsetY;
54789
54819
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
54790
54820
  }
54791
- recomposeRect(viewportRects) {
54792
- const x = Math.min(...viewportRects.map((rect) => rect.x));
54793
- const y = Math.min(...viewportRects.map((rect) => rect.y));
54794
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
54795
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
54796
- return { x, y, width, height };
54821
+ mapViewportsToRect(sheetId, rectCallBack) {
54822
+ let x = Infinity;
54823
+ let y = Infinity;
54824
+ let width = 0;
54825
+ let height = 0;
54826
+ let hasViewports = false;
54827
+ for (const viewport of this.getSubViewports(sheetId)) {
54828
+ const rect = rectCallBack(viewport);
54829
+ if (rect) {
54830
+ hasViewports = true;
54831
+ x = Math.min(x, rect.x);
54832
+ y = Math.min(y, rect.y);
54833
+ width = Math.max(width, rect.x + rect.width);
54834
+ height = Math.max(height, rect.y + rect.height);
54835
+ }
54836
+ }
54837
+ if (!hasViewports) {
54838
+ return { x: 0, y: 0, width: 0, height: 0 };
54839
+ }
54840
+ return { x, y, width: width - x, height: height - y };
54797
54841
  }
54798
54842
  }
54799
54843
 
@@ -61091,6 +61135,6 @@ const constants = {
61091
61135
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
61092
61136
 
61093
61137
 
61094
- __info__.version = "17.2.34";
61095
- __info__.date = "2025-01-31T07:58:03.953Z";
61096
- __info__.hash = "0bffee9";
61138
+ __info__.version = "17.2.36";
61139
+ __info__.date = "2025-02-10T08:59:25.719Z";
61140
+ __info__.hash = "5a942bd";