@odoo/o-spreadsheet 18.1.5 → 18.1.7

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.1.5
6
- * @date 2025-01-31T08:00:10.263Z
7
- * @hash 97acb8b
5
+ * @version 18.1.7
6
+ * @date 2025-02-10T09:00:28.556Z
7
+ * @hash 338d8a1
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -769,9 +769,16 @@
769
769
  }
770
770
  return true;
771
771
  }
772
- /** Check if the given array contains all the values of the other array. */
772
+ /**
773
+ * Check if the given array contains all the values of the other array.
774
+ * It makes the assumption that both array do not contain duplicates.
775
+ */
773
776
  function includesAll(arr, values) {
774
- return values.every((value) => arr.includes(value));
777
+ if (arr.length < values.length) {
778
+ return false;
779
+ }
780
+ const set = new Set(arr);
781
+ return values.every((value) => set.has(value));
775
782
  }
776
783
  /**
777
784
  * Return an object with all the keys in the object that have a falsy value removed.
@@ -18455,19 +18462,20 @@ stores.inject(MyMetaStore, storeInstance);
18455
18462
  description: _t("Horizontal lookup"),
18456
18463
  args: [
18457
18464
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18458
- 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.")),
18465
+ 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.")),
18459
18466
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18460
18467
  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.")),
18461
18468
  ],
18462
18469
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18463
18470
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18464
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18471
+ const _range = toMatrix(range);
18472
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18465
18473
  const getValueFromRange = (range, index) => range[index][0].value;
18466
18474
  const _isSorted = toBoolean(isSorted.value);
18467
18475
  const colIndex = _isSorted
18468
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18469
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18470
- const col = range[colIndex];
18476
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18477
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18478
+ const col = _range[colIndex];
18471
18479
  if (col === undefined) {
18472
18480
  return valueNotAvailable(searchKey);
18473
18481
  }
@@ -18559,35 +18567,37 @@ stores.inject(MyMetaStore, storeInstance);
18559
18567
  description: _t("Look up a value."),
18560
18568
  args: [
18561
18569
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18562
- 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.")),
18563
- 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.")),
18570
+ 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.")),
18571
+ 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.")),
18564
18572
  ],
18565
18573
  compute: function (searchKey, searchArray, resultRange) {
18566
- let nbCol = searchArray.length;
18567
- let nbRow = searchArray[0].length;
18574
+ const _searchArray = toMatrix(searchArray);
18575
+ const _resultRange = toMatrix(resultRange);
18576
+ let nbCol = _searchArray.length;
18577
+ let nbRow = _searchArray[0].length;
18568
18578
  const verticalSearch = nbRow >= nbCol;
18569
18579
  const getElement = verticalSearch
18570
18580
  ? (range, index) => range[0][index].value
18571
18581
  : (range, index) => range[index][0].value;
18572
18582
  const rangeLength = verticalSearch ? nbRow : nbCol;
18573
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18583
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18574
18584
  if (index === -1 ||
18575
- (verticalSearch && searchArray[0][index] === undefined) ||
18576
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18585
+ (verticalSearch && _searchArray[0][index] === undefined) ||
18586
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
18577
18587
  return valueNotAvailable(searchKey);
18578
18588
  }
18579
- if (resultRange === undefined) {
18580
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18589
+ if (_resultRange[0].length === 0) {
18590
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
18581
18591
  }
18582
- nbCol = resultRange.length;
18583
- nbRow = resultRange[0].length;
18592
+ nbCol = _resultRange.length;
18593
+ nbRow = _resultRange[0].length;
18584
18594
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
18585
18595
  if (nbCol > 1) {
18586
18596
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
18587
- return resultRange[index][0];
18597
+ return _resultRange[index][0];
18588
18598
  }
18589
18599
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
18590
- return resultRange[0][index];
18600
+ return _resultRange[0][index];
18591
18601
  },
18592
18602
  isExported: true,
18593
18603
  };
@@ -18604,28 +18614,29 @@ stores.inject(MyMetaStore, storeInstance);
18604
18614
  ],
18605
18615
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
18606
18616
  let _searchType = toNumber(searchType, this.locale);
18607
- const nbCol = range.length;
18608
- const nbRow = range[0].length;
18617
+ const _range = toMatrix(range);
18618
+ const nbCol = _range.length;
18619
+ const nbRow = _range[0].length;
18609
18620
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
18610
18621
  let index = -1;
18611
18622
  const getElement = nbCol === 1
18612
- ? (range, index) => range[0][index].value
18613
- : (range, index) => range[index][0].value;
18614
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
18623
+ ? (_range, index) => _range[0][index].value
18624
+ : (_range, index) => _range[index][0].value;
18625
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
18615
18626
  _searchType = Math.sign(_searchType);
18616
18627
  switch (_searchType) {
18617
18628
  case 1:
18618
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18629
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18619
18630
  break;
18620
18631
  case 0:
18621
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
18632
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18622
18633
  break;
18623
18634
  case -1:
18624
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18635
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18625
18636
  break;
18626
18637
  }
18627
- if ((nbCol === 1 && range[0][index] === undefined) ||
18628
- (nbCol !== 1 && range[index] === undefined)) {
18638
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
18639
+ (nbCol !== 1 && _range[index] === undefined)) {
18629
18640
  return valueNotAvailable(searchKey);
18630
18641
  }
18631
18642
  return index + 1;
@@ -18680,13 +18691,14 @@ stores.inject(MyMetaStore, storeInstance);
18680
18691
  ],
18681
18692
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18682
18693
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18683
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18694
+ const _range = toMatrix(range);
18695
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18684
18696
  const getValueFromRange = (range, index) => range[0][index].value;
18685
18697
  const _isSorted = toBoolean(isSorted.value);
18686
18698
  const rowIndex = _isSorted
18687
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18688
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
18689
- const value = range[_index - 1][rowIndex];
18699
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18700
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18701
+ const value = _range[_index - 1][rowIndex];
18690
18702
  if (value === undefined) {
18691
18703
  return valueNotAvailable(searchKey);
18692
18704
  }
@@ -18723,27 +18735,29 @@ stores.inject(MyMetaStore, storeInstance);
18723
18735
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18724
18736
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18725
18737
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18726
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18738
+ const _lookupRange = toMatrix(lookupRange);
18739
+ const _returnRange = toMatrix(returnRange);
18740
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18727
18741
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18728
18742
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18729
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18743
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18730
18744
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18731
18745
  assert(() => lookupDirection === "col"
18732
- ? returnRange[0].length === lookupRange[0].length
18733
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18746
+ ? _returnRange[0].length === _lookupRange[0].length
18747
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18734
18748
  const getElement = lookupDirection === "col"
18735
18749
  ? (range, index) => range[0][index].value
18736
18750
  : (range, index) => range[index][0].value;
18737
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18751
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18738
18752
  const mode = MATCH_MODE[_matchMode];
18739
18753
  const reverseSearch = _searchMode === -1;
18740
18754
  const index = _searchMode === 2 || _searchMode === -2
18741
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18742
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18755
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18756
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18743
18757
  if (index !== -1) {
18744
18758
  return lookupDirection === "col"
18745
- ? returnRange.map((col) => [col[index]])
18746
- : [returnRange[index]];
18759
+ ? _returnRange.map((col) => [col[index]])
18760
+ : [_returnRange[index]];
18747
18761
  }
18748
18762
  if (defaultValue === undefined) {
18749
18763
  return valueNotAvailable(searchKey);
@@ -28435,11 +28449,7 @@ stores.inject(MyMetaStore, storeInstance);
28435
28449
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
28436
28450
  return [];
28437
28451
  }
28438
- const labelMin = Math.min(...labels);
28439
- const labelMax = Math.max(...labels);
28440
- const labelRange = labelMax - labelMin;
28441
- const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
28442
- const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
28452
+ const { normalizedLabels, normalizedNewLabels } = normalizeLabels(labels, newLabels, config);
28443
28453
  try {
28444
28454
  switch (config.type) {
28445
28455
  case "polynomial": {
@@ -28484,6 +28494,30 @@ stores.inject(MyMetaStore, storeInstance);
28484
28494
  return newLabels.map((x) => ({ x, y: NaN }));
28485
28495
  }
28486
28496
  }
28497
+ function normalizeLabels(labels, newLabels, config) {
28498
+ let normalizedLabels = [];
28499
+ let normalizedNewLabels = [];
28500
+ if (config.type === "logarithmic") {
28501
+ // Logarithmic trends in charts are used to visualize proportional growth or
28502
+ // relative changes. Therefore, we change the normalization technique for
28503
+ // logarithmic trend lines for a better fit. The method used here is Max Absolute
28504
+ // Scaling. This Technique is ideal for data spanning several orders of magnitude,
28505
+ // as it balances differences between small and large values by compressing larger
28506
+ // values while preserving proportionality and ensuring all values are scaled relative
28507
+ // to the largest magnitude.
28508
+ const labelMax = Math.max(...labels.map(Math.abs));
28509
+ normalizedLabels = labels.map((l) => l / labelMax);
28510
+ normalizedNewLabels = newLabels.map((l) => l / labelMax);
28511
+ }
28512
+ else {
28513
+ const labelMax = Math.max(...labels);
28514
+ const labelMin = Math.min(...labels);
28515
+ const labelRange = labelMax - labelMin;
28516
+ normalizedLabels = labels.map((l) => (l - labelMax) / labelRange);
28517
+ normalizedNewLabels = newLabels.map((l) => (l - labelMax) / labelRange);
28518
+ }
28519
+ return { normalizedLabels, normalizedNewLabels };
28520
+ }
28487
28521
  function getChartAxisType(chart, labelRange, getters) {
28488
28522
  if (isDateChart(chart, labelRange, getters) && isLuxonTimeAdapterInstalled()) {
28489
28523
  return "time";
@@ -41003,8 +41037,8 @@ stores.inject(MyMetaStore, storeInstance);
41003
41037
  document.body.style.cursor = "move";
41004
41038
  state.draggedItemId = args.draggedItemId;
41005
41039
  const container = direction === "horizontal"
41006
- ? new HorizontalContainer(args.containerEl)
41007
- : new VerticalContainer(args.containerEl);
41040
+ ? new HorizontalContainer(args.scrollableContainerEl)
41041
+ : new VerticalContainer(args.scrollableContainerEl);
41008
41042
  dndHelper = new DOMDndHelper({
41009
41043
  ...args,
41010
41044
  container,
@@ -41015,8 +41049,8 @@ stores.inject(MyMetaStore, storeInstance);
41015
41049
  const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
41016
41050
  cleanupFns.push(stopListening);
41017
41051
  const onScroll = dndHelper.onScroll.bind(dndHelper);
41018
- args.containerEl.addEventListener("scroll", onScroll);
41019
- cleanupFns.push(() => args.containerEl.removeEventListener("scroll", onScroll));
41052
+ args.scrollableContainerEl.addEventListener("scroll", onScroll);
41053
+ cleanupFns.push(() => args.scrollableContainerEl.removeEventListener("scroll", onScroll));
41020
41054
  cleanupFns.push(dndHelper.destroy.bind(dndHelper));
41021
41055
  };
41022
41056
  owl.onWillUnmount(() => {
@@ -41473,7 +41507,7 @@ stores.inject(MyMetaStore, storeInstance);
41473
41507
  draggedItemId: cf.id,
41474
41508
  initialMousePosition: event.clientY,
41475
41509
  items: items,
41476
- containerEl: this.cfListRef.el,
41510
+ scrollableContainerEl: this.cfListRef.el,
41477
41511
  onDragEnd: (cfId, finalIndex) => this.onDragEnd(cfId, finalIndex),
41478
41512
  });
41479
41513
  }
@@ -44655,6 +44689,7 @@ stores.inject(MyMetaStore, storeInstance);
44655
44689
  unusedGranularities: Object,
44656
44690
  dateGranularities: Array,
44657
44691
  datetimeGranularities: Array,
44692
+ getScrollableContainerEl: { type: Function, optional: true },
44658
44693
  pivotId: String,
44659
44694
  };
44660
44695
  dimensionsRef = owl.useRef("pivot-dimensions");
@@ -44688,7 +44723,7 @@ stores.inject(MyMetaStore, storeInstance);
44688
44723
  draggedItemId: dimension.nameWithGranularity,
44689
44724
  initialMousePosition: event.clientY,
44690
44725
  items: draggableItems,
44691
- containerEl: this.dimensionsRef.el,
44726
+ scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
44692
44727
  onDragEnd: (dimensionName, finalIndex) => {
44693
44728
  const originalIndex = draggableIds.findIndex((id) => id === dimensionName);
44694
44729
  if (originalIndex === finalIndex) {
@@ -44737,7 +44772,7 @@ stores.inject(MyMetaStore, storeInstance);
44737
44772
  draggedItemId: measure.id,
44738
44773
  initialMousePosition: event.clientY,
44739
44774
  items: draggableItems,
44740
- containerEl: this.dimensionsRef.el,
44775
+ scrollableContainerEl: this.props.getScrollableContainerEl?.() || this.dimensionsRef.el,
44741
44776
  onDragEnd: (measureName, finalIndex) => {
44742
44777
  const originalIndex = draggableIds.findIndex((id) => id === measureName);
44743
44778
  if (originalIndex === finalIndex) {
@@ -45370,7 +45405,7 @@ stores.inject(MyMetaStore, storeInstance);
45370
45405
  rowTreeToRows(tree, parentRow) {
45371
45406
  return tree.flatMap((node) => {
45372
45407
  const row = {
45373
- indent: parentRow ? parentRow.indent + 1 : 0,
45408
+ indent: parentRow ? parentRow.indent + 1 : 1,
45374
45409
  fields: [...(parentRow?.fields || []), node.field],
45375
45410
  values: [...(parentRow?.values || []), node.value],
45376
45411
  };
@@ -45426,7 +45461,7 @@ stores.inject(MyMetaStore, storeInstance);
45426
45461
  pivotTableRows.push({
45427
45462
  fields: _fields,
45428
45463
  values: _values,
45429
- indent: index,
45464
+ indent: index + 1,
45430
45465
  });
45431
45466
  const record = groups[value];
45432
45467
  if (record) {
@@ -46424,6 +46459,7 @@ stores.inject(MyMetaStore, storeInstance);
46424
46459
  };
46425
46460
  store;
46426
46461
  state;
46462
+ pivotSidePanelRef = owl.useRef("pivotSidePanel");
46427
46463
  setup() {
46428
46464
  this.store = useLocalStore(PivotSidePanelStore, this.props.pivotId);
46429
46465
  this.state = owl.useState({
@@ -46452,6 +46488,9 @@ stores.inject(MyMetaStore, storeInstance);
46452
46488
  get definition() {
46453
46489
  return this.store.definition;
46454
46490
  }
46491
+ getScrollableContainerEl() {
46492
+ return this.pivotSidePanelRef.el;
46493
+ }
46455
46494
  onSelectionChanged(ranges) {
46456
46495
  this.state.rangeHasChanged = true;
46457
46496
  this.state.range = ranges[0];
@@ -50487,11 +50526,10 @@ stores.inject(MyMetaStore, storeInstance);
50487
50526
  switch (layer) {
50488
50527
  case "Background":
50489
50528
  this.drawGlobalBackground(renderingContext);
50490
- for (const zone of this.getters.getAllActiveViewportsZones()) {
50529
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
50491
50530
  const { ctx } = renderingContext;
50492
50531
  ctx.save();
50493
50532
  ctx.beginPath();
50494
- const rect = this.getters.getVisibleRect(zone);
50495
50533
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
50496
50534
  ctx.clip();
50497
50535
  const boxes = this.getGridBoxes(zone);
@@ -50767,10 +50805,8 @@ stores.inject(MyMetaStore, storeInstance);
50767
50805
  const { ctx, thinLineWidth } = renderingContext;
50768
50806
  const visibleCols = this.getters.getSheetViewVisibleCols();
50769
50807
  const left = visibleCols[0];
50770
- const right = visibleCols[visibleCols.length - 1];
50771
50808
  const visibleRows = this.getters.getSheetViewVisibleRows();
50772
50809
  const top = visibleRows[0];
50773
- const bottom = visibleRows[visibleRows.length - 1];
50774
50810
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
50775
50811
  const selection = this.getters.getSelectedZones();
50776
50812
  const selectedCols = getZonesCols(selection);
@@ -50786,7 +50822,7 @@ stores.inject(MyMetaStore, storeInstance);
50786
50822
  ctx.lineWidth = thinLineWidth;
50787
50823
  ctx.strokeStyle = "#333";
50788
50824
  // Columns headers background
50789
- for (let col = left; col <= right; col++) {
50825
+ for (const col of visibleCols) {
50790
50826
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
50791
50827
  const { x, width } = this.getters.getVisibleRect(colZone);
50792
50828
  const isColActive = activeCols.has(col);
@@ -50803,7 +50839,7 @@ stores.inject(MyMetaStore, storeInstance);
50803
50839
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
50804
50840
  }
50805
50841
  // Rows headers background
50806
- for (let row = top; row <= bottom; row++) {
50842
+ for (const row of visibleRows) {
50807
50843
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
50808
50844
  const { y, height } = this.getters.getVisibleRect(rowZone);
50809
50845
  const isRowActive = activeRows.has(row);
@@ -50829,21 +50865,21 @@ stores.inject(MyMetaStore, storeInstance);
50829
50865
  ctx.stroke();
50830
50866
  ctx.beginPath();
50831
50867
  // column text + separator
50832
- for (const i of visibleCols) {
50833
- const colSize = this.getters.getColSize(sheetId, i);
50834
- const colName = numberToLetters(i);
50835
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
50836
- let colStart = this.getHeaderOffset("COL", left, i);
50868
+ for (const col of visibleCols) {
50869
+ const colSize = this.getters.getColSize(sheetId, col);
50870
+ const colName = numberToLetters(col);
50871
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
50872
+ let colStart = this.getHeaderOffset("COL", left, col);
50837
50873
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
50838
50874
  ctx.moveTo(colStart + colSize, 0);
50839
50875
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
50840
50876
  }
50841
50877
  // row text + separator
50842
- for (const i of visibleRows) {
50843
- const rowSize = this.getters.getRowSize(sheetId, i);
50844
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
50845
- let rowStart = this.getHeaderOffset("ROW", top, i);
50846
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
50878
+ for (const row of visibleRows) {
50879
+ const rowSize = this.getters.getRowSize(sheetId, row);
50880
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
50881
+ let rowStart = this.getHeaderOffset("ROW", top, row);
50882
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
50847
50883
  ctx.moveTo(0, rowStart + rowSize);
50848
50884
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
50849
50885
  }
@@ -51150,6 +51186,9 @@ stores.inject(MyMetaStore, storeInstance);
51150
51186
  canvas.width = width * dpr;
51151
51187
  canvas.height = height * dpr;
51152
51188
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
51189
+ if (width === 0 || height === 0) {
51190
+ return;
51191
+ }
51153
51192
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
51154
51193
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
51155
51194
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -52696,7 +52735,7 @@ stores.inject(MyMetaStore, storeInstance);
52696
52735
  getCommonSides(border1, border2) {
52697
52736
  const commonBorder = {};
52698
52737
  for (let side of ["top", "bottom", "left", "right"]) {
52699
- if (border1[side] && border1[side] === border2[side]) {
52738
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52700
52739
  commonBorder[side] = border1[side];
52701
52740
  }
52702
52741
  }
@@ -66912,8 +66951,17 @@ stores.inject(MyMetaStore, storeInstance);
66912
66951
  this.getters = getters;
66913
66952
  this.sheetId = sheetId;
66914
66953
  this.boundaries = boundaries;
66915
- this.viewportWidth = sizeInGrid.width;
66916
- this.viewportHeight = sizeInGrid.height;
66954
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
66955
+ throw new Error("Viewport size cannot be negative");
66956
+ }
66957
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
66958
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
66959
+ this.top = boundaries.top;
66960
+ this.bottom = boundaries.bottom;
66961
+ this.left = boundaries.left;
66962
+ this.right = boundaries.right;
66963
+ this.offsetX = offsets.x;
66964
+ this.offsetY = offsets.y;
66917
66965
  this.offsetScrollbarX = offsets.x;
66918
66966
  this.offsetScrollbarY = offsets.y;
66919
66967
  this.canScrollVertically = options.canScrollVertically;
@@ -66956,9 +67004,9 @@ stores.inject(MyMetaStore, storeInstance);
66956
67004
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
66957
67005
  );
66958
67006
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
66959
- }
66960
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
66961
- height += FOOTER_HEIGHT;
67007
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
67008
+ height += FOOTER_HEIGHT;
67009
+ }
66962
67010
  }
66963
67011
  return { width, height };
66964
67012
  }
@@ -67099,6 +67147,9 @@ stores.inject(MyMetaStore, storeInstance);
67099
67147
  !this.getters.isRowHidden(this.sheetId, row));
67100
67148
  }
67101
67149
  searchHeaderIndex(dimension, position, startIndex = 0) {
67150
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
67151
+ return -1;
67152
+ }
67102
67153
  const sheetId = this.sheetId;
67103
67154
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
67104
67155
  // using a binary search:
@@ -67135,7 +67186,7 @@ stores.inject(MyMetaStore, storeInstance);
67135
67186
  this.adjustViewportZoneY();
67136
67187
  }
67137
67188
  /** Corrects the viewport's horizontal offset based on the current structure
67138
- * To make sure that at least on column is visible inside the viewport.
67189
+ * To make sure that at least one column is visible inside the viewport.
67139
67190
  */
