@odoo/o-spreadsheet 18.0.12 → 18.0.14

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.12
6
- * @date 2025-01-29T06:24:22.122Z
7
- * @hash a881cff
5
+ * @version 18.0.14
6
+ * @date 2025-02-05T06:47:33.041Z
7
+ * @hash 90f2af4
8
8
  */
9
9
 
10
10
  'use strict';
@@ -3185,11 +3185,11 @@ const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(l
3185
3185
  * number from the point of view of the isNumber function.
3186
3186
  */
3187
3187
  function parseNumber(str, locale) {
3188
+ // remove invaluable characters
3189
+ str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3188
3190
  if (locale.decimalSeparator !== ".") {
3189
3191
  str = str.replace(locale.decimalSeparator, ".");
3190
3192
  }
3191
- // remove invaluable characters
3192
- str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3193
3193
  let n = Number(str);
3194
3194
  if (isNaN(n) && str.includes("%")) {
3195
3195
  n = Number(str.split("%")[0]);
@@ -4284,7 +4284,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4284
4284
  * @param reverseSearch if true, search in the array starting from the end.
4285
4285
 
4286
4286
  */
4287
- function linearSearch(data, target, mode, numberOfValues, getValueInData, reverseSearch = false) {
4287
+ function linearSearch(data, target, mode, numberOfValues, getValueInData, lookupCaches, reverseSearch = false) {
4288
4288
  if (target === undefined || target.value === null) {
4289
4289
  return -1;
4290
4290
  }
@@ -4293,17 +4293,48 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4293
4293
  }
4294
4294
  const _target = normalizeValue(target.value);
4295
4295
  const getValue = reverseSearch
4296
- ? (data, i) => getValueInData(data, numberOfValues - i - 1)
4297
- : getValueInData;
4296
+ ? (data, i) => normalizeValue(getValueInData(data, numberOfValues - i - 1))
4297
+ : (data, i) => normalizeValue(getValueInData(data, i));
4298
+ // first check if the target is in the cache
4299
+ const isNotWildcardTarget = mode !== "wildcard" ||
4300
+ typeof _target !== "string" ||
4301
+ !(_target.includes("*") || _target.includes("?"));
4302
+ if (lookupCaches && isNotWildcardTarget) {
4303
+ const searchMode = reverseSearch ? "reverseSearch" : "forwardSearch";
4304
+ let cache = lookupCaches[searchMode].get(data);
4305
+ if (cache === undefined) {
4306
+ // build the cache for all the values
4307
+ cache = new Map();
4308
+ for (let i = 0; i < numberOfValues; i++) {
4309
+ const value = getValue(data, i) ?? null;
4310
+ if (!cache.has(value)) {
4311
+ cache.set(value, i);
4312
+ }
4313
+ }
4314
+ lookupCaches[searchMode].set(data, cache);
4315
+ }
4316
+ if (cache.has(_target)) {
4317
+ const resultIndex = cache.get(_target);
4318
+ return reverseSearch ? numberOfValues - resultIndex - 1 : resultIndex;
4319
+ }
4320
+ if (mode === "strict") {
4321
+ return -1;
4322
+ }
4323
+ }
4324
+ // else perform the linear search
4325
+ const resultIndex = _linearSearch(data, _target, mode, numberOfValues, getValue);
4326
+ return reverseSearch && resultIndex !== -1 ? numberOfValues - resultIndex - 1 : resultIndex;
4327
+ }
4328
+ function _linearSearch(data, _target, mode, numberOfValues, getNormalizeValue) {
4298
4329
  let indexMatchTarget = (i) => {
4299
- return normalizeValue(getValue(data, i)) === _target;
4330
+ return getNormalizeValue(data, i) === _target;
4300
4331
  };
4301
4332
  if (mode === "wildcard" &&
4302
4333
  typeof _target === "string" &&
4303
4334
  (_target.includes("*") || _target.includes("?"))) {
4304
4335
  const regExp = wildcardToRegExp(_target);
4305
4336
  indexMatchTarget = (i) => {
4306
- const value = normalizeValue(getValue(data, i));
4337
+ const value = getNormalizeValue(data, i);
4307
4338
  if (typeof value === "string") {
4308
4339
  return regExp.test(value);
4309
4340
  }
@@ -4314,7 +4345,7 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4314
4345
  let closestMatchIndex = -1;
4315
4346
  if (mode === "nextSmaller") {
4316
4347
  indexMatchTarget = (i) => {
4317
- const value = normalizeValue(getValue(data, i));
4348
+ const value = getNormalizeValue(data, i);
4318
4349
  if ((!closestMatch && compareCellValues(_target, value) >= 0) ||
4319
4350
  (compareCellValues(_target, value) >= 0 && compareCellValues(value, closestMatch) > 0)) {
4320
4351
  closestMatch = value;
@@ -4325,7 +4356,7 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4325
4356
  }
4326
4357
  if (mode === "nextGreater") {
4327
4358
  indexMatchTarget = (i) => {
4328
- const value = normalizeValue(getValue(data, i));
4359
+ const value = getNormalizeValue(data, i);
4329
4360
  if ((!closestMatch && compareCellValues(_target, value) <= 0) ||
4330
4361
  (compareCellValues(_target, value) <= 0 && compareCellValues(value, closestMatch) < 0)) {
4331
4362
  closestMatch = value;
@@ -4336,12 +4367,10 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
4336
4367
  }
4337
4368
  for (let i = 0; i < numberOfValues; i++) {
4338
4369
  if (indexMatchTarget(i)) {
4339
- return reverseSearch ? numberOfValues - i - 1 : i;
4370
+ return i;
4340
4371
  }
4341
4372
  }
4342
- return reverseSearch && closestMatchIndex !== -1
4343
- ? numberOfValues - closestMatchIndex - 1
4344
- : closestMatchIndex;
4373
+ return closestMatchIndex;
4345
4374
  }
4346
4375
  /**
4347
4376
  * Normalize a value.
@@ -18355,19 +18384,20 @@ const HLOOKUP = {
18355
18384
  description: _t("Horizontal lookup"),
18356
18385
  args: [
18357
18386
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18358
- 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.")),
18387
+ 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.")),
18359
18388
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18360
18389
  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.")),
18361
18390
  ],
18362
18391
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18363
18392
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18364
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18393
+ const _range = toMatrix(range);
18394
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18365
18395
  const getValueFromRange = (range, index) => range[index][0].value;
18366
18396
  const _isSorted = toBoolean(isSorted.value);
18367
18397
  const colIndex = _isSorted
18368
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18369
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18370
- const col = range[colIndex];
18398
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18399
+ : linearSearch(range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches);
18400
+ const col = _range[colIndex];
18371
18401
  if (col === undefined) {
18372
18402
  return valueNotAvailable(searchKey);
18373
18403
  }
@@ -18459,35 +18489,37 @@ const LOOKUP = {
18459
18489
  description: _t("Look up a value."),
18460
18490
  args: [
18461
18491
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18462
- 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.")),
18463
- 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.")),
18492
+ 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.")),
18493
+ 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.")),
18464
18494
  ],
18465
18495
  compute: function (searchKey, searchArray, resultRange) {
18466
- let nbCol = searchArray.length;
18467
- let nbRow = searchArray[0].length;
18496
+ const _searchArray = toMatrix(searchArray);
18497
+ const _resultRange = toMatrix(resultRange);
18498
+ let nbCol = _searchArray.length;
18499
+ let nbRow = _searchArray[0].length;
18468
18500
  const verticalSearch = nbRow >= nbCol;
18469
18501
  const getElement = verticalSearch
18470
18502
  ? (range, index) => range[0][index].value
18471
18503
  : (range, index) => range[index][0].value;
18472
18504
  const rangeLength = verticalSearch ? nbRow : nbCol;
18473
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18505
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18474
18506
  if (index === -1 ||
18475
- (verticalSearch && searchArray[0][index] === undefined) ||
18476
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18507
+ (verticalSearch && _searchArray[0][index] === undefined) ||
18508
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
18477
18509
  return valueNotAvailable(searchKey);
18478
18510
  }
18479
- if (resultRange === undefined) {
18480
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18511
+ if (_resultRange[0].length === 0) {
18512
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
18481
18513
  }
18482
- nbCol = resultRange.length;
18483
- nbRow = resultRange[0].length;
18514
+ nbCol = _resultRange.length;
18515
+ nbRow = _resultRange[0].length;
18484
18516
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
18485
18517
  if (nbCol > 1) {
18486
18518
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
18487
- return resultRange[index][0];
18519
+ return _resultRange[index][0];
18488
18520
  }
18489
18521
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
18490
- return resultRange[0][index];
18522
+ return _resultRange[0][index];
18491
18523
  },
18492
18524
  isExported: true,
18493
18525
  };
@@ -18504,28 +18536,29 @@ const MATCH = {
18504
18536
  ],
18505
18537
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
18506
18538
  let _searchType = toNumber(searchType, this.locale);
18507
- const nbCol = range.length;
18508
- const nbRow = range[0].length;
18539
+ const _range = toMatrix(range);
18540
+ const nbCol = _range.length;
18541
+ const nbRow = _range[0].length;
18509
18542
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
18510
18543
  let index = -1;
18511
18544
  const getElement = nbCol === 1
18512
- ? (range, index) => range[0][index].value
18513
- : (range, index) => range[index][0].value;
18514
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
18545
+ ? (_range, index) => _range[0][index].value
18546
+ : (_range, index) => _range[index][0].value;
18547
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
18515
18548
  _searchType = Math.sign(_searchType);
18516
18549
  switch (_searchType) {
18517
18550
  case 1:
18518
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18551
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18519
18552
  break;
18520
18553
  case 0:
18521
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
18554
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18522
18555
  break;
18523
18556
  case -1:
18524
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18557
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18525
18558
  break;
18526
18559
  }
18527
- if ((nbCol === 1 && range[0][index] === undefined) ||
18528
- (nbCol !== 1 && range[index] === undefined)) {
18560
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
18561
+ (nbCol !== 1 && _range[index] === undefined)) {
18529
18562
  return valueNotAvailable(searchKey);
18530
18563
  }
18531
18564
  return index + 1;
@@ -18580,13 +18613,14 @@ const VLOOKUP = {
18580
18613
  ],
18581
18614
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18582
18615
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18583
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18616
+ const _range = toMatrix(range);
18617
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18584
18618
  const getValueFromRange = (range, index) => range[0][index].value;
18585
18619
  const _isSorted = toBoolean(isSorted.value);
18586
18620
  const rowIndex = _isSorted
18587
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18588
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
18589
- const value = range[_index - 1][rowIndex];
18621
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18622
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches);
18623
+ const value = _range[_index - 1][rowIndex];
18590
18624
  if (value === undefined) {
18591
18625
  return valueNotAvailable(searchKey);
18592
18626
  }
@@ -18623,27 +18657,29 @@ const XLOOKUP = {
18623
18657
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18624
18658
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18625
18659
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18626
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18660
+ const _lookupRange = toMatrix(lookupRange);
18661
+ const _returnRange = toMatrix(returnRange);
18662
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18627
18663
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18628
18664
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18629
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18665
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18630
18666
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18631
18667
  assert(() => lookupDirection === "col"
18632
- ? returnRange[0].length === lookupRange[0].length
18633
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18668
+ ? _returnRange[0].length === _lookupRange[0].length
18669
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18634
18670
  const getElement = lookupDirection === "col"
18635
18671
  ? (range, index) => range[0][index].value
18636
18672
  : (range, index) => range[index][0].value;
18637
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18673
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18638
18674
  const mode = MATCH_MODE[_matchMode];
18639
18675
  const reverseSearch = _searchMode === -1;
18640
18676
  const index = _searchMode === 2 || _searchMode === -2
18641
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18642
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18677
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18678
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18643
18679
  if (index !== -1) {
18644
18680
  return lookupDirection === "col"
18645
- ? returnRange.map((col) => [col[index]])
18646
- : [returnRange[index]];
18681
+ ? _returnRange.map((col) => [col[index]])
18682
+ : [_returnRange[index]];
18647
18683
  }
18648
18684
  if (defaultValue === undefined) {
18649
18685
  return valueNotAvailable(searchKey);
@@ -21079,6 +21115,12 @@ class Composer extends owl.Component {
21079
21115
  }
21080
21116
  this.contentHelper.updateEl(el);
21081
21117
  });
21118
+ this.env.model.selection.observe(this, {
21119
+ handleEvent: () => this.autoCompleteState.hide(),
21120
+ });
21121
+ owl.onWillUnmount(() => {
21122
+ this.env.model.selection.detachObserver(this);
21123
+ });
21082
21124
  owl.useEffect(() => {
21083
21125
  this.processContent();
21084
21126
  if (document.activeElement === this.contentHelper.el &&
@@ -48443,11 +48485,10 @@ class GridRenderer {
48443
48485
  switch (layer) {
48444
48486
  case "Background":
48445
48487
  this.drawGlobalBackground(renderingContext);
48446
- for (const zone of this.getters.getAllActiveViewportsZones()) {
48488
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
48447
48489
  const { ctx } = renderingContext;
48448
48490
  ctx.save();
48449
48491
  ctx.beginPath();
48450
- const rect = this.getters.getVisibleRect(zone);
48451
48492
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
48452
48493
  ctx.clip();
48453
48494
  const boxes = this.getGridBoxes(zone);
@@ -48723,10 +48764,8 @@ class GridRenderer {
48723
48764
  const { ctx, thinLineWidth } = renderingContext;
48724
48765
  const visibleCols = this.getters.getSheetViewVisibleCols();
48725
48766
  const left = visibleCols[0];
48726
- const right = visibleCols[visibleCols.length - 1];
48727
48767
  const visibleRows = this.getters.getSheetViewVisibleRows();
48728
48768
  const top = visibleRows[0];
48729
- const bottom = visibleRows[visibleRows.length - 1];
48730
48769
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
48731
48770
  const selection = this.getters.getSelectedZones();
48732
48771
  const selectedCols = getZonesCols(selection);
@@ -48742,7 +48781,7 @@ class GridRenderer {
48742
48781
  ctx.lineWidth = thinLineWidth;
48743
48782
  ctx.strokeStyle = "#333";
48744
48783
  // Columns headers background
48745
- for (let col = left; col <= right; col++) {
48784
+ for (const col of visibleCols) {
48746
48785
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
48747
48786
  const { x, width } = this.getters.getVisibleRect(colZone);
48748
48787
  const isColActive = activeCols.has(col);
@@ -48759,7 +48798,7 @@ class GridRenderer {
48759
48798
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
48760
48799
  }
48761
48800
  // Rows headers background
48762
- for (let row = top; row <= bottom; row++) {
48801
+ for (const row of visibleRows) {
48763
48802
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
48764
48803
  const { y, height } = this.getters.getVisibleRect(rowZone);
48765
48804
  const isRowActive = activeRows.has(row);
@@ -48785,21 +48824,21 @@ class GridRenderer {
48785
48824
  ctx.stroke();
48786
48825
  ctx.beginPath();
48787
48826
  // column text + separator
48788
- for (const i of visibleCols) {
48789
- const colSize = this.getters.getColSize(sheetId, i);
48790
- const colName = numberToLetters(i);
48791
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
48792
- let colStart = this.getHeaderOffset("COL", left, i);
48827
+ for (const col of visibleCols) {
48828
+ const colSize = this.getters.getColSize(sheetId, col);
48829
+ const colName = numberToLetters(col);
48830
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
48831
+ let colStart = this.getHeaderOffset("COL", left, col);
48793
48832
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
48794
48833
  ctx.moveTo(colStart + colSize, 0);
48795
48834
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
48796
48835
  }
48797
48836
  // row text + separator
48798
- for (const i of visibleRows) {
48799
- const rowSize = this.getters.getRowSize(sheetId, i);
48800
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
48801
- let rowStart = this.getHeaderOffset("ROW", top, i);
48802
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
48837
+ for (const row of visibleRows) {
48838
+ const rowSize = this.getters.getRowSize(sheetId, row);
48839
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
48840
+ let rowStart = this.getHeaderOffset("ROW", top, row);
48841
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
48803
48842
  ctx.moveTo(0, rowStart + rowSize);
48804
48843
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
48805
48844
  }
@@ -49099,6 +49138,9 @@ function useGridDrawing(refName, model, canvasSize) {
49099
49138
  canvas.width = width * dpr;
49100
49139
  canvas.height = height * dpr;
49101
49140
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
49141
+ if (width === 0 || height === 0) {
49142
+ return;
49143
+ }
49102
49144
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
49103
49145
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
49104
49146
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -50670,7 +50712,7 @@ class BordersPlugin extends CorePlugin {
50670
50712
  getCommonSides(border1, border2) {
50671
50713
  const commonBorder = {};
50672
50714
  for (let side of ["top", "bottom", "left", "right"]) {
50673
- if (border1[side] && border1[side] === border2[side]) {
50715
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
50674
50716
  commonBorder[side] = border1[side];
50675
50717
  }
50676
50718
  }
@@ -57560,6 +57602,10 @@ class Evaluator {
57560
57602
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
57561
57603
  this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
57562
57604
  this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
57605
+ this.compilationParams.evalContext.lookupCaches = {
57606
+ forwardSearch: new Map(),
57607
+ reverseSearch: new Map(),
57608
+ };
57563
57609
  }
57564
57610
  createEmptyPositionSet() {
57565
57611
  const sheetSizes = {};
@@ -59409,7 +59455,7 @@ function withPivotPresentationLayer (PivotClass) {
59409
59455
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
59410
59456
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
59411
59457
  }
59412
- return this._getPivotCellValueAndFormat(symbolName, domain);
59458
+ return this.getPivotCellValueAndFormat(symbolName, domain);
59413
59459
  };
59414
59460
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
59415
59461
  if (isMatrix(result)) {
@@ -62526,14 +62572,12 @@ class SheetUIPlugin extends UIPlugin {
62526
62572
  }
62527
62573
  break;
62528
62574
  case "AUTORESIZE_ROWS":
62529
- for (let row of cmd.rows) {
62530
- this.dispatch("RESIZE_COLUMNS_ROWS", {
62531
- elements: [row],
62532
- dimension: "ROW",
62533
- size: null,
62534
- sheetId: cmd.sheetId,
62535
- });
62536
- }
62575
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
62576
+ elements: cmd.rows,
62577
+ dimension: "ROW",
62578
+ size: null,
62579
+ sheetId: cmd.sheetId,
62580
+ });
62537
62581
  break;
62538
62582
  }
62539
62583
  }
@@ -64938,8 +64982,17 @@ class InternalViewport {
64938
64982
  this.getters = getters;
64939
64983
  this.sheetId = sheetId;
64940
64984
  this.boundaries = boundaries;
64941
- this.viewportWidth = sizeInGrid.width;
64942
- this.viewportHeight = sizeInGrid.height;
64985
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
64986
+ throw new Error("Viewport size cannot be negative");
64987
+ }
64988
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
64989
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
64990
+ this.top = boundaries.top;
64991
+ this.bottom = boundaries.bottom;
64992
+ this.left = boundaries.left;
64993
+ this.right = boundaries.right;
64994
+ this.offsetX = offsets.x;
64995
+ this.offsetY = offsets.y;
64943
64996
  this.offsetScrollbarX = offsets.x;
64944
64997
  this.offsetScrollbarY = offsets.y;
64945
64998
  this.canScrollVertically = options.canScrollVertically;
@@ -64982,9 +65035,9 @@ class InternalViewport {
64982
65035
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
64983
65036
  );
64984
65037
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
64985
- }
64986
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
64987
- height += FOOTER_HEIGHT;
65038
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
65039
+ height += FOOTER_HEIGHT;
65040
+ }
64988
65041
  }
64989
65042
  return { width, height };
64990
65043
  }
@@ -65125,6 +65178,9 @@ class InternalViewport {
65125
65178
  !this.getters.isRowHidden(this.sheetId, row));
65126
65179
  }
65127
65180
  searchHeaderIndex(dimension, position, startIndex = 0) {
65181
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
65182
+ return -1;
65183
+ }
65128
65184
  const sheetId = this.sheetId;
65129
65185
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
65130
65186
  // using a binary search:
@@ -65161,7 +65217,7 @@ class InternalViewport {
65161
65217
  this.adjustViewportZoneY();
65162
65218
  }
65163
65219
  /** Corrects the viewport's horizontal offset based on the current structure
65164
- * To make sure that at least on column is visible inside the viewport.
65220
+ * To make sure that at least one column is visible inside the viewport.
65165
65221
  */
65166
65222
  adjustViewportOffsetX() {
65167
65223
  if (this.canScrollHorizontally) {
@@ -65173,7 +65229,7 @@ class InternalViewport {
65173
65229
  this.adjustViewportZoneX();
65174
65230
  }
65175
65231
  /** Corrects the viewport's vertical offset based on the current structure
65176
- * To make sure that at least on row is visible inside the viewport.
65232
+ * To make sure that at least one row is visible inside the viewport.
65177
65233
  */
65178
65234
  adjustViewportOffsetY() {
65179
65235
  if (this.canScrollVertically) {
@@ -65190,11 +65246,14 @@ class InternalViewport {
65190
65246
  const sheetId = this.sheetId;
65191
65247
  this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
65192
65248
  this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
65249
+ if (!this.viewportWidth) {
65250
+ return;
65251
+ }
65193
65252
  if (this.left === -1) {
65194
65253
  this.left = this.boundaries.left;
65195
65254
  }
65196
65255
  if (this.right === -1) {
65197
- this.right = this.getters.getNumberCols(sheetId) - 1;
65256
+ this.right = this.boundaries.right;
65198
65257
  }
65199
65258
  this.offsetX =
65200
65259
  this.getters.getColDimensions(sheetId, this.left).start -
@@ -65206,11 +65265,14 @@ class InternalViewport {
65206
65265
  const sheetId = this.sheetId;
65207
65266
  this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
65208
65267
  this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
65268
+ if (!this.viewportHeight) {
65269
+ return;
65270
+ }
65209
65271
  if (this.top === -1) {
65210
65272
  this.top = this.boundaries.top;
65211
65273
  }
65212
65274
  if (this.bottom === -1) {
65213
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
65275
+ this.bottom = this.boundaries.bottom;
65214
65276
  }
65215
65277
  this.offsetY =
65216
65278
  this.getters.getRowDimensions(sheetId, this.top).start -
@@ -65284,7 +65346,7 @@ class SheetViewPlugin extends UIPlugin {
65284
65346
  "isPositionVisible",
65285
65347
  "getColDimensionsInViewport",
65286
65348
  "getRowDimensionsInViewport",
65287
- "getAllActiveViewportsZones",
65349
+ "getAllActiveViewportsZonesAndRect",
65288
65350
  "getRect",
65289
65351
  ];
65290
65352
  viewports = {};
@@ -65517,12 +65579,12 @@ class SheetViewPlugin extends UIPlugin {
65517
65579
  const sheetId = this.getters.getActiveSheetId();
65518
65580
  const viewports = this.getSubViewports(sheetId);
65519
65581
  //TODO ake another commit to eimprove this
65520
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
65582
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
65521
65583
  }
65522
65584
  getSheetViewVisibleRows() {
65523
65585
  const sheetId = this.getters.getActiveSheetId();
65524
65586
  const viewports = this.getSubViewports(sheetId);
65525
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
65587
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
65526
65588
  }
65527
65589
  /**
65528
65590
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -65565,19 +65627,19 @@ class SheetViewPlugin extends UIPlugin {
65565
65627
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
65566
65628
  };
65567
65629
  }
65568
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
65569
- const sheetId = this.getters.getActiveSheetId();
65570
- const visibleCols = this.getters.getSheetViewVisibleCols();
65571
- const visibleRows = this.getters.getSheetViewVisibleRows();
65572
- if (index < referenceIndex) {
65573
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
65630
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
65631
+ if (targetHeaderIndex < referenceHeaderIndex) {
65632
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
65574
65633
  }
65634
+ const sheetId = this.getters.getActiveSheetId();
65635
+ const visibleHeaders = dimension === "COL"
65636
+ ? this.getters.getSheetViewVisibleCols()
65637
+ : this.getters.getSheetViewVisibleRows();
65638
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
65639
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
65640
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
65575
65641
  let offset = 0;
65576
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
65577
- for (let i = referenceIndex; i < index; i++) {
65578
- if (!visibleIndexes.includes(i)) {
65579
- continue;
65580
- }
65642
+ for (const i of relevantIndexes) {
65581
65643
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
65582
65644
  }
65583
65645
  return offset;
@@ -65624,7 +65686,7 @@ class SheetViewPlugin extends UIPlugin {
65624
65686
  }
65625
65687
  return { canEdgeScroll, direction, delay };
65626
65688
  }
65627
- getEdgeScrollRow(y, previousY, tartingY) {
65689
+ getEdgeScrollRow(y, previousY, startingY) {
65628
65690
  let canEdgeScroll = false;
65629
65691
  let direction = 0;
65630
65692
  let delay = 0;
@@ -65645,7 +65707,7 @@ class SheetViewPlugin extends UIPlugin {
65645
65707
  delay = scrollDelay(y - height);
65646
65708
  direction = 1;
65647
65709
  }
65648
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
65710
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
65649
65711
  // 2
65650
65712
  canEdgeScroll = true;
65651
65713
  delay = scrollDelay(offsetCorrectionY - y);
@@ -65671,13 +65733,7 @@ class SheetViewPlugin extends UIPlugin {
65671
65733
  */
65672
65734
  getVisibleRectWithoutHeaders(zone) {
65673
65735
  const sheetId = this.getters.getActiveSheetId();
65674
- const viewportRects = this.getSubViewports(sheetId)
65675
- .map((viewport) => viewport.getVisibleRect(zone))
65676
- .filter(isDefined);
65677
- if (viewportRects.length === 0) {
65678
- return { x: 0, y: 0, width: 0, height: 0 };
65679
- }
65680
- return this.recomposeRect(viewportRects);
65736
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
65681
65737
  }
65682
65738
  /**
65683
65739
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -65685,13 +65741,7 @@ class SheetViewPlugin extends UIPlugin {
65685
65741
  */
65686
65742
  getRect(zone) {
65687
65743
  const sheetId = this.getters.getActiveSheetId();
65688
- const viewportRects = this.getSubViewports(sheetId)
65689
- .map((viewport) => viewport.getFullRect(zone))
65690
- .filter(isDefined);
65691
- if (viewportRects.length === 0) {
65692
- return { x: 0, y: 0, width: 0, height: 0 };
65693
- }
65694
- const rect = this.recomposeRect(viewportRects);
65744
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
65695
65745
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
65696
65746
  }
65697
65747
  /**
@@ -65736,9 +65786,18 @@ class SheetViewPlugin extends UIPlugin {
65736
65786
  end: start + (isRowHidden ? 0 : size),
65737
65787
  };
65738
65788
  }
65739
- getAllActiveViewportsZones() {
65789
+ getAllActiveViewportsZonesAndRect() {
65740
65790
  const sheetId = this.getters.getActiveSheetId();
65741
- return this.getSubViewports(sheetId);
65791
+ return this.getSubViewports(sheetId).map((viewport) => {
65792
+ return {
65793
+ zone: viewport,
65794
+ rect: {
65795
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
65796
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
65797
+ ...viewport.getMaxSize(),
65798
+ },
65799
+ };
65800
+ });
65742
65801
  }
65743
65802
  // ---------------------------------------------------------------------------
65744
65803
  // Private
@@ -65797,12 +65856,11 @@ class SheetViewPlugin extends UIPlugin {
65797
65856
  }
65798
65857
  /** gets rid of deprecated sheetIds */
65799
65858
  cleanViewports() {
65800
- const sheetIds = this.getters.getSheetIds();
65801
- for (let sheetId of Object.keys(this.viewports)) {
65802
- if (!sheetIds.includes(sheetId)) {
65803
- delete this.viewports[sheetId];
65804
- }
65859
+ const newViewport = {};
65860
+ for (const sheetId of this.getters.getSheetIds()) {
65861
+ newViewport[sheetId] = this.viewports[sheetId];
65805
65862
  }
65863
+ this.viewports = newViewport;
65806
65864
  }
65807
65865
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
65808
65866
  this.sheetViewHeight = height;
@@ -65812,7 +65870,7 @@ class SheetViewPlugin extends UIPlugin {
65812
65870
  this.recomputeViewports();
65813
65871
  }
65814
65872
  recomputeViewports() {
65815
- for (let sheetId of Object.keys(this.viewports)) {
65873
+ for (const sheetId of this.getters.getSheetIds()) {
65816
65874
  this.resetViewports(sheetId);
65817
65875
  }
65818
65876
  }
@@ -65834,8 +65892,10 @@ class SheetViewPlugin extends UIPlugin {
65834
65892
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
65835
65893
  const nCols = this.getters.getNumberCols(sheetId);
65836
65894
  const nRows = this.getters.getNumberRows(sheetId);
65837
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
65838
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
65895
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
65896
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
65897
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
65898
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
65839
65899
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
65840
65900
  const canScrollHorizontally = xRatio < 1.0;
65841
65901
  const canScrollVertically = yRatio < 1.0;
@@ -65846,14 +65906,14 @@ class SheetViewPlugin extends UIPlugin {
65846
65906
  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 })) ||
65847
65907
  undefined,
65848
65908
  topRight: (ySplit &&
65849
- 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 })) ||
65909
+ 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 })) ||
65850
65910
  undefined,
65851
65911
  bottomLeft: (xSplit &&
65852
- 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 })) ||
65912
+ 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 })) ||
65853
65913
  undefined,
65854
65914
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
65855
- width: this.sheetViewWidth - colOffset,
65856
- height: this.sheetViewHeight - rowOffset,
65915
+ width: unfrozenWidth,
65916
+ height: unfrozenHeight,
65857
65917
  }, { canScrollHorizontally, canScrollVertically }, {
65858
65918
  x: canScrollHorizontally ? previousOffset.x : 0,
65859
65919
  y: canScrollVertically ? previousOffset.y : 0,
@@ -65930,12 +65990,26 @@ class SheetViewPlugin extends UIPlugin {
65930
65990
  const height = this.sheetViewHeight + this.gridOffsetY;
65931
65991
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
65932
65992
  }
65933
- recomposeRect(viewportRects) {
65934
- const x = Math.min(...viewportRects.map((rect) => rect.x));
65935
- const y = Math.min(...viewportRects.map((rect) => rect.y));
65936
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
65937
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
65938
- return { x, y, width, height };
65993
+ mapViewportsToRect(sheetId, rectCallBack) {
65994
+ let x = Infinity;
65995
+ let y = Infinity;
65996
+ let width = 0;
65997
+ let height = 0;
65998
+ let hasViewports = false;
65999
+ for (const viewport of this.getSubViewports(sheetId)) {
66000
+ const rect = rectCallBack(viewport);
66001
+ if (rect) {
66002
+ hasViewports = true;
66003
+ x = Math.min(x, rect.x);
66004
+ y = Math.min(y, rect.y);
66005
+ width = Math.max(width, rect.x + rect.width);
66006
+ height = Math.max(height, rect.y + rect.height);
66007
+ }
66008
+ }
66009
+ if (!hasViewports) {
66010
+ return { x: 0, y: 0, width: 0, height: 0 };
66011
+ }
66012
+ return { x, y, width: width - x, height: height - y };
65939
66013
  }
65940
66014
  }
65941
66015
 
@@ -69640,6 +69714,9 @@ class EventStream {
69640
69714
  observe(owner, callbacks) {
69641
69715
  this.observers.set(owner, { owner, callbacks });
69642
69716
  }
69717
+ detachObserver(owner) {
69718
+ this.observers.delete(owner);
69719
+ }
69643
69720
  /**
69644
69721
  * Capture the stream for yourself
69645
69722
  */
@@ -69732,6 +69809,9 @@ class SelectionStreamProcessorImpl {
69732
69809
  observe(owner, callbacks) {
69733
69810
  this.stream.observe(owner, callbacks);
69734
69811
  }
69812
+ detachObserver(owner) {
69813
+ this.stream.detachObserver(owner);
69814
+ }
69735
69815
  release(owner) {
69736
69816
  if (this.stream.isListening(owner)) {
69737
69817
  this.stream.release(owner);
@@ -73090,6 +73170,6 @@ exports.tokenColors = tokenColors;
73090
73170
  exports.tokenize = tokenize;
73091
73171
 
73092
73172
 
73093
- __info__.version = "18.0.12";
73094
- __info__.date = "2025-01-29T06:24:22.122Z";
73095
- __info__.hash = "a881cff";
73173
+ __info__.version = "18.0.14";
73174
+ __info__.date = "2025-02-05T06:47:33.041Z";
73175
+ __info__.hash = "90f2af4";