@odoo/o-spreadsheet 17.4.20 → 17.4.21

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.4.20
6
- * @date 2025-01-31T08:00:07.103Z
7
- * @hash 54a344a
5
+ * @version 17.4.21
6
+ * @date 2025-02-05T06:46:54.366Z
7
+ * @hash 0c55e18
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -18881,22 +18881,23 @@ const HLOOKUP = {
18881
18881
  description: _t("Horizontal lookup"),
18882
18882
  args: [
18883
18883
  arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18884
- 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.")),
18884
+ 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.")),
18885
18885
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18886
18886
  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.")),
18887
18887
  ],
18888
18888
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18889
18889
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18890
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18890
+ const _range = toMatrix(range);
18891
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18891
18892
  if (searchKey && isEvaluationError(searchKey.value)) {
18892
18893
  return searchKey;
18893
18894
  }
18894
18895
  const getValueFromRange = (range, index) => range[index][0].value;
18895
18896
  const _isSorted = toBoolean(isSorted.value);
18896
18897
  const colIndex = _isSorted
18897
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18898
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18899
- const col = range[colIndex];
18898
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18899
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18900
+ const col = _range[colIndex];
18900
18901
  if (col === undefined) {
18901
18902
  return valueNotAvailable(searchKey);
18902
18903
  }
@@ -18995,35 +18996,37 @@ const LOOKUP = {
18995
18996
  description: _t("Look up a value."),
18996
18997
  args: [
18997
18998
  arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18998
- 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.")),
18999
- 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.")),
18999
+ 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.")),
19000
+ 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.")),
19000
19001
  ],
19001
19002
  compute: function (searchKey, searchArray, resultRange) {
19002
- let nbCol = searchArray.length;
19003
- let nbRow = searchArray[0].length;
19003
+ const _searchArray = toMatrix(searchArray);
19004
+ const _resultRange = toMatrix(resultRange);
19005
+ let nbCol = _searchArray.length;
19006
+ let nbRow = _searchArray[0].length;
19004
19007
  const verticalSearch = nbRow >= nbCol;
19005
19008
  const getElement = verticalSearch
19006
19009
  ? (range, index) => range[0][index].value
19007
19010
  : (range, index) => range[index][0].value;
19008
19011
  const rangeLength = verticalSearch ? nbRow : nbCol;
19009
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
19012
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
19010
19013
  if (index === -1 ||
19011
- (verticalSearch && searchArray[0][index] === undefined) ||
19012
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
19014
+ (verticalSearch && _searchArray[0][index] === undefined) ||
19015
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
19013
19016
  return valueNotAvailable(searchKey);
19014
19017
  }
19015
- if (resultRange === undefined) {
19016
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
19018
+ if (_resultRange[0].length === 0) {
19019
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
19017
19020
  }
19018
- nbCol = resultRange.length;
19019
- nbRow = resultRange[0].length;
19021
+ nbCol = _resultRange.length;
19022
+ nbRow = _resultRange[0].length;
19020
19023
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
19021
19024
  if (nbCol > 1) {
19022
19025
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
19023
- return resultRange[index][0];
19026
+ return _resultRange[index][0];
19024
19027
  }
19025
19028
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
19026
- return resultRange[0][index];
19029
+ return _resultRange[0][index];
19027
19030
  },
19028
19031
  isExported: true,
19029
19032
  };
@@ -19040,28 +19043,29 @@ const MATCH = {
19040
19043
  ],
19041
19044
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
19042
19045
  let _searchType = toNumber(searchType, this.locale);
19043
- const nbCol = range.length;
19044
- const nbRow = range[0].length;
19046
+ const _range = toMatrix(range);
19047
+ const nbCol = _range.length;
19048
+ const nbRow = _range[0].length;
19045
19049
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
19046
19050
  let index = -1;
19047
19051
  const getElement = nbCol === 1
19048
- ? (range, index) => range[0][index].value
19049
- : (range, index) => range[index][0].value;
19050
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
19052
+ ? (_range, index) => _range[0][index].value
19053
+ : (_range, index) => _range[index][0].value;
19054
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
19051
19055
  _searchType = Math.sign(_searchType);
19052
19056
  switch (_searchType) {
19053
19057
  case 1:
19054
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
19058
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
19055
19059
  break;
19056
19060
  case 0:
19057
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
19061
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
19058
19062
  break;
19059
19063
  case -1:
19060
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
19064
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
19061
19065
  break;
19062
19066
  }
19063
- if ((nbCol === 1 && range[0][index] === undefined) ||
19064
- (nbCol !== 1 && range[index] === undefined)) {
19067
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
19068
+ (nbCol !== 1 && _range[index] === undefined)) {
19065
19069
  return valueNotAvailable(searchKey);
19066
19070
  }
19067
19071
  return index + 1;
@@ -19115,16 +19119,17 @@ const VLOOKUP = {
19115
19119
  ],
19116
19120
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
19117
19121
  const _index = Math.trunc(toNumber(index?.value, this.locale));
19118
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
19122
+ const _range = toMatrix(range);
19123
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
19119
19124
  if (searchKey && isEvaluationError(searchKey.value)) {
19120
19125
  return searchKey;
19121
19126
  }
19122
19127
  const getValueFromRange = (range, index) => range[0][index].value;
19123
19128
  const _isSorted = toBoolean(isSorted.value);
19124
19129
  const rowIndex = _isSorted
19125
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
19126
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
19127
- const value = range[_index - 1][rowIndex];
19130
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
19131
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
19132
+ const value = _range[_index - 1][rowIndex];
19128
19133
  if (value === undefined) {
19129
19134
  return valueNotAvailable(searchKey);
19130
19135
  }
@@ -19161,30 +19166,32 @@ const XLOOKUP = {
19161
19166
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
19162
19167
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
19163
19168
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
19164
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
19169
+ const _lookupRange = toMatrix(lookupRange);
19170
+ const _returnRange = toMatrix(returnRange);
19171
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
19165
19172
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
19166
19173
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
19167
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
19174
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
19168
19175
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
19169
19176
  assert(() => lookupDirection === "col"
19170
- ? returnRange[0].length === lookupRange[0].length
19171
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
19177
+ ? _returnRange[0].length === _lookupRange[0].length
19178
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
19172
19179
  if (searchKey && isEvaluationError(searchKey.value)) {
19173
19180
  return [[searchKey]];
19174
19181
  }
19175
19182
  const getElement = lookupDirection === "col"
19176
19183
  ? (range, index) => range[0][index].value
19177
19184
  : (range, index) => range[index][0].value;
19178
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
19185
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
19179
19186
  const mode = MATCH_MODE[_matchMode];
19180
19187
  const reverseSearch = _searchMode === -1;
19181
19188
  const index = _searchMode === 2 || _searchMode === -2
19182
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
19183
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
19189
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
19190
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
19184
19191
  if (index !== -1) {
19185
19192
  return lookupDirection === "col"
19186
- ? returnRange.map((col) => [col[index]])
19187
- : [returnRange[index]];
19193
+ ? _returnRange.map((col) => [col[index]])
19194
+ : [_returnRange[index]];
19188
19195
  }
19189
19196
  if (defaultValue === undefined) {
19190
19197
  return valueNotAvailable(searchKey);
@@ -41218,11 +41225,10 @@ class GridRenderer {
41218
41225
  switch (layer) {
41219
41226
  case "Background":
41220
41227
  this.drawGlobalBackground(renderingContext);
41221
- for (const zone of this.getters.getAllActiveViewportsZones()) {
41228
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
41222
41229
  const { ctx } = renderingContext;
41223
41230
  ctx.save();
41224
41231
  ctx.beginPath();
41225
- const rect = this.getters.getVisibleRect(zone);
41226
41232
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
41227
41233
  ctx.clip();
41228
41234
  const boxes = this.getGridBoxes(zone);
@@ -41492,10 +41498,8 @@ class GridRenderer {
41492
41498
  const { ctx, thinLineWidth } = renderingContext;
41493
41499
  const visibleCols = this.getters.getSheetViewVisibleCols();
41494
41500
  const left = visibleCols[0];
41495
- const right = visibleCols[visibleCols.length - 1];
41496
41501
  const visibleRows = this.getters.getSheetViewVisibleRows();
41497
41502
  const top = visibleRows[0];
41498
- const bottom = visibleRows[visibleRows.length - 1];
41499
41503
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
41500
41504
  const selection = this.getters.getSelectedZones();
41501
41505
  const selectedCols = getZonesCols(selection);
@@ -41511,7 +41515,7 @@ class GridRenderer {
41511
41515
  ctx.lineWidth = thinLineWidth;
41512
41516
  ctx.strokeStyle = "#333";
41513
41517
  // Columns headers background
41514
- for (let col = left; col <= right; col++) {
41518
+ for (const col of visibleCols) {
41515
41519
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
41516
41520
  const { x, width } = this.getters.getVisibleRect(colZone);
41517
41521
  const isColActive = activeCols.has(col);
@@ -41528,7 +41532,7 @@ class GridRenderer {
41528
41532
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
41529
41533
  }
41530
41534
  // Rows headers background
41531
- for (let row = top; row <= bottom; row++) {
41535
+ for (const row of visibleRows) {
41532
41536
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
41533
41537
  const { y, height } = this.getters.getVisibleRect(rowZone);
41534
41538
  const isRowActive = activeRows.has(row);
@@ -41554,21 +41558,21 @@ class GridRenderer {
41554
41558
  ctx.stroke();
41555
41559
  ctx.beginPath();
41556
41560
  // column text + separator
41557
- for (const i of visibleCols) {
41558
- const colSize = this.getters.getColSize(sheetId, i);
41559
- const colName = numberToLetters(i);
41560
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
41561
- let colStart = this.getHeaderOffset("COL", left, i);
41561
+ for (const col of visibleCols) {
41562
+ const colSize = this.getters.getColSize(sheetId, col);
41563
+ const colName = numberToLetters(col);
41564
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
41565
+ let colStart = this.getHeaderOffset("COL", left, col);
41562
41566
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
41563
41567
  ctx.moveTo(colStart + colSize, 0);
41564
41568
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
41565
41569
  }
41566
41570
  // row text + separator
41567
- for (const i of visibleRows) {
41568
- const rowSize = this.getters.getRowSize(sheetId, i);
41569
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
41570
- let rowStart = this.getHeaderOffset("ROW", top, i);
41571
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
41571
+ for (const row of visibleRows) {
41572
+ const rowSize = this.getters.getRowSize(sheetId, row);
41573
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
41574
+ let rowStart = this.getHeaderOffset("ROW", top, row);
41575
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
41572
41576
  ctx.moveTo(0, rowStart + rowSize);
41573
41577
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
41574
41578
  }
@@ -41866,6 +41870,9 @@ function useGridDrawing(refName, model, canvasSize) {
41866
41870
  canvas.width = width * dpr;
41867
41871
  canvas.height = height * dpr;
41868
41872
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
41873
+ if (width === 0 || height === 0) {
41874
+ return;
41875
+ }
41869
41876
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
41870
41877
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
41871
41878
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -47598,7 +47605,7 @@ class BordersPlugin extends CorePlugin {
47598
47605
  getCommonSides(border1, border2) {
47599
47606
  const commonBorder = {};
47600
47607
  for (let side of ["top", "bottom", "left", "right"]) {
47601
- if (border1[side] && border1[side] === border2[side]) {
47608
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
47602
47609
  commonBorder[side] = border1[side];
47603
47610
  }
47604
47611
  }
@@ -60941,8 +60948,17 @@ class InternalViewport {
60941
60948
  this.getters = getters;
60942
60949
  this.sheetId = sheetId;
60943
60950
  this.boundaries = boundaries;
60944
- this.viewportWidth = sizeInGrid.width;
60945
- this.viewportHeight = sizeInGrid.height;
60951
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
60952
+ throw new Error("Viewport size cannot be negative");
60953
+ }
60954
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
60955
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
60956
+ this.top = boundaries.top;
60957
+ this.bottom = boundaries.bottom;
60958
+ this.left = boundaries.left;
60959
+ this.right = boundaries.right;
60960
+ this.offsetX = offsets.x;
60961
+ this.offsetY = offsets.y;
60946
60962
  this.offsetScrollbarX = offsets.x;
60947
60963
  this.offsetScrollbarY = offsets.y;
60948
60964
  this.canScrollVertically = options.canScrollVertically;
@@ -60985,9 +61001,9 @@ class InternalViewport {
60985
61001
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
60986
61002
  );
60987
61003
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
60988
- }
60989
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
60990
- height += FOOTER_HEIGHT;
61004
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
61005
+ height += FOOTER_HEIGHT;
61006
+ }
60991
61007
  }
60992
61008
  return { width, height };
60993
61009
  }
@@ -61128,6 +61144,9 @@ class InternalViewport {
61128
61144
  !this.getters.isRowHidden(this.sheetId, row));
61129
61145
  }
61130
61146
  searchHeaderIndex(dimension, position, startIndex = 0) {
61147
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
61148
+ return -1;
61149
+ }
61131
61150
  const sheetId = this.sheetId;
61132
61151
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
61133
61152
  // using a binary search:
@@ -61164,7 +61183,7 @@ class InternalViewport {
61164
61183
  this.adjustViewportZoneY();
61165
61184
  }
61166
61185
  /** Corrects the viewport's horizontal offset based on the current structure
61167
- * To make sure that at least on column is visible inside the viewport.
61186
+ * To make sure that at least one column is visible inside the viewport.
61168
61187
  */
61169
61188
  adjustViewportOffsetX() {
61170
61189
  if (this.canScrollHorizontally) {
@@ -61176,7 +61195,7 @@ class InternalViewport {
61176
61195
  this.adjustViewportZoneX();
61177
61196
  }
61178
61197
  /** Corrects the viewport's vertical offset based on the current structure
61179
- * To make sure that at least on row is visible inside the viewport.
61198
+ * To make sure that at least one row is visible inside the viewport.
61180
61199
  */
61181
61200
  adjustViewportOffsetY() {
61182
61201
  if (this.canScrollVertically) {
@@ -61193,11 +61212,14 @@ class InternalViewport {
61193
61212
  const sheetId = this.sheetId;
61194
61213
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
61195
61214
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
61215
+ if (!this.viewportWidth) {
61216
+ return;
61217
+ }
61196
61218
  if (this.left === -1) {
61197
61219
  this.left = this.boundaries.left;
61198
61220
  }
61199
61221
  if (this.right === -1) {
61200
- this.right = this.getters.getNumberCols(sheetId) - 1;
61222
+ this.right = this.boundaries.right;
61201
61223
  }
61202
61224
  this.offsetX =
61203
61225
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -61209,11 +61231,14 @@ class InternalViewport {
61209
61231
  const sheetId = this.sheetId;
61210
61232
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
61211
61233
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
61234
+ if (!this.viewportHeight) {
61235
+ return;
61236
+ }
61212
61237
  if (this.top === -1) {
61213
61238
  this.top = this.boundaries.top;
61214
61239
  }
61215
61240
  if (this.bottom === -1) {
61216
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
61241
+ this.bottom = this.boundaries.bottom;
61217
61242
  }
61218
61243
  this.offsetY =
61219
61244
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -61287,7 +61312,7 @@ class SheetViewPlugin extends UIPlugin {
61287
61312
  "isPositionVisible",
61288
61313
  "getColDimensionsInViewport",
61289
61314
  "getRowDimensionsInViewport",
61290
- "getAllActiveViewportsZones",
61315
+ "getAllActiveViewportsZonesAndRect",
61291
61316
  "getRect",
61292
61317
  ];
61293
61318
  viewports = {};
@@ -61520,12 +61545,12 @@ class SheetViewPlugin extends UIPlugin {
61520
61545
  const sheetId = this.getters.getActiveSheetId();
61521
61546
  const viewports = this.getSubViewports(sheetId);
61522
61547
  //TODO ake another commit to eimprove this
61523
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
61548
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
61524
61549
  }
61525
61550
  getSheetViewVisibleRows() {
61526
61551
  const sheetId = this.getters.getActiveSheetId();
61527
61552
  const viewports = this.getSubViewports(sheetId);
61528
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
61553
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
61529
61554
  }
61530
61555
  /**
61531
61556
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -61568,19 +61593,19 @@ class SheetViewPlugin extends UIPlugin {
61568
61593
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
61569
61594
  };
61570
61595
  }
61571
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
61572
- const sheetId = this.getters.getActiveSheetId();
61573
- const visibleCols = this.getters.getSheetViewVisibleCols();
61574
- const visibleRows = this.getters.getSheetViewVisibleRows();
61575
- if (index < referenceIndex) {
61576
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
61596
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
61597
+ if (targetHeaderIndex < referenceHeaderIndex) {
61598
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
61577
61599
  }
61600
+ const sheetId = this.getters.getActiveSheetId();
61601
+ const visibleHeaders = dimension === "COL"
61602
+ ? this.getters.getSheetViewVisibleCols()
61603
+ : this.getters.getSheetViewVisibleRows();
61604
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
61605
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
61606
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
61578
61607
  let offset = 0;
61579
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
61580
- for (let i = referenceIndex; i < index; i++) {
61581
- if (!visibleIndexes.includes(i)) {
61582
- continue;
61583
- }
61608
+ for (const i of relevantIndexes) {
61584
61609
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
61585
61610
  }
61586
61611
  return offset;
@@ -61627,7 +61652,7 @@ class SheetViewPlugin extends UIPlugin {
61627
61652
  }
61628
61653
  return { canEdgeScroll, direction, delay };
61629
61654
  }
61630
- getEdgeScrollRow(y, previousY, tartingY) {
61655
+ getEdgeScrollRow(y, previousY, startingY) {
61631
61656
  let canEdgeScroll = false;
61632
61657
  let direction = 0;
61633
61658
  let delay = 0;
@@ -61648,7 +61673,7 @@ class SheetViewPlugin extends UIPlugin {
61648
61673
  delay = scrollDelay(y - height);
61649
61674
  direction = 1;
61650
61675
  }
61651
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
61676
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
61652
61677
  // 2
61653
61678
  canEdgeScroll = true;
61654
61679
  delay = scrollDelay(offsetCorrectionY - y);
@@ -61674,13 +61699,7 @@ class SheetViewPlugin extends UIPlugin {
61674
61699
  */
61675
61700
  getVisibleRectWithoutHeaders(zone) {
61676
61701
  const sheetId = this.getters.getActiveSheetId();
61677
- const viewportRects = this.getSubViewports(sheetId)
61678
- .map((viewport) => viewport.getVisibleRect(zone))
61679
- .filter(isDefined);
61680
- if (viewportRects.length === 0) {
61681
- return { x: 0, y: 0, width: 0, height: 0 };
61682
- }
61683
- return this.recomposeRect(viewportRects);
61702
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
61684
61703
  }
61685
61704
  /**
61686
61705
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -61688,13 +61707,7 @@ class SheetViewPlugin extends UIPlugin {
61688
61707
  */
61689
61708
  getRect(zone) {
61690
61709
  const sheetId = this.getters.getActiveSheetId();
61691
- const viewportRects = this.getSubViewports(sheetId)
61692
- .map((viewport) => viewport.getFullRect(zone))
61693
- .filter(isDefined);
61694
- if (viewportRects.length === 0) {
61695
- return { x: 0, y: 0, width: 0, height: 0 };
61696
- }
61697
- const rect = this.recomposeRect(viewportRects);
61710
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
61698
61711
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
61699
61712
  }
61700
61713
  /**
@@ -61739,9 +61752,18 @@ class SheetViewPlugin extends UIPlugin {
61739
61752
  end: start + (isRowHidden ? 0 : size),
61740
61753
  };
61741
61754
  }
61742
- getAllActiveViewportsZones() {
61755
+ getAllActiveViewportsZonesAndRect() {
61743
61756
  const sheetId = this.getters.getActiveSheetId();
61744
- return this.getSubViewports(sheetId);
61757
+ return this.getSubViewports(sheetId).map((viewport) => {
61758
+ return {
61759
+ zone: viewport,
61760
+ rect: {
61761
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
61762
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
61763
+ ...viewport.getMaxSize(),
61764
+ },
61765
+ };
61766
+ });
61745
61767
  }
61746
61768
  // ---------------------------------------------------------------------------
61747
61769
  // Private
@@ -61800,12 +61822,11 @@ class SheetViewPlugin extends UIPlugin {
61800
61822
  }
61801
61823
  /** gets rid of deprecated sheetIds */
61802
61824
  cleanViewports() {
61803
- const sheetIds = this.getters.getSheetIds();
61804
- for (let sheetId of Object.keys(this.viewports)) {
61805
- if (!sheetIds.includes(sheetId)) {
61806
- delete this.viewports[sheetId];
61807
- }
61825
+ const newViewport = {};
61826
+ for (const sheetId of this.getters.getSheetIds()) {
61827
+ newViewport[sheetId] = this.viewports[sheetId];
61808
61828
  }
61829
+ this.viewports = newViewport;
61809
61830
  }
61810
61831
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
61811
61832
  this.sheetViewHeight = height;
@@ -61815,7 +61836,7 @@ class SheetViewPlugin extends UIPlugin {
61815
61836
  this.recomputeViewports();
61816
61837
  }
61817
61838
  recomputeViewports() {
61818
- for (let sheetId of Object.keys(this.viewports)) {
61839
+ for (const sheetId of this.getters.getSheetIds()) {
61819
61840
  this.resetViewports(sheetId);
61820
61841
  }
61821
61842
  }
@@ -61837,8 +61858,10 @@ class SheetViewPlugin extends UIPlugin {
61837
61858
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
61838
61859
  const nCols = this.getters.getNumberCols(sheetId);
61839
61860
  const nRows = this.getters.getNumberRows(sheetId);
61840
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
61841
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
61861
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
61862
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
61863
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
61864
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
61842
61865
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
61843
61866
  const canScrollHorizontally = xRatio < 1.0;
61844
61867
  const canScrollVertically = yRatio < 1.0;
@@ -61849,14 +61872,14 @@ class SheetViewPlugin extends UIPlugin {
61849
61872
  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 })) ||
61850
61873
  undefined,
61851
61874
  topRight: (ySplit &&
61852
- 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 })) ||
61875
+ 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 })) ||
61853
61876
  undefined,
61854
61877
  bottomLeft: (xSplit &&
61855
- 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 })) ||
61878
+ 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 })) ||
61856
61879
  undefined,
61857
61880
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
61858
- width: this.sheetViewWidth - colOffset,
61859
- height: this.sheetViewHeight - rowOffset,
61881
+ width: unfrozenWidth,
61882
+ height: unfrozenHeight,
61860
61883
  }, { canScrollHorizontally, canScrollVertically }, {
61861
61884
  x: canScrollHorizontally ? previousOffset.x : 0,
61862
61885
  y: canScrollVertically ? previousOffset.y : 0,
@@ -61933,12 +61956,26 @@ class SheetViewPlugin extends UIPlugin {
61933
61956
  const height = this.sheetViewHeight + this.gridOffsetY;
61934
61957
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
61935
61958
  }
61936
- recomposeRect(viewportRects) {
61937
- const x = Math.min(...viewportRects.map((rect) => rect.x));
61938
- const y = Math.min(...viewportRects.map((rect) => rect.y));
61939
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
61940
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
61941
- return { x, y, width, height };
61959
+ mapViewportsToRect(sheetId, rectCallBack) {
61960
+ let x = Infinity;
61961
+ let y = Infinity;
61962
+ let width = 0;
61963
+ let height = 0;
61964
+ let hasViewports = false;
61965
+ for (const viewport of this.getSubViewports(sheetId)) {
61966
+ const rect = rectCallBack(viewport);
61967
+ if (rect) {
61968
+ hasViewports = true;
61969
+ x = Math.min(x, rect.x);
61970
+ y = Math.min(y, rect.y);
61971
+ width = Math.max(width, rect.x + rect.width);
61972
+ height = Math.max(height, rect.y + rect.height);
61973
+ }
61974
+ }
61975
+ if (!hasViewports) {
61976
+ return { x: 0, y: 0, width: 0, height: 0 };
61977
+ }
61978
+ return { x, y, width: width - x, height: height - y };
61942
61979
  }
61943
61980
  }
61944
61981
 
@@ -68828,6 +68865,6 @@ const constants = {
68828
68865
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, 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 };
68829
68866
 
68830
68867
 
68831
- __info__.version = "17.4.20";
68832
- __info__.date = "2025-01-31T08:00:07.103Z";
68833
- __info__.hash = "54a344a";
68868
+ __info__.version = "17.4.21";
68869
+ __info__.date = "2025-02-05T06:46:54.366Z";
68870
+ __info__.hash = "0c55e18";