@odoo/o-spreadsheet 17.4.20 → 17.4.22

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.22
6
+ * @date 2025-02-10T09:14:48.889Z
7
+ * @hash 667f2b1
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -747,9 +747,16 @@
747
747
  }
748
748
  return true;
749
749
  }
750
- /** Check if the given array contains all the values of the other array. */
750
+ /**
751
+ * Check if the given array contains all the values of the other array.
752
+ * It makes the assumption that both array do not contain duplicates.
753
+ */
751
754
  function includesAll(arr, values) {
752
- return values.every((value) => arr.includes(value));
755
+ if (arr.length < values.length) {
756
+ return false;
757
+ }
758
+ const set = new Set(arr);
759
+ return values.every((value) => set.has(value));
753
760
  }
754
761
  /**
755
762
  * Return an object with all the keys in the object that have a falsy value removed.
@@ -18882,22 +18889,23 @@ stores.inject(MyMetaStore, storeInstance);
18882
18889
  description: _t("Horizontal lookup"),
18883
18890
  args: [
18884
18891
  arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18885
- 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.")),
18892
+ 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.")),
18886
18893
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18887
18894
  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.")),
18888
18895
  ],
18889
18896
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18890
18897
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18891
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18898
+ const _range = toMatrix(range);
18899
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18892
18900
  if (searchKey && isEvaluationError(searchKey.value)) {
18893
18901
  return searchKey;
18894
18902
  }
18895
18903
  const getValueFromRange = (range, index) => range[index][0].value;
18896
18904
  const _isSorted = toBoolean(isSorted.value);
18897
18905
  const colIndex = _isSorted
18898
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18899
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18900
- const col = range[colIndex];
18906
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18907
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18908
+ const col = _range[colIndex];
18901
18909
  if (col === undefined) {
18902
18910
  return valueNotAvailable(searchKey);
18903
18911
  }
@@ -18996,35 +19004,37 @@ stores.inject(MyMetaStore, storeInstance);
18996
19004
  description: _t("Look up a value."),
18997
19005
  args: [
18998
19006
  arg("search_key (any)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18999
- 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.")),
19000
- 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.")),
19007
+ 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.")),
19008
+ 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.")),
19001
19009
  ],
19002
19010
  compute: function (searchKey, searchArray, resultRange) {
19003
- let nbCol = searchArray.length;
19004
- let nbRow = searchArray[0].length;
19011
+ const _searchArray = toMatrix(searchArray);
19012
+ const _resultRange = toMatrix(resultRange);
19013
+ let nbCol = _searchArray.length;
19014
+ let nbRow = _searchArray[0].length;
19005
19015
  const verticalSearch = nbRow >= nbCol;
19006
19016
  const getElement = verticalSearch
19007
19017
  ? (range, index) => range[0][index].value
19008
19018
  : (range, index) => range[index][0].value;
19009
19019
  const rangeLength = verticalSearch ? nbRow : nbCol;
19010
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
19020
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
19011
19021
  if (index === -1 ||
19012
- (verticalSearch && searchArray[0][index] === undefined) ||
19013
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
19022
+ (verticalSearch && _searchArray[0][index] === undefined) ||
19023
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
19014
19024
  return valueNotAvailable(searchKey);
19015
19025
  }
19016
- if (resultRange === undefined) {
19017
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
19026
+ if (_resultRange[0].length === 0) {
19027
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
19018
19028
  }
19019
- nbCol = resultRange.length;
19020
- nbRow = resultRange[0].length;
19029
+ nbCol = _resultRange.length;
19030
+ nbRow = _resultRange[0].length;
19021
19031
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
19022
19032
  if (nbCol > 1) {
19023
19033
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
19024
- return resultRange[index][0];
19034
+ return _resultRange[index][0];
19025
19035
  }
19026
19036
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
19027
- return resultRange[0][index];
19037
+ return _resultRange[0][index];
19028
19038
  },
19029
19039
  isExported: true,
19030
19040
  };
@@ -19041,28 +19051,29 @@ stores.inject(MyMetaStore, storeInstance);
19041
19051
  ],
19042
19052
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
19043
19053
  let _searchType = toNumber(searchType, this.locale);
19044
- const nbCol = range.length;
19045
- const nbRow = range[0].length;
19054
+ const _range = toMatrix(range);
19055
+ const nbCol = _range.length;
19056
+ const nbRow = _range[0].length;
19046
19057
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
19047
19058
  let index = -1;
19048
19059
  const getElement = nbCol === 1
19049
- ? (range, index) => range[0][index].value
19050
- : (range, index) => range[index][0].value;
19051
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
19060
+ ? (_range, index) => _range[0][index].value
19061
+ : (_range, index) => _range[index][0].value;
19062
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
19052
19063
  _searchType = Math.sign(_searchType);
19053
19064
  switch (_searchType) {
19054
19065
  case 1:
19055
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
19066
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
19056
19067
  break;
19057
19068
  case 0:
19058
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
19069
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
19059
19070
  break;
19060
19071
  case -1:
19061
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
19072
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
19062
19073
  break;
19063
19074
  }
19064
- if ((nbCol === 1 && range[0][index] === undefined) ||
19065
- (nbCol !== 1 && range[index] === undefined)) {
19075
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
19076
+ (nbCol !== 1 && _range[index] === undefined)) {
19066
19077
  return valueNotAvailable(searchKey);
19067
19078
  }
19068
19079
  return index + 1;
@@ -19116,16 +19127,17 @@ stores.inject(MyMetaStore, storeInstance);
19116
19127
  ],
19117
19128
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
19118
19129
  const _index = Math.trunc(toNumber(index?.value, this.locale));
19119
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
19130
+ const _range = toMatrix(range);
19131
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
19120
19132
  if (searchKey && isEvaluationError(searchKey.value)) {
19121
19133
  return searchKey;
19122
19134
  }
19123
19135
  const getValueFromRange = (range, index) => range[0][index].value;
19124
19136
  const _isSorted = toBoolean(isSorted.value);
19125
19137
  const rowIndex = _isSorted
19126
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
19127
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
19128
- const value = range[_index - 1][rowIndex];
19138
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
19139
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
19140
+ const value = _range[_index - 1][rowIndex];
19129
19141
  if (value === undefined) {
19130
19142
  return valueNotAvailable(searchKey);
19131
19143
  }
@@ -19162,30 +19174,32 @@ stores.inject(MyMetaStore, storeInstance);
19162
19174
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
19163
19175
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
19164
19176
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
19165
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
19177
+ const _lookupRange = toMatrix(lookupRange);
19178
+ const _returnRange = toMatrix(returnRange);
19179
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
19166
19180
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
19167
19181
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
19168
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
19182
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
19169
19183
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
19170
19184
  assert(() => lookupDirection === "col"
19171
- ? returnRange[0].length === lookupRange[0].length
19172
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
19185
+ ? _returnRange[0].length === _lookupRange[0].length
19186
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
19173
19187
  if (searchKey && isEvaluationError(searchKey.value)) {
19174
19188
  return [[searchKey]];
19175
19189
  }
19176
19190
  const getElement = lookupDirection === "col"
19177
19191
  ? (range, index) => range[0][index].value
19178
19192
  : (range, index) => range[index][0].value;
19179
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
19193
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
19180
19194
  const mode = MATCH_MODE[_matchMode];
19181
19195
  const reverseSearch = _searchMode === -1;
19182
19196
  const index = _searchMode === 2 || _searchMode === -2
19183
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
19184
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
19197
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
19198
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
19185
19199
  if (index !== -1) {
19186
19200
  return lookupDirection === "col"
19187
- ? returnRange.map((col) => [col[index]])
19188
- : [returnRange[index]];
19201
+ ? _returnRange.map((col) => [col[index]])
19202
+ : [_returnRange[index]];
19189
19203
  }
19190
19204
  if (defaultValue === undefined) {
19191
19205
  return valueNotAvailable(searchKey);
@@ -33539,8 +33553,8 @@ stores.inject(MyMetaStore, storeInstance);
33539
33553
  document.body.style.cursor = "move";
33540
33554
  state.draggedItemId = args.draggedItemId;
33541
33555
  const container = direction === "horizontal"
33542
- ? new HorizontalContainer(args.containerEl)
33543
- : new VerticalContainer(args.containerEl);
33556
+ ? new HorizontalContainer(args.scrollableContainerEl)
33557
+ : new VerticalContainer(args.scrollableContainerEl);
33544
33558
  dndHelper = new DOMDndHelper({
33545
33559
  ...args,
33546
33560
  container,
@@ -33551,8 +33565,8 @@ stores.inject(MyMetaStore, storeInstance);
33551
33565
  const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
33552
33566
  cleanupFns.push(stopListening);
33553
33567
  const onScroll = dndHelper.onScroll.bind(dndHelper);
33554
- args.containerEl.addEventListener("scroll", onScroll);
33555
- cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
33568
+ args.scrollableContainerEl.addEventListener("scroll", onScroll);
33569
+ cleanupFns.push(() => args.scrollableContainerEl.removeEventListener("scroll", onScroll));
33556
33570
  cleanupFns.push(dndHelper.destroy.bind(dndHelper));
33557
33571
  };
33558
33572
  owl.onWillUnmount(() => {
@@ -34000,7 +34014,7 @@ stores.inject(MyMetaStore, storeInstance);
34000
34014
  draggedItemId: cf.id,
34001
34015
  initialMousePosition: event.clientY,
34002
34016
  items: items,
34003
- containerEl: this.cfListRef.el,
34017
+ scrollableContainerEl: this.cfListRef.el,
34004
34018
  onDragEnd: (cfId, finalIndex) => this.onDragEnd(cfId, finalIndex),
34005
34019
  });
34006
34020
  }
@@ -36012,6 +36026,7 @@ stores.inject(MyMetaStore, storeInstance);
36012
36026
  unusedMeasureFields: Array,
36013
36027
  unusedDateTimeGranularities: Object,
36014
36028
  allGranularities: Array,
36029
+ getScrollableContainerEl: { type: Function, optional: true },
36015
36030
  };
36016
36031
  dimensionsRef = owl.useRef("pivot-dimensions");
36017
36032
  dragAndDrop = useDragAndDropListItems();
@@ -36040,7 +36055,7 @@ stores.inject(MyMetaStore, storeInstance);
36040
36055
  draggedItemId: dimension.nameWithGranularity,
36041
36056
  initialMousePosition: event.clientY,
36042
36057
  items: draggableItems,
36043
- containerEl: this.dimensionsRef.el,
36058
+ scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
36044
36059
  onDragEnd: (dimensionName, finalIndex) => {
36045
36060
  const originalIndex = draggableIds.findIndex((id) => id === dimensionName);
36046
36061
  if (originalIndex === finalIndex) {
@@ -36080,7 +36095,7 @@ stores.inject(MyMetaStore, storeInstance);
36080
36095
  draggedItemId: measure.name,
36081
36096
  initialMousePosition: event.clientY,
36082
36097
  items: draggableItems,
36083
- containerEl: this.dimensionsRef.el,
36098
+ scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
36084
36099
  onDragEnd: (measureName, finalIndex) => {
36085
36100
  const originalIndex = draggableIds.findIndex((id) => id === measureName);
36086
36101
  if (originalIndex === finalIndex) {
@@ -37519,6 +37534,7 @@ stores.inject(MyMetaStore, storeInstance);
37519
37534
  };
37520
37535
  store;
37521
37536
  state;
37537
+ pivotSidePanelRef = owl.useRef("pivotSidePanel");
37522
37538
  setup() {
37523
37539
  this.store = useLocalStore(PivotSidePanelStore, this.props.pivotId);
37524
37540
  this.state = owl.useState({
@@ -37547,6 +37563,9 @@ stores.inject(MyMetaStore, storeInstance);
37547
37563
  get definition() {
37548
37564
  return this.store.definition;
37549
37565
  }
37566
+ getScrollableContainerEl() {
37567
+ return this.pivotSidePanelRef.el;
37568
+ }
37550
37569
  onSelectionChanged(ranges) {
37551
37570
  this.state.rangeHasChanged = true;
37552
37571
  this.state.range = ranges[0];
@@ -41219,11 +41238,10 @@ stores.inject(MyMetaStore, storeInstance);
41219
41238
  switch (layer) {
41220
41239
  case "Background":
41221
41240
  this.drawGlobalBackground(renderingContext);
41222
- for (const zone of this.getters.getAllActiveViewportsZones()) {
41241
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
41223
41242
  const { ctx } = renderingContext;
41224
41243
  ctx.save();
41225
41244
  ctx.beginPath();
41226
- const rect = this.getters.getVisibleRect(zone);
41227
41245
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
41228
41246
  ctx.clip();
41229
41247
  const boxes = this.getGridBoxes(zone);
@@ -41493,10 +41511,8 @@ stores.inject(MyMetaStore, storeInstance);
41493
41511
  const { ctx, thinLineWidth } = renderingContext;
41494
41512
  const visibleCols = this.getters.getSheetViewVisibleCols();
41495
41513
  const left = visibleCols[0];
41496
- const right = visibleCols[visibleCols.length - 1];
41497
41514
  const visibleRows = this.getters.getSheetViewVisibleRows();
41498
41515
  const top = visibleRows[0];
41499
- const bottom = visibleRows[visibleRows.length - 1];
41500
41516
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
41501
41517
  const selection = this.getters.getSelectedZones();
41502
41518
  const selectedCols = getZonesCols(selection);
@@ -41512,7 +41528,7 @@ stores.inject(MyMetaStore, storeInstance);
41512
41528
  ctx.lineWidth = thinLineWidth;
41513
41529
  ctx.strokeStyle = "#333";
41514
41530
  // Columns headers background
41515
- for (let col = left; col <= right; col++) {
41531
+ for (const col of visibleCols) {
41516
41532
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
41517
41533
  const { x, width } = this.getters.getVisibleRect(colZone);
41518
41534
  const isColActive = activeCols.has(col);
@@ -41529,7 +41545,7 @@ stores.inject(MyMetaStore, storeInstance);
41529
41545
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
41530
41546
  }
41531
41547
  // Rows headers background
41532
- for (let row = top; row <= bottom; row++) {
41548
+ for (const row of visibleRows) {
41533
41549
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
41534
41550
  const { y, height } = this.getters.getVisibleRect(rowZone);
41535
41551
  const isRowActive = activeRows.has(row);
@@ -41555,21 +41571,21 @@ stores.inject(MyMetaStore, storeInstance);
41555
41571
  ctx.stroke();
41556
41572
  ctx.beginPath();
41557
41573
  // column text + separator
41558
- for (const i of visibleCols) {
41559
- const colSize = this.getters.getColSize(sheetId, i);
41560
- const colName = numberToLetters(i);
41561
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
41562
- let colStart = this.getHeaderOffset("COL", left, i);
41574
+ for (const col of visibleCols) {
41575
+ const colSize = this.getters.getColSize(sheetId, col);
41576
+ const colName = numberToLetters(col);
41577
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
41578
+ let colStart = this.getHeaderOffset("COL", left, col);
41563
41579
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
41564
41580
  ctx.moveTo(colStart + colSize, 0);
41565
41581
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
41566
41582
  }
41567
41583
  // row text + separator
41568
- for (const i of visibleRows) {
41569
- const rowSize = this.getters.getRowSize(sheetId, i);
41570
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
41571
- let rowStart = this.getHeaderOffset("ROW", top, i);
41572
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
41584
+ for (const row of visibleRows) {
41585
+ const rowSize = this.getters.getRowSize(sheetId, row);
41586
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
41587
+ let rowStart = this.getHeaderOffset("ROW", top, row);
41588
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
41573
41589
  ctx.moveTo(0, rowStart + rowSize);
41574
41590
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
41575
41591
  }
@@ -41867,6 +41883,9 @@ stores.inject(MyMetaStore, storeInstance);
41867
41883
  canvas.width = width * dpr;
41868
41884
  canvas.height = height * dpr;
41869
41885
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
41886
+ if (width === 0 || height === 0) {
41887
+ return;
41888
+ }
41870
41889
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
41871
41890
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
41872
41891
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -47599,7 +47618,7 @@ stores.inject(MyMetaStore, storeInstance);
47599
47618
  getCommonSides(border1, border2) {
47600
47619
  const commonBorder = {};
47601
47620
  for (let side of ["top", "bottom", "left", "right"]) {
47602
- if (border1[side] && border1[side] === border2[side]) {
47621
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
47603
47622
  commonBorder[side] = border1[side];
47604
47623
  }
47605
47624
  }
@@ -60942,8 +60961,17 @@ stores.inject(MyMetaStore, storeInstance);
60942
60961
  this.getters = getters;
60943
60962
  this.sheetId = sheetId;
60944
60963
  this.boundaries = boundaries;
60945
- this.viewportWidth = sizeInGrid.width;
60946
- this.viewportHeight = sizeInGrid.height;
60964
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
60965
+ throw new Error("Viewport size cannot be negative");
60966
+ }
60967
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
60968
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
60969
+ this.top = boundaries.top;
60970
+ this.bottom = boundaries.bottom;
60971
+ this.left = boundaries.left;
60972
+ this.right = boundaries.right;
60973
+ this.offsetX = offsets.x;
60974
+ this.offsetY = offsets.y;
60947
60975
  this.offsetScrollbarX = offsets.x;
60948
60976
  this.offsetScrollbarY = offsets.y;
60949
60977
  this.canScrollVertically = options.canScrollVertically;
@@ -60986,9 +61014,9 @@ stores.inject(MyMetaStore, storeInstance);
60986
61014
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
60987
61015
  );
60988
61016
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
60989
- }
60990
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
60991
- height += FOOTER_HEIGHT;
61017
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
61018
+ height += FOOTER_HEIGHT;
61019
+ }
60992
61020
  }
60993
61021
  return { width, height };
60994
61022
  }
@@ -61129,6 +61157,9 @@ stores.inject(MyMetaStore, storeInstance);
61129
61157
  !this.getters.isRowHidden(this.sheetId, row));
61130
61158
  }
61131
61159
  searchHeaderIndex(dimension, position, startIndex = 0) {
61160
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
61161
+ return -1;
61162
+ }
61132
61163
  const sheetId = this.sheetId;
61133
61164
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
61134
61165
  // using a binary search:
@@ -61165,7 +61196,7 @@ stores.inject(MyMetaStore, storeInstance);
61165
61196
  this.adjustViewportZoneY();
61166
61197
  }
61167
61198
  /** Corrects the viewport's horizontal offset based on the current structure
61168
- * To make sure that at least on column is visible inside the viewport.
61199
+ * To make sure that at least one column is visible inside the viewport.
61169
61200
  */
