@odoo/o-spreadsheet 18.1.4 → 18.1.6

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.4
6
- * @date 2025-01-29T06:27:32.901Z
7
- * @hash a11ef27
5
+ * @version 18.1.6
6
+ * @date 2025-02-05T07:18:57.089Z
7
+ * @hash f5b97e0
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -3348,11 +3348,11 @@ const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(l
3348
3348
  * number from the point of view of the isNumber function.
3349
3349
  */
3350
3350
  function parseNumber(str, locale) {
3351
+ // remove invaluable characters
3352
+ str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3351
3353
  if (locale.decimalSeparator !== ".") {
3352
3354
  str = str.replace(locale.decimalSeparator, ".");
3353
3355
  }
3354
- // remove invaluable characters
3355
- str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3356
3356
  let n = Number(str);
3357
3357
  if (isNaN(n) && str.includes("%")) {
3358
3358
  n = Number(str.split("%")[0]);
@@ -18454,19 +18454,20 @@ const HLOOKUP = {
18454
18454
  description: _t("Horizontal lookup"),
18455
18455
  args: [
18456
18456
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18457
- 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.")),
18457
+ 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.")),
18458
18458
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18459
18459
  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.")),
18460
18460
  ],
18461
18461
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18462
18462
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18463
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18463
+ const _range = toMatrix(range);
18464
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18464
18465
  const getValueFromRange = (range, index) => range[index][0].value;
18465
18466
  const _isSorted = toBoolean(isSorted.value);
18466
18467
  const colIndex = _isSorted
18467
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18468
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18469
- const col = range[colIndex];
18468
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18469
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18470
+ const col = _range[colIndex];
18470
18471
  if (col === undefined) {
18471
18472
  return valueNotAvailable(searchKey);
18472
18473
  }
@@ -18558,35 +18559,37 @@ const LOOKUP = {
18558
18559
  description: _t("Look up a value."),
18559
18560
  args: [
18560
18561
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18561
- 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.")),
18562
- 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.")),
18562
+ 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.")),
18563
+ 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.")),
18563
18564
  ],
18564
18565
  compute: function (searchKey, searchArray, resultRange) {
18565
- let nbCol = searchArray.length;
18566
- let nbRow = searchArray[0].length;
18566
+ const _searchArray = toMatrix(searchArray);
18567
+ const _resultRange = toMatrix(resultRange);
18568
+ let nbCol = _searchArray.length;
18569
+ let nbRow = _searchArray[0].length;
18567
18570
  const verticalSearch = nbRow >= nbCol;
18568
18571
  const getElement = verticalSearch
18569
18572
  ? (range, index) => range[0][index].value
18570
18573
  : (range, index) => range[index][0].value;
18571
18574
  const rangeLength = verticalSearch ? nbRow : nbCol;
18572
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18575
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18573
18576
  if (index === -1 ||
18574
- (verticalSearch && searchArray[0][index] === undefined) ||
18575
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18577
+ (verticalSearch && _searchArray[0][index] === undefined) ||
18578
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
18576
18579
  return valueNotAvailable(searchKey);
18577
18580
  }
18578
- if (resultRange === undefined) {
18579
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18581
+ if (_resultRange[0].length === 0) {
18582
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
18580
18583
  }
18581
- nbCol = resultRange.length;
18582
- nbRow = resultRange[0].length;
18584
+ nbCol = _resultRange.length;
18585
+ nbRow = _resultRange[0].length;
18583
18586
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
18584
18587
  if (nbCol > 1) {
18585
18588
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
18586
- return resultRange[index][0];
18589
+ return _resultRange[index][0];
18587
18590
  }
18588
18591
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
18589
- return resultRange[0][index];
18592
+ return _resultRange[0][index];
18590
18593
  },
18591
18594
  isExported: true,
18592
18595
  };
@@ -18603,28 +18606,29 @@ const MATCH = {
18603
18606
  ],
18604
18607
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
18605
18608
  let _searchType = toNumber(searchType, this.locale);
18606
- const nbCol = range.length;
18607
- const nbRow = range[0].length;
18609
+ const _range = toMatrix(range);
18610
+ const nbCol = _range.length;
18611
+ const nbRow = _range[0].length;
18608
18612
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
18609
18613
  let index = -1;
18610
18614
  const getElement = nbCol === 1
18611
- ? (range, index) => range[0][index].value
18612
- : (range, index) => range[index][0].value;
18613
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
18615
+ ? (_range, index) => _range[0][index].value
18616
+ : (_range, index) => _range[index][0].value;
18617
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
18614
18618
  _searchType = Math.sign(_searchType);
18615
18619
  switch (_searchType) {
18616
18620
  case 1:
18617
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18621
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18618
18622
  break;
18619
18623
  case 0:
18620
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
18624
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18621
18625
  break;
18622
18626
  case -1:
18623
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18627
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18624
18628
  break;
18625
18629
  }
18626
- if ((nbCol === 1 && range[0][index] === undefined) ||
18627
- (nbCol !== 1 && range[index] === undefined)) {
18630
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
18631
+ (nbCol !== 1 && _range[index] === undefined)) {
18628
18632
  return valueNotAvailable(searchKey);
18629
18633
  }
18630
18634
  return index + 1;
@@ -18679,13 +18683,14 @@ const VLOOKUP = {
18679
18683
  ],
18680
18684
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18681
18685
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18682
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18686
+ const _range = toMatrix(range);
18687
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18683
18688
  const getValueFromRange = (range, index) => range[0][index].value;
18684
18689
  const _isSorted = toBoolean(isSorted.value);
18685
18690
  const rowIndex = _isSorted
18686
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18687
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
18688
- const value = range[_index - 1][rowIndex];
18691
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18692
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18693
+ const value = _range[_index - 1][rowIndex];
18689
18694
  if (value === undefined) {
18690
18695
  return valueNotAvailable(searchKey);
18691
18696
  }
@@ -18722,27 +18727,29 @@ const XLOOKUP = {
18722
18727
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18723
18728
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18724
18729
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18725
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18730
+ const _lookupRange = toMatrix(lookupRange);
18731
+ const _returnRange = toMatrix(returnRange);
18732
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18726
18733
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18727
18734
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18728
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18735
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18729
18736
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18730
18737
  assert(() => lookupDirection === "col"
18731
- ? returnRange[0].length === lookupRange[0].length
18732
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18738
+ ? _returnRange[0].length === _lookupRange[0].length
18739
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18733
18740
  const getElement = lookupDirection === "col"
18734
18741
  ? (range, index) => range[0][index].value
18735
18742
  : (range, index) => range[index][0].value;
18736
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18743
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18737
18744
  const mode = MATCH_MODE[_matchMode];
18738
18745
  const reverseSearch = _searchMode === -1;
18739
18746
  const index = _searchMode === 2 || _searchMode === -2
18740
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18741
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18747
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18748
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18742
18749
  if (index !== -1) {
18743
18750
  return lookupDirection === "col"
18744
- ? returnRange.map((col) => [col[index]])
18745
- : [returnRange[index]];
18751
+ ? _returnRange.map((col) => [col[index]])
18752
+ : [_returnRange[index]];
18746
18753
  }
18747
18754
  if (defaultValue === undefined) {
18748
18755
  return valueNotAvailable(searchKey);
@@ -28434,11 +28441,7 @@ function interpolateData(config, values, labels, newLabels) {
28434
28441
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
28435
28442
  return [];
28436
28443
  }
28437
- const labelMin = Math.min(...labels);
28438
- const labelMax = Math.max(...labels);
28439
- const labelRange = labelMax - labelMin;
28440
- const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
28441
- const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
28444
+ const { normalizedLabels, normalizedNewLabels } = normalizeLabels(labels, newLabels, config);
28442
28445
  try {
28443
28446
  switch (config.type) {
28444
28447
  case "polynomial": {
@@ -28483,6 +28486,30 @@ function interpolateData(config, values, labels, newLabels) {
28483
28486
  return newLabels.map((x) => ({ x, y: NaN }));
28484
28487
  }
28485
28488
  }
28489
+ function normalizeLabels(labels, newLabels, config) {
28490
+ let normalizedLabels = [];
28491
+ let normalizedNewLabels = [];
28492
+ if (config.type === "logarithmic") {
28493
+ // Logarithmic trends in charts are used to visualize proportional growth or
28494
+ // relative changes. Therefore, we change the normalization technique for
28495
+ // logarithmic trend lines for a better fit. The method used here is Max Absolute
28496
+ // Scaling. This Technique is ideal for data spanning several orders of magnitude,
28497
+ // as it balances differences between small and large values by compressing larger
28498
+ // values while preserving proportionality and ensuring all values are scaled relative
28499
+ // to the largest magnitude.
28500
+ const labelMax = Math.max(...labels.map(Math.abs));
28501
+ normalizedLabels = labels.map((l) => l / labelMax);
28502
+ normalizedNewLabels = newLabels.map((l) => l / labelMax);
28503
+ }
28504
+ else {
28505
+ const labelMax = Math.max(...labels);
28506
+ const labelMin = Math.min(...labels);
28507
+ const labelRange = labelMax - labelMin;
28508
+ normalizedLabels = labels.map((l) => (l - labelMax) / labelRange);
28509
+ normalizedNewLabels = newLabels.map((l) => (l - labelMax) / labelRange);
28510
+ }
28511
+ return { normalizedLabels, normalizedNewLabels };
28512
+ }
28486
28513
  function getChartAxisType(chart, labelRange, getters) {
28487
28514
  if (isDateChart(chart, labelRange, getters) && isLuxonTimeAdapterInstalled()) {
28488
28515
  return "time";
@@ -40367,6 +40394,12 @@ class Composer extends Component {
40367
40394
  }
40368
40395
  this.contentHelper.updateEl(el);
40369
40396
  });
40397
+ this.env.model.selection.observe(this, {
40398
+ handleEvent: () => this.autoCompleteState.hide(),
40399
+ });
40400
+ onWillUnmount(() => {
40401
+ this.env.model.selection.detachObserver(this);
40402
+ });
40370
40403
  useEffect(() => {
40371
40404
  this.processContent();
40372
40405
  if (document.activeElement === this.contentHelper.el &&
@@ -50480,11 +50513,10 @@ class GridRenderer {
50480
50513
  switch (layer) {
50481
50514
  case "Background":
50482
50515
  this.drawGlobalBackground(renderingContext);
50483
- for (const zone of this.getters.getAllActiveViewportsZones()) {
50516
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
50484
50517
  const { ctx } = renderingContext;
50485
50518
  ctx.save();
50486
50519
  ctx.beginPath();
50487
- const rect = this.getters.getVisibleRect(zone);
50488
50520
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
50489
50521
  ctx.clip();
50490
50522
  const boxes = this.getGridBoxes(zone);
@@ -50760,10 +50792,8 @@ class GridRenderer {
50760
50792
  const { ctx, thinLineWidth } = renderingContext;
50761
50793
  const visibleCols = this.getters.getSheetViewVisibleCols();
50762
50794
  const left = visibleCols[0];
50763
- const right = visibleCols[visibleCols.length - 1];
50764
50795
  const visibleRows = this.getters.getSheetViewVisibleRows();
50765
50796
  const top = visibleRows[0];
50766
- const bottom = visibleRows[visibleRows.length - 1];
50767
50797
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
50768
50798
  const selection = this.getters.getSelectedZones();
50769
50799
  const selectedCols = getZonesCols(selection);
@@ -50779,7 +50809,7 @@ class GridRenderer {
50779
50809
  ctx.lineWidth = thinLineWidth;
50780
50810
  ctx.strokeStyle = "#333";
50781
50811
  // Columns headers background
50782
- for (let col = left; col <= right; col++) {
50812
+ for (const col of visibleCols) {
50783
50813
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
50784
50814
  const { x, width } = this.getters.getVisibleRect(colZone);
50785
50815
  const isColActive = activeCols.has(col);
@@ -50796,7 +50826,7 @@ class GridRenderer {
50796
50826
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
50797
50827
  }
50798
50828
  // Rows headers background
50799
- for (let row = top; row <= bottom; row++) {
50829
+ for (const row of visibleRows) {
50800
50830
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
50801
50831
  const { y, height } = this.getters.getVisibleRect(rowZone);
50802
50832
  const isRowActive = activeRows.has(row);
@@ -50822,21 +50852,21 @@ class GridRenderer {
50822
50852
  ctx.stroke();
50823
50853
  ctx.beginPath();
50824
50854
  // column text + separator
50825
- for (const i of visibleCols) {
50826
- const colSize = this.getters.getColSize(sheetId, i);
50827
- const colName = numberToLetters(i);
50828
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
50829
- let colStart = this.getHeaderOffset("COL", left, i);
50855
+ for (const col of visibleCols) {
50856
+ const colSize = this.getters.getColSize(sheetId, col);
50857
+ const colName = numberToLetters(col);
50858
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
50859
+ let colStart = this.getHeaderOffset("COL", left, col);
50830
50860
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
50831
50861
  ctx.moveTo(colStart + colSize, 0);
50832
50862
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
50833
50863
  }
50834
50864
  // row text + separator
50835
- for (const i of visibleRows) {
50836
- const rowSize = this.getters.getRowSize(sheetId, i);
50837
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
50838
- let rowStart = this.getHeaderOffset("ROW", top, i);
50839
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
50865
+ for (const row of visibleRows) {
50866
+ const rowSize = this.getters.getRowSize(sheetId, row);
50867
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
50868
+ let rowStart = this.getHeaderOffset("ROW", top, row);
50869
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
50840
50870
  ctx.moveTo(0, rowStart + rowSize);
50841
50871
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
50842
50872
  }
@@ -51143,6 +51173,9 @@ function useGridDrawing(refName, model, canvasSize) {
51143
51173
  canvas.width = width * dpr;
51144
51174
  canvas.height = height * dpr;
51145
51175
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
51176
+ if (width === 0 || height === 0) {
51177
+ return;
51178
+ }
51146
51179
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
51147
51180
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
51148
51181
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -52689,7 +52722,7 @@ class BordersPlugin extends CorePlugin {
52689
52722
  getCommonSides(border1, border2) {
52690
52723
  const commonBorder = {};
52691
52724
  for (let side of ["top", "bottom", "left", "right"]) {
52692
- if (border1[side] && border1[side] === border2[side]) {
52725
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52693
52726
  commonBorder[side] = border1[side];
52694
52727
  }
52695
52728
  }
@@ -61318,7 +61351,7 @@ function withPivotPresentationLayer (PivotClass) {
61318
61351
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
61319
61352
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
61320
61353
  }
61321
- return this._getPivotCellValueAndFormat(symbolName, domain);
61354
+ return this.getPivotCellValueAndFormat(symbolName, domain);
61322
61355
  };
61323
61356
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
61324
61357
  if (isMatrix(result)) {
@@ -64489,14 +64522,12 @@ class SheetUIPlugin extends UIPlugin {
64489
64522
  }
64490
64523
  break;
64491
64524
  case "AUTORESIZE_ROWS":
64492
- for (let row of cmd.rows) {
64493
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64494
- elements: [row],
64495
- dimension: "ROW",
64496
- size: null,
64497
- sheetId: cmd.sheetId,
64498
- });
64499
- }
64525
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
64526
+ elements: cmd.rows,
64527
+ dimension: "ROW",
64528
+ size: null,
64529
+ sheetId: cmd.sheetId,
64530
+ });
64500
64531
  break;
64501
64532
  }
64502
64533
  }
@@ -66907,8 +66938,17 @@ class InternalViewport {
66907
66938
  this.getters = getters;
66908
66939
  this.sheetId = sheetId;
66909
66940
  this.boundaries = boundaries;
66910
- this.viewportWidth = sizeInGrid.width;
66911
- this.viewportHeight = sizeInGrid.height;
66941
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
66942
+ throw new Error("Viewport size cannot be negative");
66943
+ }
66944
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
66945
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
66946
+ this.top = boundaries.top;
66947
+ this.bottom = boundaries.bottom;
66948
+ this.left = boundaries.left;
66949
+ this.right = boundaries.right;
66950
+ this.offsetX = offsets.x;
66951
+ this.offsetY = offsets.y;
66912
66952
  this.offsetScrollbarX = offsets.x;
66913
66953
  this.offsetScrollbarY = offsets.y;
66914
66954
  this.canScrollVertically = options.canScrollVertically;
@@ -66951,9 +66991,9 @@ class InternalViewport {
66951
66991
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
66952
66992
  );
66953
66993
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
66954
- }
66955
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
66956
- height += FOOTER_HEIGHT;
66994
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
66995
+ height += FOOTER_HEIGHT;
66996
+ }
66957
66997
  }
66958
66998
  return { width, height };
66959
66999
  }
@@ -67094,6 +67134,9 @@ class InternalViewport {
67094
67134
  !this.getters.isRowHidden(this.sheetId, row));
67095
67135
  }
67096
67136
  searchHeaderIndex(dimension, position, startIndex = 0) {
67137
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
67138
+ return -1;
67139
+ }
67097
67140
  const sheetId = this.sheetId;
67098
67141
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
67099
67142
  // using a binary search:
@@ -67130,7 +67173,7 @@ class InternalViewport {
67130
67173
  this.adjustViewportZoneY();
67131
67174
  }
67132
67175
  /** Corrects the viewport's horizontal offset based on the current structure
67133
- * To make sure that at least on column is visible inside the viewport.
67176
+ * To make sure that at least one column is visible inside the viewport.
67134
67177
  */
67135
67178
  adjustViewportOffsetX() {
67136
67179
  if (this.canScrollHorizontally) {
@@ -67142,7 +67185,7 @@ class InternalViewport {
67142
67185
  this.adjustViewportZoneX();
67143
67186
  }
67144
67187
  /** Corrects the viewport's vertical offset based on the current structure
67145
- * To make sure that at least on row is visible inside the viewport.
67188
+ * To make sure that at least one row is visible inside the viewport.
67146
67189
  */
67147
67190
  adjustViewportOffsetY() {
67148
67191
  if (this.canScrollVertically) {
@@ -67159,11 +67202,14 @@ class InternalViewport {
67159
67202
  const sheetId = this.sheetId;
67160
67203
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
67161
67204
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
67205
+ if (!this.viewportWidth) {
67206
+ return;
67207
+ }
67162
67208
  if (this.left === -1) {
67163
67209
  this.left = this.boundaries.left;
67164
67210
  }
67165
67211
  if (this.right === -1) {
67166
- this.right = this.getters.getNumberCols(sheetId) - 1;
67212
+ this.right = this.boundaries.right;
67167
67213
  }
67168
67214
  this.offsetX =
67169
67215
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -67175,11 +67221,14 @@ class InternalViewport {
67175
67221
  const sheetId = this.sheetId;
67176
67222
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
67177
67223
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
67224
+ if (!this.viewportHeight) {
67225
+ return;
67226
+ }
67178
67227
  if (this.top === -1) {
67179
67228
  this.top = this.boundaries.top;
67180
67229
  }
67181
67230
  if (this.bottom === -1) {
67182
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
67231
+ this.bottom = this.boundaries.bottom;
67183
67232
  }
67184
67233
  this.offsetY =
67185
67234
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -67253,7 +67302,7 @@ class SheetViewPlugin extends UIPlugin {
67253
67302
  "isPositionVisible",
67254
67303
  "getColDimensionsInViewport",
67255
67304
  "getRowDimensionsInViewport",
67256
- "getAllActiveViewportsZones",
67305
+ "getAllActiveViewportsZonesAndRect",
67257
67306
  "getRect",
67258
67307
  ];
67259
67308
  viewports = {};
@@ -67483,12 +67532,12 @@ class SheetViewPlugin extends UIPlugin {
67483
67532
  const sheetId = this.getters.getActiveSheetId();
67484
67533
  const viewports = this.getSubViewports(sheetId);
67485
67534
  //TODO ake another commit to eimprove this
67486
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
67535
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
67487
67536
  }
67488
67537
  getSheetViewVisibleRows() {
67489
67538
  const sheetId = this.getters.getActiveSheetId();
67490
67539
  const viewports = this.getSubViewports(sheetId);
67491
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
67540
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
67492
67541
  }
67493
67542
  /**
67494
67543
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -67531,19 +67580,19 @@ class SheetViewPlugin extends UIPlugin {
67531
67580
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
67532
67581
  };
67533
67582
  }
67534
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
67535
- const sheetId = this.getters.getActiveSheetId();
67536
- const visibleCols = this.getters.getSheetViewVisibleCols();
67537
- const visibleRows = this.getters.getSheetViewVisibleRows();
67538
- if (index < referenceIndex) {
67539
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
67583
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
67584
+ if (targetHeaderIndex < referenceHeaderIndex) {
67585
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
67540
67586
  }
67587
+ const sheetId = this.getters.getActiveSheetId();
67588
+ const visibleHeaders = dimension === "COL"
67589
+ ? this.getters.getSheetViewVisibleCols()
67590
+ : this.getters.getSheetViewVisibleRows();
67591
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
67592
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
67593
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
67541
67594
  let offset = 0;
67542
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
67543
- for (let i = referenceIndex; i < index; i++) {
67544
- if (!visibleIndexes.includes(i)) {
67545
- continue;
67546
- }
67595
+ for (const i of relevantIndexes) {
67547
67596
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
67548
67597
  }
67549
67598
  return offset;
@@ -67590,7 +67639,7 @@ class SheetViewPlugin extends UIPlugin {
67590
67639
  }
67591
67640
  return { canEdgeScroll, direction, delay };
67592
67641
  }
67593
- getEdgeScrollRow(y, previousY, tartingY) {
67642
+ getEdgeScrollRow(y, previousY, startingY) {
67594
67643
  let canEdgeScroll = false;
67595
67644
  let direction = 0;
67596
67645
  let delay = 0;
@@ -67611,7 +67660,7 @@ class SheetViewPlugin extends UIPlugin {
67611
67660
  delay = scrollDelay(y - height);
67612
67661
  direction = 1;
67613
67662
  }
67614
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
67663
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
67615
67664
  // 2
67616
67665
  canEdgeScroll = true;
67617
67666
  delay = scrollDelay(offsetCorrectionY - y);
@@ -67637,13 +67686,7 @@ class SheetViewPlugin extends UIPlugin {
67637
67686
  */
67638
67687
  getVisibleRectWithoutHeaders(zone) {
67639
67688
  const sheetId = this.getters.getActiveSheetId();
67640
- const viewportRects = this.getSubViewports(sheetId)
67641
- .map((viewport) => viewport.getVisibleRect(zone))
67642
- .filter(isDefined);
67643
- if (viewportRects.length === 0) {
67644
- return { x: 0, y: 0, width: 0, height: 0 };
67645
- }
67646
- return this.recomposeRect(viewportRects);
67689
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
67647
67690
  }
67648
67691
  /**
67649
67692
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -67651,13 +67694,7 @@ class SheetViewPlugin extends UIPlugin {
67651
67694
  */
67652
67695
  getRect(zone) {
67653
67696
  const sheetId = this.getters.getActiveSheetId();
67654
- const viewportRects = this.getSubViewports(sheetId)
67655
- .map((viewport) => viewport.getFullRect(zone))
67656
- .filter(isDefined);
67657
- if (viewportRects.length === 0) {
67658
- return { x: 0, y: 0, width: 0, height: 0 };
67659
- }
67660
- const rect = this.recomposeRect(viewportRects);
67697
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
67661
67698
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
67662
67699
  }
67663
67700
  /**
@@ -67702,9 +67739,18 @@ class SheetViewPlugin extends UIPlugin {
67702
67739
  end: start + (isRowHidden ? 0 : size),
67703
67740
  };
67704
67741
  }
67705
- getAllActiveViewportsZones() {
67742
+ getAllActiveViewportsZonesAndRect() {
67706
67743
  const sheetId = this.getters.getActiveSheetId();
67707
- return this.getSubViewports(sheetId);
67744
+ return this.getSubViewports(sheetId).map((viewport) => {
67745
+ return {
67746
+ zone: viewport,
67747
+ rect: {
67748
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
67749
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
67750
+ ...viewport.getMaxSize(),
67751
+ },
67752
+ };
67753
+ });
67708
67754
  }
67709
67755
  // ---------------------------------------------------------------------------
67710
67756
  // Private
@@ -67763,12 +67809,11 @@ class SheetViewPlugin extends UIPlugin {
67763
67809
  }
67764
67810
  /** gets rid of deprecated sheetIds */
67765
67811
  cleanViewports() {
67766
- const sheetIds = this.getters.getSheetIds();
67767
- for (let sheetId of Object.keys(this.viewports)) {
67768
- if (!sheetIds.includes(sheetId)) {
67769
- delete this.viewports[sheetId];
67770
- }
67812
+ const newViewport = {};
67813
+ for (const sheetId of this.getters.getSheetIds()) {
67814
+ newViewport[sheetId] = this.viewports[sheetId];
67771
67815
  }
67816
+ this.viewports = newViewport;
67772
67817
  }
67773
67818
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
67774
67819
  this.sheetViewHeight = height;
@@ -67778,7 +67823,7 @@ class SheetViewPlugin extends UIPlugin {
67778
67823
  this.recomputeViewports();
67779
67824
  }
67780
67825
  recomputeViewports() {
67781
- for (let sheetId of Object.keys(this.viewports)) {
67826
+ for (const sheetId of this.getters.getSheetIds()) {
67782
67827
  this.resetViewports(sheetId);
67783
67828
  }
67784
67829
  }
@@ -67800,8 +67845,10 @@ class SheetViewPlugin extends UIPlugin {
67800
67845
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
67801
67846
  const nCols = this.getters.getNumberCols(sheetId);
67802
67847
  const nRows = this.getters.getNumberRows(sheetId);
67803
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
67804
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
67848
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
67849
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
67850
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
67851
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
67805
67852
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
67806
67853
  const canScrollHorizontally = xRatio < 1.0;
67807
67854
  const canScrollVertically = yRatio < 1.0;
@@ -67812,14 +67859,14 @@ class SheetViewPlugin extends UIPlugin {
67812
67859
  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 })) ||
67813
67860
  undefined,
67814
67861
  topRight: (ySplit &&
67815
- 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 })) ||
67862
+ 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 })) ||
67816
67863
  undefined,
67817
67864
  bottomLeft: (xSplit &&
67818
- 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 })) ||
67865
+ 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 })) ||
67819
67866
  undefined,
67820
67867
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
67821
- width: this.sheetViewWidth - colOffset,
67822
- height: this.sheetViewHeight - rowOffset,
67868
+ width: unfrozenWidth,
67869
+ height: unfrozenHeight,
67823
67870
  }, { canScrollHorizontally, canScrollVertically }, {
67824
67871
  x: canScrollHorizontally ? previousOffset.x : 0,
67825
67872
  y: canScrollVertically ? previousOffset.y : 0,
@@ -67896,12 +67943,26 @@ class SheetViewPlugin extends UIPlugin {
67896
67943
  const height = this.sheetViewHeight + this.gridOffsetY;
67897
67944
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
67898
67945
  }
67899
- recomposeRect(viewportRects) {
67900
- const x = Math.min(...viewportRects.map((rect) => rect.x));
67901
- const y = Math.min(...viewportRects.map((rect) => rect.y));
67902
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
67903
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
67904
- return { x, y, width, height };
67946
+ mapViewportsToRect(sheetId, rectCallBack) {
67947
+ let x = Infinity;
67948
+ let y = Infinity;
67949
+ let width = 0;
67950
+ let height = 0;
67951
+ let hasViewports = false;
67952
+ for (const viewport of this.getSubViewports(sheetId)) {
67953
+ const rect = rectCallBack(viewport);
67954
+ if (rect) {
67955
+ hasViewports = true;
67956
+ x = Math.min(x, rect.x);
67957
+ y = Math.min(y, rect.y);
67958
+ width = Math.max(width, rect.x + rect.width);
67959
+ height = Math.max(height, rect.y + rect.height);
67960
+ }
67961
+ }
67962
+ if (!hasViewports) {
67963
+ return { x: 0, y: 0, width: 0, height: 0 };
67964
+ }
67965
+ return { x, y, width: width - x, height: height - y };
67905
67966
  }
67906
67967
  }
67907
67968
 
@@ -71520,6 +71581,9 @@ class EventStream {
71520
71581
  observe(owner, callbacks) {
71521
71582
  this.observers.set(owner, { owner, callbacks });
71522
71583
  }
71584
+ detachObserver(owner) {
71585
+ this.observers.delete(owner);
71586
+ }
71523
71587
  /**
71524
71588
  * Capture the stream for yourself
71525
71589
  */
@@ -71612,6 +71676,9 @@ class SelectionStreamProcessorImpl {
71612
71676
  observe(owner, callbacks) {
71613
71677
  this.stream.observe(owner, callbacks);
71614
71678
  }
71679
+ detachObserver(owner) {
71680
+ this.stream.detachObserver(owner);
71681
+ }
71615
71682
  release(owner) {
71616
71683
  if (this.stream.isListening(owner)) {
71617
71684
  this.stream.release(owner);
@@ -74987,6 +75054,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
74987
75054
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
74988
75055
 
74989
75056
 
74990
- __info__.version = "18.1.4";
74991
- __info__.date = "2025-01-29T06:27:32.901Z";
74992
- __info__.hash = "a11ef27";
75057
+ __info__.version = "18.1.6";
75058
+ __info__.date = "2025-02-05T07:18:57.089Z";
75059
+ __info__.hash = "f5b97e0";