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