61170
61201
  adjustViewportOffsetX() {
61171
61202
  if (this.canScrollHorizontally) {
@@ -61177,7 +61208,7 @@ stores.inject(MyMetaStore, storeInstance);
61177
61208
  this.adjustViewportZoneX();
61178
61209
  }
61179
61210
  /** Corrects the viewport's vertical offset based on the current structure
61180
- * To make sure that at least on row is visible inside the viewport.
61211
+ * To make sure that at least one row is visible inside the viewport.
61181
61212
  */
61182
61213
  adjustViewportOffsetY() {
61183
61214
  if (this.canScrollVertically) {
@@ -61194,11 +61225,14 @@ stores.inject(MyMetaStore, storeInstance);
61194
61225
  const sheetId = this.sheetId;
61195
61226
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
61196
61227
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
61228
+ if (!this.viewportWidth) {
61229
+ return;
61230
+ }
61197
61231
  if (this.left === -1) {
61198
61232
  this.left = this.boundaries.left;
61199
61233
  }
61200
61234
  if (this.right === -1) {
61201
- this.right = this.getters.getNumberCols(sheetId) - 1;
61235
+ this.right = this.boundaries.right;
61202
61236
  }
61203
61237
  this.offsetX =
61204
61238
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -61210,11 +61244,14 @@ stores.inject(MyMetaStore, storeInstance);
61210
61244
  const sheetId = this.sheetId;
61211
61245
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
61212
61246
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
61247
+ if (!this.viewportHeight) {
61248
+ return;
61249
+ }
61213
61250
  if (this.top === -1) {
61214
61251
  this.top = this.boundaries.top;
61215
61252
  }
61216
61253
  if (this.bottom === -1) {
61217
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
61254
+ this.bottom = this.boundaries.bottom;
61218
61255
  }
61219
61256
  this.offsetY =
61220
61257
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -61288,7 +61325,7 @@ stores.inject(MyMetaStore, storeInstance);
61288
61325
  "isPositionVisible",
61289
61326
  "getColDimensionsInViewport",
61290
61327
  "getRowDimensionsInViewport",
61291
- "getAllActiveViewportsZones",
61328
+ "getAllActiveViewportsZonesAndRect",
61292
61329
  "getRect",
61293
61330
  ];
61294
61331
  viewports = {};
@@ -61521,12 +61558,12 @@ stores.inject(MyMetaStore, storeInstance);
61521
61558
  const sheetId = this.getters.getActiveSheetId();
61522
61559
  const viewports = this.getSubViewports(sheetId);
61523
61560
  //TODO ake another commit to eimprove this
61524
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
61561
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
61525
61562
  }
61526
61563
  getSheetViewVisibleRows() {
61527
61564
  const sheetId = this.getters.getActiveSheetId();
61528
61565
  const viewports = this.getSubViewports(sheetId);
61529
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
61566
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
61530
61567
  }
61531
61568
  /**
61532
61569
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -61569,19 +61606,19 @@ stores.inject(MyMetaStore, storeInstance);
61569
61606
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
61570
61607
  };
61571
61608
  }
61572
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
61573
- const sheetId = this.getters.getActiveSheetId();
61574
- const visibleCols = this.getters.getSheetViewVisibleCols();
61575
- const visibleRows = this.getters.getSheetViewVisibleRows();
61576
- if (index < referenceIndex) {
61577
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
61609
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
61610
+ if (targetHeaderIndex < referenceHeaderIndex) {
61611
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
61578
61612
  }
61613
+ const sheetId = this.getters.getActiveSheetId();
61614
+ const visibleHeaders = dimension === "COL"
61615
+ ? this.getters.getSheetViewVisibleCols()
61616
+ : this.getters.getSheetViewVisibleRows();
61617
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
61618
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
61619
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
61579
61620
  let offset = 0;
61580
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
61581
- for (let i = referenceIndex; i < index; i++) {
61582
- if (!visibleIndexes.includes(i)) {
61583
- continue;
61584
- }
61621
+ for (const i of relevantIndexes) {
61585
61622
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
61586
61623
  }
61587
61624
  return offset;
@@ -61628,7 +61665,7 @@ stores.inject(MyMetaStore, storeInstance);
61628
61665
  }
61629
61666
  return { canEdgeScroll, direction, delay };
61630
61667
  }
61631
- getEdgeScrollRow(y, previousY, tartingY) {
61668
+ getEdgeScrollRow(y, previousY, startingY) {
61632
61669
  let canEdgeScroll = false;
61633
61670
  let direction = 0;
61634
61671
  let delay = 0;
@@ -61649,7 +61686,7 @@ stores.inject(MyMetaStore, storeInstance);
61649
61686
  delay = scrollDelay(y - height);
61650
61687
  direction = 1;
61651
61688
  }
61652
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
61689
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
61653
61690
  // 2
61654
61691
  canEdgeScroll = true;
61655
61692
  delay = scrollDelay(offsetCorrectionY - y);
@@ -61675,13 +61712,7 @@ stores.inject(MyMetaStore, storeInstance);
61675
61712
  */
61676
61713
  getVisibleRectWithoutHeaders(zone) {
61677
61714
  const sheetId = this.getters.getActiveSheetId();
61678
- const viewportRects = this.getSubViewports(sheetId)
61679
- .map((viewport) => viewport.getVisibleRect(zone))
61680
- .filter(isDefined);
61681
- if (viewportRects.length === 0) {
61682
- return { x: 0, y: 0, width: 0, height: 0 };
61683
- }
61684
- return this.recomposeRect(viewportRects);
61715
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
61685
61716
  }
61686
61717
  /**
61687
61718
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -61689,13 +61720,7 @@ stores.inject(MyMetaStore, storeInstance);
61689
61720
  */
61690
61721
  getRect(zone) {
61691
61722
  const sheetId = this.getters.getActiveSheetId();
61692
- const viewportRects = this.getSubViewports(sheetId)
61693
- .map((viewport) => viewport.getFullRect(zone))
61694
- .filter(isDefined);
61695
- if (viewportRects.length === 0) {
61696
- return { x: 0, y: 0, width: 0, height: 0 };
61697
- }
61698
- const rect = this.recomposeRect(viewportRects);
61723
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
61699
61724
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
61700
61725
  }
61701
61726
  /**
@@ -61740,9 +61765,18 @@ stores.inject(MyMetaStore, storeInstance);
61740
61765
  end: start + (isRowHidden ? 0 : size),
61741
61766
  };
61742
61767
  }
61743
- getAllActiveViewportsZones() {
61768
+ getAllActiveViewportsZonesAndRect() {
61744
61769
  const sheetId = this.getters.getActiveSheetId();
61745
- return this.getSubViewports(sheetId);
61770
+ return this.getSubViewports(sheetId).map((viewport) => {
61771
+ return {
61772
+ zone: viewport,
61773
+ rect: {
61774
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
61775
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
61776
+ ...viewport.getMaxSize(),
61777
+ },
61778
+ };
61779
+ });
61746
61780
  }
61747
61781
  // ---------------------------------------------------------------------------
61748
61782
  // Private
@@ -61801,12 +61835,11 @@ stores.inject(MyMetaStore, storeInstance);
61801
61835
  }
61802
61836
  /** gets rid of deprecated sheetIds */
61803
61837
  cleanViewports() {
61804
- const sheetIds = this.getters.getSheetIds();
61805
- for (let sheetId of Object.keys(this.viewports)) {
61806
- if (!sheetIds.includes(sheetId)) {
61807
- delete this.viewports[sheetId];
61808
- }
61838
+ const newViewport = {};
61839
+ for (const sheetId of this.getters.getSheetIds()) {
61840
+ newViewport[sheetId] = this.viewports[sheetId];
61809
61841
  }
61842
+ this.viewports = newViewport;
61810
61843
  }
61811
61844
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
61812
61845
  this.sheetViewHeight = height;
@@ -61816,7 +61849,7 @@ stores.inject(MyMetaStore, storeInstance);
61816
61849
  this.recomputeViewports();
61817
61850
  }
61818
61851
  recomputeViewports() {
61819
- for (let sheetId of Object.keys(this.viewports)) {
61852
+ for (const sheetId of this.getters.getSheetIds()) {
61820
61853
  this.resetViewports(sheetId);
61821
61854
  }
61822
61855
  }
@@ -61838,8 +61871,10 @@ stores.inject(MyMetaStore, storeInstance);
61838
61871
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
61839
61872
  const nCols = this.getters.getNumberCols(sheetId);
61840
61873
  const nRows = this.getters.getNumberRows(sheetId);
61841
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
61842
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
61874
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
61875
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
61876
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
61877
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
61843
61878
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
61844
61879
  const canScrollHorizontally = xRatio < 1.0;
61845
61880
  const canScrollVertically = yRatio < 1.0;
@@ -61850,14 +61885,14 @@ stores.inject(MyMetaStore, storeInstance);
61850
61885
  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 })) ||
