@odoo/o-spreadsheet 18.0.13 → 18.0.15

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 18.0.13
6
- * @date 2025-01-31T07:59:17.481Z
7
- * @hash f505971
5
+ * @version 18.0.15
6
+ * @date 2025-02-10T08:59:22.993Z
7
+ * @hash 5b19f88
8
8
  */
9
9
 
10
10
  'use strict';
@@ -767,9 +767,16 @@ function deepEqualsArray(arr1, arr2) {
767
767
  }
768
768
  return true;
769
769
  }
770
- /** Check if the given array contains all the values of the other array. */
770
+ /**
771
+ * Check if the given array contains all the values of the other array.
772
+ * It makes the assumption that both array do not contain duplicates.
773
+ */
771
774
  function includesAll(arr, values) {
772
- return values.every((value) => arr.includes(value));
775
+ if (arr.length < values.length) {
776
+ return false;
777
+ }
778
+ const set = new Set(arr);
779
+ return values.every((value) => set.has(value));
773
780
  }
774
781
  /**
775
782
  * Return an object with all the keys in the object that have a falsy value removed.
@@ -18384,19 +18391,20 @@ const HLOOKUP = {
18384
18391
  description: _t("Horizontal lookup"),
18385
18392
  args: [
18386
18393
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18387
- 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.")),
18394
+ 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.")),
18388
18395
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18389
18396
  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.")),
18390
18397
  ],
18391
18398
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18392
18399
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18393
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18400
+ const _range = toMatrix(range);
18401
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18394
18402
  const getValueFromRange = (range, index) => range[index][0].value;
18395
18403
  const _isSorted = toBoolean(isSorted.value);
18396
18404
  const colIndex = _isSorted
18397
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18398
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange, this.lookupCaches);
18399
- const col = range[colIndex];
18405
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18406
+ : linearSearch(range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches);
18407
+ const col = _range[colIndex];
18400
18408
  if (col === undefined) {
18401
18409
  return valueNotAvailable(searchKey);
18402
18410
  }
@@ -18488,35 +18496,37 @@ const LOOKUP = {
18488
18496
  description: _t("Look up a value."),
18489
18497
  args: [
18490
18498
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18491
- 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.")),
18492
- 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.")),
18499
+ 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.")),
18500
+ 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.")),
18493
18501
  ],
18494
18502
  compute: function (searchKey, searchArray, resultRange) {
18495
- let nbCol = searchArray.length;
18496
- let nbRow = searchArray[0].length;
18503
+ const _searchArray = toMatrix(searchArray);
18504
+ const _resultRange = toMatrix(resultRange);
18505
+ let nbCol = _searchArray.length;
18506
+ let nbRow = _searchArray[0].length;
18497
18507
  const verticalSearch = nbRow >= nbCol;
18498
18508
  const getElement = verticalSearch
18499
18509
  ? (range, index) => range[0][index].value
18500
18510
  : (range, index) => range[index][0].value;
18501
18511
  const rangeLength = verticalSearch ? nbRow : nbCol;
18502
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18512
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18503
18513
  if (index === -1 ||
18504
- (verticalSearch && searchArray[0][index] === undefined) ||
18505
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18514
+ (verticalSearch && _searchArray[0][index] === undefined) ||
18515
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
18506
18516
  return valueNotAvailable(searchKey);
18507
18517
  }
18508
- if (resultRange === undefined) {
18509
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18518
+ if (_resultRange[0].length === 0) {
18519
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
18510
18520
  }
18511
- nbCol = resultRange.length;
18512
- nbRow = resultRange[0].length;
18521
+ nbCol = _resultRange.length;
18522
+ nbRow = _resultRange[0].length;
18513
18523
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
18514
18524
  if (nbCol > 1) {
18515
18525
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
18516
- return resultRange[index][0];
18526
+ return _resultRange[index][0];
18517
18527
  }
18518
18528
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
18519
- return resultRange[0][index];
18529
+ return _resultRange[0][index];
18520
18530
  },
18521
18531
  isExported: true,
18522
18532
  };
@@ -18533,28 +18543,29 @@ const MATCH = {
18533
18543
  ],
18534
18544
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
18535
18545
  let _searchType = toNumber(searchType, this.locale);
18536
- const nbCol = range.length;
18537
- const nbRow = range[0].length;
18546
+ const _range = toMatrix(range);
18547
+ const nbCol = _range.length;
18548
+ const nbRow = _range[0].length;
18538
18549
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
18539
18550
  let index = -1;
18540
18551
  const getElement = nbCol === 1
18541
- ? (range, index) => range[0][index].value
18542
- : (range, index) => range[index][0].value;
18543
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
18552
+ ? (_range, index) => _range[0][index].value
18553
+ : (_range, index) => _range[index][0].value;
18554
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
18544
18555
  _searchType = Math.sign(_searchType);
18545
18556
  switch (_searchType) {
18546
18557
  case 1:
18547
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18558
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18548
18559
  break;
18549
18560
  case 0:
18550
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18561
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18551
18562
  break;
18552
18563
  case -1:
18553
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18564
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18554
18565
  break;
18555
18566
  }
18556
- if ((nbCol === 1 && range[0][index] === undefined) ||
18557
- (nbCol !== 1 && range[index] === undefined)) {
18567
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
18568
+ (nbCol !== 1 && _range[index] === undefined)) {
18558
18569
  return valueNotAvailable(searchKey);
18559
18570
  }
18560
18571
  return index + 1;
@@ -18609,13 +18620,14 @@ const VLOOKUP = {
18609
18620
  ],
18610
18621
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18611
18622
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18612
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18623
+ const _range = toMatrix(range);
18624
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18613
18625
  const getValueFromRange = (range, index) => range[0][index].value;
18614
18626
  const _isSorted = toBoolean(isSorted.value);
18615
18627
  const rowIndex = _isSorted
18616
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18617
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange, this.lookupCaches);
18618
- const value = range[_index - 1][rowIndex];
18628
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18629
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches);
18630
+ const value = _range[_index - 1][rowIndex];
18619
18631
  if (value === undefined) {
18620
18632
  return valueNotAvailable(searchKey);
18621
18633
  }
@@ -18652,27 +18664,29 @@ const XLOOKUP = {
18652
18664
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18653
18665
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18654
18666
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18655
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18667
+ const _lookupRange = toMatrix(lookupRange);
18668
+ const _returnRange = toMatrix(returnRange);
18669
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18656
18670
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18657
18671
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18658
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18672
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18659
18673
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18660
18674
  assert(() => lookupDirection === "col"
18661
- ? returnRange[0].length === lookupRange[0].length
18662
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18675
+ ? _returnRange[0].length === _lookupRange[0].length
18676
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18663
18677
  const getElement = lookupDirection === "col"
18664
18678
  ? (range, index) => range[0][index].value
18665
18679
  : (range, index) => range[index][0].value;
18666
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18680
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18667
18681
  const mode = MATCH_MODE[_matchMode];
18668
18682
  const reverseSearch = _searchMode === -1;
18669
18683
  const index = _searchMode === 2 || _searchMode === -2
18670
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18671
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18684
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18685
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18672
18686
  if (index !== -1) {
18673
18687
  return lookupDirection === "col"
18674
- ? returnRange.map((col) => [col[index]])
18675
- : [returnRange[index]];
18688
+ ? _returnRange.map((col) => [col[index]])
18689
+ : [_returnRange[index]];
18676
18690
  }
18677
18691
  if (defaultValue === undefined) {
18678
18692
  return valueNotAvailable(searchKey);
@@ -39250,8 +39264,8 @@ function useDragAndDropListItems() {
39250
39264
  document.body.style.cursor = "move";
39251
39265
  state.draggedItemId = args.draggedItemId;
39252
39266
  const container = direction === "horizontal"
39253
- ? new HorizontalContainer(args.containerEl)
39254
- : new VerticalContainer(args.containerEl);
39267
+ ? new HorizontalContainer(args.scrollableContainerEl)
39268
+ : new VerticalContainer(args.scrollableContainerEl);
39255
39269
  dndHelper = new DOMDndHelper({
39256
39270
  ...args,
39257
39271
  container,
@@ -39262,8 +39276,8 @@ function useDragAndDropListItems() {
39262
39276
  const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
39263
39277
  cleanupFns.push(stopListening);
39264
39278
  const onScroll = dndHelper.onScroll.bind(dndHelper);
39265
- args.containerEl.addEventListener("scroll", onScroll);
39266
- cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
39279
+ args.scrollableContainerEl.addEventListener("scroll", onScroll);
39280
+ cleanupFns.push(() => args.scrollableContainerEl.removeEventListener("scroll", onScroll));
39267
39281
  cleanupFns.push(dndHelper.destroy.bind(dndHelper));
39268
39282
  };
39269
39283
  owl.onWillUnmount(() => {
@@ -39727,7 +39741,7 @@ class ConditionalFormatPreviewList extends owl.Component {
39727
39741
  draggedItemId: cf.id,
39728
39742
  initialMousePosition: event.clientY,
39729
39743
  items: items,
39730
- containerEl: this.cfListRef.el,
39744
+ scrollableContainerEl: this.cfListRef.el,
39731
39745
  onDragEnd: (cfId, finalIndex) => this.onDragEnd(cfId, finalIndex),
39732
39746
  });
39733
39747
  }
@@ -42711,6 +42725,7 @@ class PivotLayoutConfigurator extends owl.Component {
42711
42725
  unusedGranularities: Object,
42712
42726
  dateGranularities: Array,
42713
42727
  datetimeGranularities: Array,
42728
+ getScrollableContainerEl: { type: Function, optional: true },
42714
42729
  pivotId: String,
42715
42730
  };
42716
42731
  dimensionsRef = owl.useRef("pivot-dimensions");
@@ -42744,7 +42759,7 @@ class PivotLayoutConfigurator extends owl.Component {
42744
42759
  draggedItemId: dimension.nameWithGranularity,
42745
42760
  initialMousePosition: event.clientY,
42746
42761
  items: draggableItems,
42747
- containerEl: this.dimensionsRef.el,
42762
+ scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
42748
42763
  onDragEnd: (dimensionName, finalIndex) => {
42749
42764
  const originalIndex = draggableIds.findIndex((id) => id === dimensionName);
42750
42765
  if (originalIndex === finalIndex) {
@@ -42793,7 +42808,7 @@ class PivotLayoutConfigurator extends owl.Component {
42793
42808
  draggedItemId: measure.id,
42794
42809
  initialMousePosition: event.clientY,
42795
42810
  items: draggableItems,
42796
- containerEl: this.dimensionsRef.el,
42811
+ scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
42797
42812
  onDragEnd: (measureName, finalIndex) => {
42798
42813
  const originalIndex = draggableIds.findIndex((id) => id === measureName);
42799
42814
  if (originalIndex === finalIndex) {
@@ -44421,6 +44436,7 @@ class PivotSpreadsheetSidePanel extends owl.Component {
44421
44436
  };
44422
44437
  store;
44423
44438
  state;
44439
+ pivotSidePanelRef = owl.useRef("pivotSidePanel");
44424
44440
  setup() {
44425
44441
  this.store = useLocalStore(PivotSidePanelStore, this.props.pivotId);
44426
44442
  this.state = owl.useState({
@@ -44449,6 +44465,9 @@ class PivotSpreadsheetSidePanel extends owl.Component {
44449
44465
  get definition() {
44450
44466
  return this.store.definition;
44451
44467
  }
44468
+ getScrollableContainerEl() {
44469
+ return this.pivotSidePanelRef.el;
44470
+ }
44452
44471
  onSelectionChanged(ranges) {
44453
44472
  this.state.rangeHasChanged = true;
44454
44473
  this.state.range = ranges[0];
@@ -48478,11 +48497,10 @@ class GridRenderer {
48478
48497
  switch (layer) {
48479
48498
  case "Background":
48480
48499
  this.drawGlobalBackground(renderingContext);
48481
- for (const zone of this.getters.getAllActiveViewportsZones()) {
48500
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
48482
48501
  const { ctx } = renderingContext;
48483
48502
  ctx.save();
48484
48503
  ctx.beginPath();
48485
- const rect = this.getters.getVisibleRect(zone);
48486
48504
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
48487
48505
  ctx.clip();
48488
48506
  const boxes = this.getGridBoxes(zone);
@@ -48758,10 +48776,8 @@ class GridRenderer {
48758
48776
  const { ctx, thinLineWidth } = renderingContext;
48759
48777
  const visibleCols = this.getters.getSheetViewVisibleCols();
48760
48778
  const left = visibleCols[0];
48761
- const right = visibleCols[visibleCols.length - 1];
48762
48779
  const visibleRows = this.getters.getSheetViewVisibleRows();
48763
48780
  const top = visibleRows[0];
48764
- const bottom = visibleRows[visibleRows.length - 1];
48765
48781
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
48766
48782
  const selection = this.getters.getSelectedZones();
48767
48783
  const selectedCols = getZonesCols(selection);
@@ -48777,7 +48793,7 @@ class GridRenderer {
48777
48793
  ctx.lineWidth = thinLineWidth;
48778
48794
  ctx.strokeStyle = "#333";
48779
48795
  // Columns headers background
48780
- for (let col = left; col <= right; col++) {
48796
+ for (const col of visibleCols) {
48781
48797
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
48782
48798
  const { x, width } = this.getters.getVisibleRect(colZone);
48783
48799
  const isColActive = activeCols.has(col);
@@ -48794,7 +48810,7 @@ class GridRenderer {
48794
48810
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
48795
48811
  }
48796
48812
  // Rows headers background
48797
- for (let row = top; row <= bottom; row++) {
48813
+ for (const row of visibleRows) {
48798
48814
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
48799
48815
  const { y, height } = this.getters.getVisibleRect(rowZone);
48800
48816
  const isRowActive = activeRows.has(row);
@@ -48820,21 +48836,21 @@ class GridRenderer {
48820
48836
  ctx.stroke();
48821
48837
  ctx.beginPath();
48822
48838
  // column text + separator
48823
- for (const i of visibleCols) {
48824
- const colSize = this.getters.getColSize(sheetId, i);
48825
- const colName = numberToLetters(i);
48826
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
48827
- let colStart = this.getHeaderOffset("COL", left, i);
48839
+ for (const col of visibleCols) {
48840
+ const colSize = this.getters.getColSize(sheetId, col);
48841
+ const colName = numberToLetters(col);
48842
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
48843
+ let colStart = this.getHeaderOffset("COL", left, col);
48828
48844
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
48829
48845
  ctx.moveTo(colStart + colSize, 0);
48830
48846
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
48831
48847
  }
48832
48848
  // row text + separator
48833
- for (const i of visibleRows) {
48834
- const rowSize = this.getters.getRowSize(sheetId, i);
48835
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
48836
- let rowStart = this.getHeaderOffset("ROW", top, i);
48837
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
48849
+ for (const row of visibleRows) {
48850
+ const rowSize = this.getters.getRowSize(sheetId, row);
48851
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
48852
+ let rowStart = this.getHeaderOffset("ROW", top, row);
48853
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
48838
48854
  ctx.moveTo(0, rowStart + rowSize);
48839
48855
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
48840
48856
  }
@@ -49134,6 +49150,9 @@ function useGridDrawing(refName, model, canvasSize) {
49134
49150
  canvas.width = width * dpr;
49135
49151
  canvas.height = height * dpr;
49136
49152
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
49153
+ if (width === 0 || height === 0) {
49154
+ return;
49155
+ }
49137
49156
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
49138
49157
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
49139
49158
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -50705,7 +50724,7 @@ class BordersPlugin extends CorePlugin {
50705
50724
  getCommonSides(border1, border2) {
50706
50725
  const commonBorder = {};
50707
50726
  for (let side of ["top", "bottom", "left", "right"]) {
50708
- if (border1[side] && border1[side] === border2[side]) {
50727
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
50709
50728
  commonBorder[side] = border1[side];
50710
50729
  }
50711
50730
  }
@@ -64975,8 +64994,17 @@ class InternalViewport {
64975
64994
  this.getters = getters;
64976
64995
  this.sheetId = sheetId;
64977
64996
  this.boundaries = boundaries;
64978
- this.viewportWidth = sizeInGrid.width;
64979
- this.viewportHeight = sizeInGrid.height;
64997
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
64998
+ throw new Error("Viewport size cannot be negative");
64999
+ }
65000
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
65001
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
65002
+ this.top = boundaries.top;
65003
+ this.bottom = boundaries.bottom;
65004
+ this.left = boundaries.left;
65005
+ this.right = boundaries.right;
65006
+ this.offsetX = offsets.x;
65007
+ this.offsetY = offsets.y;
64980
65008
  this.offsetScrollbarX = offsets.x;
64981
65009
  this.offsetScrollbarY = offsets.y;
64982
65010
  this.canScrollVertically = options.canScrollVertically;
@@ -65019,9 +65047,9 @@ class InternalViewport {
65019
65047
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
65020
65048
  );
65021
65049
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
65022
- }
65023
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
65024
- height += FOOTER_HEIGHT;
65050
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
65051
+ height += FOOTER_HEIGHT;
65052
+ }
65025
65053
  }
65026
65054
  return { width, height };
65027
65055
  }
@@ -65162,6 +65190,9 @@ class InternalViewport {
65162
65190
  !this.getters.isRowHidden(this.sheetId, row));
65163
65191
  }
65164
65192
  searchHeaderIndex(dimension, position, startIndex = 0) {
65193
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
65194
+ return -1;
65195
+ }
65165
65196
  const sheetId = this.sheetId;
65166
65197
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
65167
65198
  // using a binary search:
@@ -65198,7 +65229,7 @@ class InternalViewport {
65198
65229
  this.adjustViewportZoneY();
65199
65230
  }
65200
65231
  /** Corrects the viewport's horizontal offset based on the current structure
65201
- * To make sure that at least on column is visible inside the viewport.
65232
+ * To make sure that at least one column is visible inside the viewport.
65202
65233
  */