67140
67191
  adjustViewportOffsetX() {
67141
67192
  if (this.canScrollHorizontally) {
@@ -67147,7 +67198,7 @@ stores.inject(MyMetaStore, storeInstance);
67147
67198
  this.adjustViewportZoneX();
67148
67199
  }
67149
67200
  /** Corrects the viewport's vertical offset based on the current structure
67150
- * To make sure that at least on row is visible inside the viewport.
67201
+ * To make sure that at least one row is visible inside the viewport.
67151
67202
  */
67152
67203
  adjustViewportOffsetY() {
67153
67204
  if (this.canScrollVertically) {
@@ -67164,11 +67215,14 @@ stores.inject(MyMetaStore, storeInstance);
67164
67215
  const sheetId = this.sheetId;
67165
67216
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
67166
67217
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
67218
+ if (!this.viewportWidth) {
67219
+ return;
67220
+ }
67167
67221
  if (this.left === -1) {
67168
67222
  this.left = this.boundaries.left;
67169
67223
  }
67170
67224
  if (this.right === -1) {
67171
- this.right = this.getters.getNumberCols(sheetId) - 1;
67225
+ this.right = this.boundaries.right;
67172
67226
  }
67173
67227
  this.offsetX =
67174
67228
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -67180,11 +67234,14 @@ stores.inject(MyMetaStore, storeInstance);
67180
67234
  const sheetId = this.sheetId;
67181
67235
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
67182
67236
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
67237
+ if (!this.viewportHeight) {
67238
+ return;
67239
+ }
67183
67240
  if (this.top === -1) {
67184
67241
  this.top = this.boundaries.top;
67185
67242
  }
67186
67243
  if (this.bottom === -1) {
67187
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
67244
+ this.bottom = this.boundaries.bottom;
67188
67245
  }
67189
67246
  this.offsetY =
67190
67247
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -67258,7 +67315,7 @@ stores.inject(MyMetaStore, storeInstance);
67258
67315
  "isPositionVisible",
67259
67316
  "getColDimensionsInViewport",
67260
67317
  "getRowDimensionsInViewport",
67261
- "getAllActiveViewportsZones",
67318
+ "getAllActiveViewportsZonesAndRect",
67262
67319
  "getRect",
67263
67320
  ];
67264
67321
  viewports = {};
@@ -67488,12 +67545,12 @@ stores.inject(MyMetaStore, storeInstance);
67488
67545
  const sheetId = this.getters.getActiveSheetId();
67489
67546
  const viewports = this.getSubViewports(sheetId);
67490
67547
  //TODO ake another commit to eimprove this
67491
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
67548
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
67492
67549
  }
67493
67550
  getSheetViewVisibleRows() {
67494
67551
  const sheetId = this.getters.getActiveSheetId();
67495
67552
  const viewports = this.getSubViewports(sheetId);
67496
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
67553
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
67497
67554
  }
67498
67555
  /**
67499
67556
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -67536,19 +67593,19 @@ stores.inject(MyMetaStore, storeInstance);
67536
67593
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
67537
67594
  };
67538
67595
  }
67539
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
67540
- const sheetId = this.getters.getActiveSheetId();
67541
- const visibleCols = this.getters.getSheetViewVisibleCols();
67542
- const visibleRows = this.getters.getSheetViewVisibleRows();
67543
- if (index < referenceIndex) {
67544
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
67596
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
67597
+ if (targetHeaderIndex < referenceHeaderIndex) {
67598
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
67545
67599
  }
67600
+ const sheetId = this.getters.getActiveSheetId();
67601
+ const visibleHeaders = dimension === "COL"
67602
+ ? this.getters.getSheetViewVisibleCols()
67603
+ : this.getters.getSheetViewVisibleRows();
67604
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
67605
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
67606
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
67546
67607
  let offset = 0;
67547
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
67548
- for (let i = referenceIndex; i < index; i++) {
67549
- if (!visibleIndexes.includes(i)) {
67550
- continue;
67551
- }
67608
+ for (const i of relevantIndexes) {
67552
67609
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
67553
67610
  }
67554
67611
  return offset;
@@ -67595,7 +67652,7 @@ stores.inject(MyMetaStore, storeInstance);
67595
67652
  }
67596
67653
  return { canEdgeScroll, direction, delay };
67597
67654
  }
67598
- getEdgeScrollRow(y, previousY, tartingY) {
67655
+ getEdgeScrollRow(y, previousY, startingY) {
67599
67656
  let canEdgeScroll = false;
67600
67657
  let direction = 0;
67601
67658
  let delay = 0;
@@ -67616,7 +67673,7 @@ stores.inject(MyMetaStore, storeInstance);
67616
67673
  delay = scrollDelay(y - height);
67617
67674
  direction = 1;
67618
67675
  }
67619
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
67676
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
67620
67677
  // 2
67621
67678
  canEdgeScroll = true;
67622
67679
  delay = scrollDelay(offsetCorrectionY - y);
@@ -67642,13 +67699,7 @@ stores.inject(MyMetaStore, storeInstance);
67642
67699
  */
67643
67700
  getVisibleRectWithoutHeaders(zone) {
67644
67701
  const sheetId = this.getters.getActiveSheetId();
67645
- const viewportRects = this.getSubViewports(sheetId)
67646
- .map((viewport) => viewport.getVisibleRect(zone))
67647
- .filter(isDefined);
67648
- if (viewportRects.length === 0) {
67649
- return { x: 0, y: 0, width: 0, height: 0 };
67650
- }
67651
- return this.recomposeRect(viewportRects);
67702
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
67652
67703
  }
67653
67704
  /**
67654
67705
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -67656,13 +67707,7 @@ stores.inject(MyMetaStore, storeInstance);
67656
67707
  */
67657
67708
  getRect(zone) {
67658
67709
  const sheetId = this.getters.getActiveSheetId();
67659
- const viewportRects = this.getSubViewports(sheetId)
67660
- .map((viewport) => viewport.getFullRect(zone))
67661
- .filter(isDefined);
67662
- if (viewportRects.length === 0) {
67663
- return { x: 0, y: 0, width: 0, height: 0 };
67664
- }
67665
- const rect = this.recomposeRect(viewportRects);
67710
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
67666
67711
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
67667
67712
  }
67668
67713
  /**
@@ -67707,9 +67752,18 @@ stores.inject(MyMetaStore, storeInstance);
67707
67752
  end: start + (isRowHidden ? 0 : size),
67708
67753
  };
67709
67754
  }
67710
- getAllActiveViewportsZones() {
67755
+ getAllActiveViewportsZonesAndRect() {
67711
67756
  const sheetId = this.getters.getActiveSheetId();
67712
- return this.getSubViewports(sheetId);
67757
+ return this.getSubViewports(sheetId).map((viewport) => {
67758
+ return {
67759
+ zone: viewport,
67760
+ rect: {
67761
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
67762
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
67763
+ ...viewport.getMaxSize(),
67764
+ },
67765
+ };
67766
+ });
67713
67767
  }
67714
67768
  // ---------------------------------------------------------------------------
67715
67769
  // Private
@@ -67768,12 +67822,11 @@ stores.inject(MyMetaStore, storeInstance);
67768
67822
  }
67769
67823
  /** gets rid of deprecated sheetIds */
67770
67824
  cleanViewports() {
67771
- const sheetIds = this.getters.getSheetIds();
67772
- for (let sheetId of Object.keys(this.viewports)) {
67773
- if (!sheetIds.includes(sheetId)) {
67774
- delete this.viewports[sheetId];
67775
- }
67825
+ const newViewport = {};
67826
+ for (const sheetId of this.getters.getSheetIds()) {
67827
+ newViewport[sheetId] = this.viewports[sheetId];
67776
67828
  }
67829
+ this.viewports = newViewport;
67777
67830
  }
67778
67831
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
67779
67832
  this.sheetViewHeight = height;
@@ -67783,7 +67836,7 @@ stores.inject(MyMetaStore, storeInstance);
67783
67836
  this.recomputeViewports();
67784
67837
  }
67785
67838
  recomputeViewports() {
67786
- for (let sheetId of Object.keys(this.viewports)) {
67839
+ for (const sheetId of this.getters.getSheetIds()) {
67787
67840
  this.resetViewports(sheetId);
67788
67841
  }
67789
67842
  }
@@ -67805,8 +67858,10 @@ stores.inject(MyMetaStore, storeInstance);
67805
67858
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
67806
67859
  const nCols = this.getters.getNumberCols(sheetId);
67807
67860
  const nRows = this.getters.getNumberRows(sheetId);
67808
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
67809
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
67861
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
67862
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
67863
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
67864
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
67810
67865
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
67811
67866
  const canScrollHorizontally = xRatio < 1.0;
67812
67867
  const canScrollVertically = yRatio < 1.0;
@@ -67817,14 +67872,14 @@ stores.inject(MyMetaStore, storeInstance);
67817
67872
  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 })) ||
