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