65203
65234
  adjustViewportOffsetX() {
65204
65235
  if (this.canScrollHorizontally) {
@@ -65210,7 +65241,7 @@ class InternalViewport {
65210
65241
  this.adjustViewportZoneX();
65211
65242
  }
65212
65243
  /** Corrects the viewport's vertical offset based on the current structure
65213
- * To make sure that at least on row is visible inside the viewport.
65244
+ * To make sure that at least one row is visible inside the viewport.
65214
65245
  */
65215
65246
  adjustViewportOffsetY() {
65216
65247
  if (this.canScrollVertically) {
@@ -65227,11 +65258,14 @@ class InternalViewport {
65227
65258
  const sheetId = this.sheetId;
65228
65259
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
65229
65260
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
65261
+ if (!this.viewportWidth) {
65262
+ return;
65263
+ }
65230
65264
  if (this.left === -1) {
65231
65265
  this.left = this.boundaries.left;
65232
65266
  }
65233
65267
  if (this.right === -1) {
65234
- this.right = this.getters.getNumberCols(sheetId) - 1;
65268
+ this.right = this.boundaries.right;
65235
65269
  }
65236
65270
  this.offsetX =
65237
65271
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -65243,11 +65277,14 @@ class InternalViewport {
65243
65277
  const sheetId = this.sheetId;
65244
65278
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
65245
65279
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
65280
+ if (!this.viewportHeight) {
65281
+ return;
65282
+ }
65246
65283
  if (this.top === -1) {
65247
65284
  this.top = this.boundaries.top;
65248
65285
  }
65249
65286
  if (this.bottom === -1) {
65250
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
65287
+ this.bottom = this.boundaries.bottom;
65251
65288
  }
65252
65289
  this.offsetY =
65253
65290
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -65321,7 +65358,7 @@ class SheetViewPlugin extends UIPlugin {
65321
65358
  "isPositionVisible",
65322
65359
  "getColDimensionsInViewport",
65323
65360
  "getRowDimensionsInViewport",
65324
- "getAllActiveViewportsZones",
65361
+ "getAllActiveViewportsZonesAndRect",
65325
65362
  "getRect",
65326
65363
  ];
65327
65364
  viewports = {};
@@ -65554,12 +65591,12 @@ class SheetViewPlugin extends UIPlugin {
65554
65591
  const sheetId = this.getters.getActiveSheetId();
65555
65592
  const viewports = this.getSubViewports(sheetId);
65556
65593
  //TODO ake another commit to eimprove this
65557
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
65594
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
65558
65595
  }
65559
65596
  getSheetViewVisibleRows() {
65560
65597
  const sheetId = this.getters.getActiveSheetId();
65561
65598
  const viewports = this.getSubViewports(sheetId);
65562
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
65599
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
65563
65600
  }
65564
65601
  /**
65565
65602
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -65602,19 +65639,19 @@ class SheetViewPlugin extends UIPlugin {
65602
65639
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
65603
65640
  };
65604
65641
  }
65605
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
65606
- const sheetId = this.getters.getActiveSheetId();
65607
- const visibleCols = this.getters.getSheetViewVisibleCols();
65608
- const visibleRows = this.getters.getSheetViewVisibleRows();
65609
- if (index < referenceIndex) {
65610
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
65642
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
65643
+ if (targetHeaderIndex < referenceHeaderIndex) {
65644
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
65611
65645
  }
65646
+ const sheetId = this.getters.getActiveSheetId();
65647
+ const visibleHeaders = dimension === "COL"
65648
+ ? this.getters.getSheetViewVisibleCols()
65649
+ : this.getters.getSheetViewVisibleRows();
65650
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
65651
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
65652
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
65612
65653
  let offset = 0;
65613
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
65614
- for (let i = referenceIndex; i < index; i++) {
65615
- if (!visibleIndexes.includes(i)) {
65616
- continue;
65617
- }
65654
+ for (const i of relevantIndexes) {
65618
65655
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
65619
65656
  }
65620
65657
  return offset;
@@ -65661,7 +65698,7 @@ class SheetViewPlugin extends UIPlugin {
65661
65698
  }
65662
65699
  return { canEdgeScroll, direction, delay };
65663
65700
  }
65664
- getEdgeScrollRow(y, previousY, tartingY) {
65701
+ getEdgeScrollRow(y, previousY, startingY) {
65665
65702
  let canEdgeScroll = false;
65666
65703
  let direction = 0;
65667
65704
  let delay = 0;
@@ -65682,7 +65719,7 @@ class SheetViewPlugin extends UIPlugin {
65682
65719
  delay = scrollDelay(y - height);
65683
65720
  direction = 1;
65684
65721
  }
65685
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
65722
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
65686
65723
  // 2
65687
65724
  canEdgeScroll = true;
65688
65725
  delay = scrollDelay(offsetCorrectionY - y);
@@ -65708,13 +65745,7 @@ class SheetViewPlugin extends UIPlugin {
65708
65745
  */
65709
65746
  getVisibleRectWithoutHeaders(zone) {
65710
65747
  const sheetId = this.getters.getActiveSheetId();
65711
- const viewportRects = this.getSubViewports(sheetId)
65712
- .map((viewport) => viewport.getVisibleRect(zone))
65713
- .filter(isDefined);
65714
- if (viewportRects.length === 0) {
65715
- return { x: 0, y: 0, width: 0, height: 0 };
65716
- }
65717
- return this.recomposeRect(viewportRects);
65748
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
65718
65749
  }
65719
65750
  /**
65720
65751
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -65722,13 +65753,7 @@ class SheetViewPlugin extends UIPlugin {
65722
65753
  */
65723
65754
  getRect(zone) {
65724
65755
  const sheetId = this.getters.getActiveSheetId();
65725
- const viewportRects = this.getSubViewports(sheetId)
65726
- .map((viewport) => viewport.getFullRect(zone))
65727
- .filter(isDefined);
65728
- if (viewportRects.length === 0) {
65729
- return { x: 0, y: 0, width: 0, height: 0 };
65730
- }
65731
- const rect = this.recomposeRect(viewportRects);
65756
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
65732
65757
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
65733
65758
  }
65734
65759
  /**
@@ -65773,9 +65798,18 @@ class SheetViewPlugin extends UIPlugin {
65773
65798
  end: start + (isRowHidden ? 0 : size),
65774
65799
  };
65775
65800
  }
65776
- getAllActiveViewportsZones() {
65801
+ getAllActiveViewportsZonesAndRect() {
65777
65802
  const sheetId = this.getters.getActiveSheetId();
65778
- return this.getSubViewports(sheetId);
65803
+ return this.getSubViewports(sheetId).map((viewport) => {
65804
+ return {
65805
+ zone: viewport,
65806
+ rect: {
65807
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
65808
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
65809
+ ...viewport.getMaxSize(),
65810
+ },
65811
+ };
65812
+ });
65779
65813
  }
65780
65814
  // ---------------------------------------------------------------------------
65781
65815
  // Private
@@ -65834,12 +65868,11 @@ class SheetViewPlugin extends UIPlugin {
65834
65868
  }
65835
65869
  /** gets rid of deprecated sheetIds */
65836
65870
  cleanViewports() {
65837
- const sheetIds = this.getters.getSheetIds();
65838
- for (let sheetId of Object.keys(this.viewports)) {
65839
- if (!sheetIds.includes(sheetId)) {
65840
- delete this.viewports[sheetId];
65841
- }
65871
+ const newViewport = {};
65872
+ for (const sheetId of this.getters.getSheetIds()) {
65873
+ newViewport[sheetId] = this.viewports[sheetId];
65842
65874
  }
65875
+ this.viewports = newViewport;
65843
65876
  }
65844
65877
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
65845
65878
  this.sheetViewHeight = height;
@@ -65849,7 +65882,7 @@ class SheetViewPlugin extends UIPlugin {
65849
65882
  this.recomputeViewports();
65850
65883
  }
65851
65884
  recomputeViewports() {
65852
- for (let sheetId of Object.keys(this.viewports)) {
65885
+ for (const sheetId of this.getters.getSheetIds()) {
65853
65886
  this.resetViewports(sheetId);
65854
65887
  }
65855
65888
  }
@@ -65871,8 +65904,10 @@ class SheetViewPlugin extends UIPlugin {
65871
65904
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
65872
65905
  const nCols = this.getters.getNumberCols(sheetId);
65873
65906
  const nRows = this.getters.getNumberRows(sheetId);
65874
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
65875
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
65907
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
65908
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
65909
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
65910
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
65876
65911
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
65877
65912
  const canScrollHorizontally = xRatio < 1.0;
65878
65913
  const canScrollVertically = yRatio < 1.0;
@@ -65883,14 +65918,14 @@ class SheetViewPlugin extends UIPlugin {
65883
65918
  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 })) ||
65884
65919
  undefined,
65885
65920
  topRight: (ySplit &&
65886
- 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 })) ||
65921
+ 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 })) ||
65887
65922
  undefined,
65888
65923
  bottomLeft: (xSplit &&
65889
- 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 })) ||
65924
+ 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 })) ||
65890
65925
  undefined,