67818
67873
  undefined,
67819
67874
  topRight: (ySplit &&
67820
- 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 })) ||
67875
+ 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 })) ||
67821
67876
  undefined,
67822
67877
  bottomLeft: (xSplit &&
67823
- 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 })) ||
67878
+ 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 })) ||
67824
67879
  undefined,
67825
67880
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
67826
- width: this.sheetViewWidth - colOffset,
67827
- height: this.sheetViewHeight - rowOffset,
67881
+ width: unfrozenWidth,
67882
+ height: unfrozenHeight,
67828
67883
  }, { canScrollHorizontally, canScrollVertically }, {
67829
67884
  x: canScrollHorizontally ? previousOffset.x : 0,
67830
67885
  y: canScrollVertically ? previousOffset.y : 0,
@@ -67901,12 +67956,26 @@ stores.inject(MyMetaStore, storeInstance);
67901
67956
  const height = this.sheetViewHeight + this.gridOffsetY;
67902
67957
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
67903
67958
  }
67904
- recomposeRect(viewportRects) {
67905
- const x = Math.min(...viewportRects.map((rect) => rect.x));
67906
- const y = Math.min(...viewportRects.map((rect) => rect.y));
67907
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
67908
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
67909
- return { x, y, width, height };
67959
+ mapViewportsToRect(sheetId, rectCallBack) {
67960
+ let x = Infinity;
67961
+ let y = Infinity;
67962
+ let width = 0;
67963
+ let height = 0;
67964
+ let hasViewports = false;
67965
+ for (const viewport of this.getSubViewports(sheetId)) {
67966
+ const rect = rectCallBack(viewport);
67967
+ if (rect) {
67968
+ hasViewports = true;
67969
+ x = Math.min(x, rect.x);
67970
+ y = Math.min(y, rect.y);
67971
+ width = Math.max(width, rect.x + rect.width);
67972
+ height = Math.max(height, rect.y + rect.height);
67973
+ }
67974
+ }
67975
+ if (!hasViewports) {
67976
+ return { x: 0, y: 0, width: 0, height: 0 };
67977
+ }
67978
+ return { x, y, width: width - x, height: height - y };
67910
67979
  }
67911
67980
  }
67912
67981
 
@@ -68903,7 +68972,7 @@ stores.inject(MyMetaStore, storeInstance);
68903
68972
  draggedItemId: sheetId,
68904
68973
  initialMousePosition: event.clientX,
68905
68974
  items: sheets,
68906
- containerEl: this.sheetListRef.el,
68975
+ scrollableContainerEl: this.sheetListRef.el,
68907
68976
  onDragEnd: (sheetId, finalIndex) => this.onDragEnd(sheetId, finalIndex),
68908
68977
  });
68909
68978
  }
@@ -75042,9 +75111,9 @@ stores.inject(MyMetaStore, storeInstance);
75042
75111
  exports.tokenize = tokenize;
75043
75112
 
75044
75113
 
75045
- __info__.version = "18.1.5";
75046
- __info__.date = "2025-01-31T08:00:10.263Z";
75047
- __info__.hash = "97acb8b";
75114
+ __info__.version = "18.1.7";
75115
+ __info__.date = "2025-02-10T09:00:28.556Z";
75116
+ __info__.hash = "338d8a1";
75048
75117
 
75049
75118
 
75050
75119
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);