61851
61886
  undefined,
61852
61887
  topRight: (ySplit &&
61853
- 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 })) ||
61888
+ 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 })) ||
61854
61889
  undefined,
61855
61890
  bottomLeft: (xSplit &&
61856
- 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 })) ||
61891
+ 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 })) ||
61857
61892
  undefined,
61858
61893
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
61859
- width: this.sheetViewWidth - colOffset,
61860
- height: this.sheetViewHeight - rowOffset,
61894
+ width: unfrozenWidth,
61895
+ height: unfrozenHeight,
61861
61896
  }, { canScrollHorizontally, canScrollVertically }, {
61862
61897
  x: canScrollHorizontally ? previousOffset.x : 0,
61863
61898
  y: canScrollVertically ? previousOffset.y : 0,
@@ -61934,12 +61969,26 @@ stores.inject(MyMetaStore, storeInstance);
61934
61969
  const height = this.sheetViewHeight + this.gridOffsetY;
61935
61970
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
61936
61971
  }
61937
- recomposeRect(viewportRects) {
61938
- const x = Math.min(...viewportRects.map((rect) => rect.x));
61939
- const y = Math.min(...viewportRects.map((rect) => rect.y));
61940
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
61941
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
61942
- return { x, y, width, height };
61972
+ mapViewportsToRect(sheetId, rectCallBack) {
61973
+ let x = Infinity;
61974
+ let y = Infinity;
61975
+ let width = 0;
61976
+ let height = 0;
61977
+ let hasViewports = false;
61978
+ for (const viewport of this.getSubViewports(sheetId)) {
61979
+ const rect = rectCallBack(viewport);
61980
+ if (rect) {
61981
+ hasViewports = true;
61982
+ x = Math.min(x, rect.x);
61983
+ y = Math.min(y, rect.y);
61984
+ width = Math.max(width, rect.x + rect.width);
61985
+ height = Math.max(height, rect.y + rect.height);
61986
+ }
61987
+ }
61988
+ if (!hasViewports) {
61989
+ return { x: 0, y: 0, width: 0, height: 0 };
61990
+ }
61991
+ return { x, y, width: width - x, height: height - y };
61943
61992
  }
61944
61993
  }
61945
61994
 
@@ -62910,7 +62959,7 @@ stores.inject(MyMetaStore, storeInstance);
62910
62959
  draggedItemId: sheetId,
62911
62960
  initialMousePosition: event.clientX,
62912
62961
  items: sheets,
62913
- containerEl: this.sheetListRef.el,
62962
+ scrollableContainerEl: this.sheetListRef.el,
62914
62963
  onDragEnd: (sheetId, finalIndex) => this.onDragEnd(sheetId, finalIndex),
62915
62964
  });
62916
62965
  }
@@ -68872,9 +68921,9 @@ stores.inject(MyMetaStore, storeInstance);
68872
68921
  exports.tokenize = tokenize;
68873
68922
 
68874
68923
 
68875
- __info__.version = "17.4.20";
68876
- __info__.date = "2025-01-31T08:00:07.103Z";
68877
- __info__.hash = "54a344a";
68924
+ __info__.version = "17.4.22";
68925
+ __info__.date = "2025-02-10T09:14:48.889Z";
68926
+ __info__.hash = "667f2b1";
68878
68927
 
68879
68928
 
68880
68929
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);