65891
65926
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
65892
- width: this.sheetViewWidth - colOffset,
65893
- height: this.sheetViewHeight - rowOffset,
65927
+ width: unfrozenWidth,
65928
+ height: unfrozenHeight,
65894
65929
  }, { canScrollHorizontally, canScrollVertically }, {
65895
65930
  x: canScrollHorizontally ? previousOffset.x : 0,
65896
65931
  y: canScrollVertically ? previousOffset.y : 0,
@@ -65967,12 +66002,26 @@ class SheetViewPlugin extends UIPlugin {
65967
66002
  const height = this.sheetViewHeight + this.gridOffsetY;
65968
66003
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
65969
66004
  }
65970
- recomposeRect(viewportRects) {
65971
- const x = Math.min(...viewportRects.map((rect) => rect.x));
65972
- const y = Math.min(...viewportRects.map((rect) => rect.y));
65973
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
65974
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
65975
- return { x, y, width, height };
66005
+ mapViewportsToRect(sheetId, rectCallBack) {
66006
+ let x = Infinity;
66007
+ let y = Infinity;
66008
+ let width = 0;
66009
+ let height = 0;
66010
+ let hasViewports = false;
66011
+ for (const viewport of this.getSubViewports(sheetId)) {
66012
+ const rect = rectCallBack(viewport);
66013
+ if (rect) {
66014
+ hasViewports = true;
66015
+ x = Math.min(x, rect.x);
66016
+ y = Math.min(y, rect.y);
66017
+ width = Math.max(width, rect.x + rect.width);
66018
+ height = Math.max(height, rect.y + rect.height);
66019
+ }
66020
+ }
66021
+ if (!hasViewports) {
66022
+ return { x: 0, y: 0, width: 0, height: 0 };
66023
+ }
66024
+ return { x, y, width: width - x, height: height - y };
65976
66025
  }
65977
66026
  }
65978
66027
 
@@ -66968,7 +67017,7 @@ class BottomBar extends owl.Component {
66968
67017
  draggedItemId: sheetId,
66969
67018
  initialMousePosition: event.clientX,
66970
67019
  items: sheets,
66971
- containerEl: this.sheetListRef.el,
67020
+ scrollableContainerEl: this.sheetListRef.el,
66972
67021
  onDragEnd: (sheetId, finalIndex) => this.onDragEnd(sheetId, finalIndex),
66973
67022
  });
66974
67023
  }
@@ -73133,6 +73182,6 @@ exports.tokenColors = tokenColors;
73133
73182
  exports.tokenize = tokenize;
73134
73183
 
73135
73184
 
73136
- __info__.version = "18.0.13";
73137
- __info__.date = "2025-01-31T07:59:17.481Z";
73138
- __info__.hash = "f505971";
73185
+ __info__.version = "18.0.15";
73186
+ __info__.date = "2025-02-10T08:59:22.993Z";
73187
+ __info__.hash = "5b19f88";