@odoo/o-spreadsheet 18.3.58 → 18.3.60

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.3.58
6
- * @date 2026-07-30T06:52:20.660Z
7
- * @hash a2d77fd
5
+ * @version 18.3.60
6
+ * @date 2026-08-21T15:28:06.523Z
7
+ * @hash ab0ffc5
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -3459,6 +3459,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3459
3459
  default: return "";
3460
3460
  }
3461
3461
  }
3462
+ /**
3463
+ * Only converts the decimal separator, ignore number format such as % or dates
3464
+ */
3465
+ function toLocaleString(data, locale) {
3466
+ const value = toValue(data);
3467
+ switch (typeof value) {
3468
+ case "string": return value;
3469
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3470
+ case "boolean": return value ? "TRUE" : "FALSE";
3471
+ default: return "";
3472
+ }
3473
+ }
3462
3474
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3463
3475
  const normalizeString = memoize(function normalizeString(str) {
3464
3476
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -3648,19 +3660,58 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3648
3660
  }
3649
3661
  }
3650
3662
  if (countVectorizedCol === 1 && countVectorizedRow === 1) return formula(...args);
3651
- const getArgOffset = (i, j) => args.map((arg, index) => {
3652
- switch (vectorArgsType?.[index]) {
3653
- case "matrix": return arg[i][j];
3654
- case "horizontal": return arg[i][0];
3655
- case "vertical": return arg[0][j];
3656
- case void 0: return arg;
3663
+ const argsBuffer = new Array(args.length);
3664
+ const argGetters = [];
3665
+ const vectorizedIndices = [];
3666
+ for (let k = 0; k < args.length; k++) {
3667
+ const arg = args[k];
3668
+ switch (vectorArgsType?.[k]) {
3669
+ case "matrix":
3670
+ argGetters.push((i, j) => arg[i][j]);
3671
+ vectorizedIndices.push(k);
3672
+ break;
3673
+ case "horizontal":
3674
+ argGetters.push((i) => arg[i][0]);
3675
+ vectorizedIndices.push(k);
3676
+ break;
3677
+ case "vertical":
3678
+ argGetters.push((_i, j) => arg[0][j]);
3679
+ vectorizedIndices.push(k);
3680
+ break;
3681
+ case void 0:
3682
+ argsBuffer[k] = arg;
3683
+ break;
3657
3684
  }
3658
- });
3659
- return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
3660
- if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
3661
- const singleCellComputeResult = formula(...getArgOffset(col, row));
3662
- return isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
3663
- });
3685
+ }
3686
+ const nbVectorized = vectorizedIndices.length;
3687
+ let callFormula;
3688
+ switch (argsBuffer.length) {
3689
+ case 1:
3690
+ callFormula = () => formula(argsBuffer[0]);
3691
+ break;
3692
+ case 2:
3693
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1]);
3694
+ break;
3695
+ case 3:
3696
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1], argsBuffer[2]);
3697
+ break;
3698
+ default: callFormula = () => formula(...argsBuffer);
3699
+ }
3700
+ const result = new Array(countVectorizedCol);
3701
+ for (let col = 0; col < countVectorizedCol; col++) {
3702
+ const column = new Array(countVectorizedRow);
3703
+ result[col] = column;
3704
+ for (let row = 0; row < countVectorizedRow; row++) {
3705
+ if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
3706
+ column[row] = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
3707
+ continue;
3708
+ }
3709
+ for (let k = 0; k < nbVectorized; k++) argsBuffer[vectorizedIndices[k]] = argGetters[k](col, row);
3710
+ const singleCellComputeResult = callFormula();
3711
+ column[row] = isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
3712
+ }
3713
+ }
3714
+ return result;
3664
3715
  }
3665
3716
  /**
3666
3717
  * This function allows to visit arguments and stop the visit if necessary.
@@ -15666,7 +15717,7 @@ stores.inject(MyMetaStore, storeInstance);
15666
15717
  description: _t("Concatenation of two values."),
15667
15718
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
15668
15719
  compute: function(value1, value2) {
15669
- return toString(value1) + toString(value2);
15720
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
15670
15721
  },
15671
15722
  isExported: true
15672
15723
  };
@@ -16476,7 +16527,7 @@ stores.inject(MyMetaStore, storeInstance);
16476
16527
  description: _t("Remove non-printable characters from a piece of text."),
16477
16528
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
16478
16529
  compute: function(text) {
16479
- const _text = toString(text);
16530
+ const _text = toLocaleString(text, this.locale);
16480
16531
  let cleanedStr = "";
16481
16532
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
16482
16533
  return cleanedStr;
@@ -16487,7 +16538,7 @@ stores.inject(MyMetaStore, storeInstance);
16487
16538
  description: _t("Appends strings to one another."),
16488
16539
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
16489
16540
  compute: function(...datas) {
16490
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
16541
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
16491
16542
  },
16492
16543
  isExported: true
16493
16544
  };
@@ -16495,7 +16546,7 @@ stores.inject(MyMetaStore, storeInstance);
16495
16546
  description: _t("Tests whether two strings are identical."),
16496
16547
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
16497
16548
  compute: function(string1, string2) {
16498
- return toString(string1) === toString(string2);
16549
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
16499
16550
  },
16500
16551
  isExported: true
16501
16552
  };
@@ -16507,13 +16558,13 @@ stores.inject(MyMetaStore, storeInstance);
16507
16558
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16508
16559
  ],
16509
16560
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16510
- const _searchFor = toString(searchFor);
16511
- const _textToSearch = toString(textToSearch);
16561
+ const _searchFor = toLocaleString(searchFor, this.locale);
16562
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16512
16563
  const _startingAt = toNumber(startingAt, this.locale);
16513
16564
  assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
16514
16565
  assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
16515
16566
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
16516
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
16567
+ assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
16517
16568
  return result + 1;
16518
16569
  },
16519
16570
  isExported: true
@@ -16526,8 +16577,8 @@ stores.inject(MyMetaStore, storeInstance);
16526
16577
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
16527
16578
  ],
16528
16579
  compute: function(delimiter, ...valuesOrArrays) {
16529
- const _delimiter = toString(delimiter);
16530
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
16580
+ const _delimiter = toLocaleString(delimiter, this.locale);
16581
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
16531
16582
  }
16532
16583
  };
16533
16584
  const LEFT = {
@@ -16536,7 +16587,7 @@ stores.inject(MyMetaStore, storeInstance);
16536
16587
  compute: function(text, ...args) {
16537
16588
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16538
16589
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
16539
- return toString(text).substring(0, _numberOfCharacters);
16590
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
16540
16591
  },
16541
16592
  isExported: true
16542
16593
  };
@@ -16544,7 +16595,7 @@ stores.inject(MyMetaStore, storeInstance);
16544
16595
  description: _t("Length of a string."),
16545
16596
  args: [arg("text (string)", _t("The string whose length will be returned."))],
16546
16597
  compute: function(text) {
16547
- return toString(text).length;
16598
+ return toLocaleString(text, this.locale).length;
16548
16599
  },
16549
16600
  isExported: true
16550
16601
  };
@@ -16552,7 +16603,7 @@ stores.inject(MyMetaStore, storeInstance);
16552
16603
  description: _t("Converts a specified string to lowercase."),
16553
16604
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
16554
16605
  compute: function(text) {
16555
- return toString(text).toLowerCase();
16606
+ return toLocaleString(text, this.locale).toLowerCase();
16556
16607
  },
16557
16608
  isExported: true
16558
16609
  };
@@ -16564,7 +16615,7 @@ stores.inject(MyMetaStore, storeInstance);
16564
16615
  arg("extract_length (number)", _t("The length of the segment to extract."))
16565
16616
  ],
16566
16617
  compute: function(text, starting_at, extract_length) {
16567
- const _text = toString(text);
16618
+ const _text = toLocaleString(text, this.locale);
16568
16619
  const _starting_at = toNumber(starting_at, this.locale);
16569
16620
  const _extract_length = toNumber(extract_length, this.locale);
16570
16621
  assert(() => _starting_at >= 1, _t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -16577,7 +16628,7 @@ stores.inject(MyMetaStore, storeInstance);
16577
16628
  description: _t("Capitalizes each word in a specified string."),
16578
16629
  args: [arg("text_to_capitalize (string)", _t("The text which will be returned with the first letter of each word in uppercase and all other letters in lowercase."))],
16579
16630
  compute: function(text) {
16580
- return toString(text).replace(wordRegex, (word) => {
16631
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
16581
16632
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
16582
16633
  });
16583
16634
  },
@@ -16594,9 +16645,9 @@ stores.inject(MyMetaStore, storeInstance);
16594
16645
  compute: function(text, position, length, newText) {
16595
16646
  const _position = toNumber(position, this.locale);
16596
16647
  assert(() => _position >= 1, _t("The position (%s) must be greater than or equal to 1.", _position.toString()));
16597
- const _text = toString(text);
16648
+ const _text = toLocaleString(text, this.locale);
16598
16649
  const _length = toNumber(length, this.locale);
16599
- const _newText = toString(newText);
16650
+ const _newText = toLocaleString(newText, this.locale);
16600
16651
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
16601
16652
  },
16602
16653
  isExported: true
@@ -16607,7 +16658,7 @@ stores.inject(MyMetaStore, storeInstance);
16607
16658
  compute: function(text, ...args) {
16608
16659
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16609
16660
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
16610
- const _text = toString(text);
16661
+ const _text = toLocaleString(text, this.locale);
16611
16662
  const stringLength = _text.length;
16612
16663
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
16613
16664
  },
@@ -16621,8 +16672,8 @@ stores.inject(MyMetaStore, storeInstance);
16621
16672
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16622
16673
  ],
16623
16674
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16624
- const _searchFor = toString(searchFor).toLowerCase();
16625
- const _textToSearch = toString(textToSearch).toLowerCase();
16675
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
16676
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
16626
16677
  const _startingAt = toNumber(startingAt, this.locale);
16627
16678
  if (_textToSearch === "") return {
16628
16679
  value: CellErrorType.GenericError,
@@ -16652,8 +16703,8 @@ stores.inject(MyMetaStore, storeInstance);
16652
16703
  arg(`remove_empty_text (boolean, default=${SPLIT_DEFAULT_REMOVE_EMPTY_TEXT})`, _t("Whether or not to remove empty text messages from the split results. The default behavior is to treat consecutive delimiters as one (if TRUE). If FALSE, empty cells values are added between consecutive delimiters."))
16653
16704
  ],
16654
16705
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
16655
- const _text = toString(text);
16656
- const _delimiter = escapeRegExp(toString(delimiter));
16706
+ const _text = toLocaleString(text, this.locale);
16707
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
16657
16708
  const _splitByEach = toBoolean(splitByEach);
16658
16709
  const _removeEmptyText = toBoolean(removeEmptyText);
16659
16710
  assert(() => _delimiter.length > 0, _t("The _delimiter (%s) must be not be empty.", _delimiter));
@@ -16675,10 +16726,10 @@ stores.inject(MyMetaStore, storeInstance);
16675
16726
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
16676
16727
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
16677
16728
  assert(() => _occurrenceNumber >= 0, _t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber.toString()));
16678
- const _textToSearch = toString(textToSearch);
16679
- const _searchFor = toString(searchFor);
16729
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16730
+ const _searchFor = toLocaleString(searchFor, this.locale);
16680
16731
  if (_searchFor === "") return _textToSearch;
16681
- const _replaceWith = toString(replaceWith);
16732
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
16682
16733
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
16683
16734
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
16684
16735
  let n = 0;
@@ -16695,10 +16746,10 @@ stores.inject(MyMetaStore, storeInstance);
16695
16746
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
16696
16747
  ],
16697
16748
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
16698
- const _delimiter = toString(delimiter);
16749
+ const _delimiter = toLocaleString(delimiter, this.locale);
16699
16750
  const _ignoreEmpty = toBoolean(ignoreEmpty);
16700
16751
  let n = 0;
16701
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
16752
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
16702
16753
  },
16703
16754
  isExported: true
16704
16755
  };
@@ -16706,7 +16757,7 @@ stores.inject(MyMetaStore, storeInstance);
16706
16757
  description: _t("Removes space characters."),
16707
16758
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
16708
16759
  compute: function(text) {
16709
- return trimContent(toString(text));
16760
+ return trimContent(toLocaleString(text, this.locale));
16710
16761
  },
16711
16762
  isExported: true
16712
16763
  };
@@ -16714,7 +16765,7 @@ stores.inject(MyMetaStore, storeInstance);
16714
16765
  description: _t("Converts a specified string to uppercase."),
16715
16766
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
16716
16767
  compute: function(text) {
16717
- return toString(text).toUpperCase();
16768
+ return toLocaleString(text, this.locale).toUpperCase();
16718
16769
  },
16719
16770
  isExported: true
16720
16771
  };
@@ -16746,7 +16797,7 @@ stores.inject(MyMetaStore, storeInstance);
16746
16797
  args: [arg("url (string)", _t("The full URL of the link enclosed in quotation marks.")), arg("link_label (string, optional)", _t("The text to display in the cell, enclosed in quotation marks."))],
16747
16798
  compute: function(url, linkLabel) {
16748
16799
  const processedUrl = toString(url).trim();
16749
- const processedLabel = toString(linkLabel) || processedUrl;
16800
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
16750
16801
  if (processedUrl === "") return processedLabel;
16751
16802
  return markdownLink(processedLabel, processedUrl);
16752
16803
  },
@@ -16848,12 +16899,15 @@ stores.inject(MyMetaStore, storeInstance);
16848
16899
  }
16849
16900
  }
16850
16901
  function createComputeFunction(descr) {
16902
+ let currentArgDefinitions = [];
16851
16903
  function vectorizedCompute(...args) {
16852
16904
  const acceptToVectorize = [];
16905
+ currentArgDefinitions = new Array(args.length);
16853
16906
  const getArgToFocus = argTargeting(descr, args.length);
16854
16907
  for (let i = 0; i < args.length; i++) {
16855
16908
  const argIndex = getArgToFocus(i) ?? -1;
16856
16909
  const argDefinition = descr.args[argIndex];
16910
+ currentArgDefinitions[i] = argDefinition;
16857
16911
  const arg = args[i];
16858
16912
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", descr.name, (i + 1).toString()));
16859
16913
  acceptToVectorize.push(!argDefinition.acceptMatrix);
@@ -16868,8 +16922,7 @@ stores.inject(MyMetaStore, storeInstance);
16868
16922
  function errorHandlingCompute(...args) {
16869
16923
  for (let i = 0; i < args.length; i++) {
16870
16924
  const arg = args[i];
16871
- const getArgToFocus = argTargeting(descr, args.length);
16872
- if (!descr.args[getArgToFocus(i) || i].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
16925
+ if (!currentArgDefinitions[i].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
16873
16926
  }
16874
16927
  try {
16875
16928
  return computeFunctionToObject.apply(this, args);
@@ -31284,12 +31337,13 @@ stores.inject(MyMetaStore, storeInstance);
31284
31337
  }
31285
31338
  sortFilterZone(sortDirection) {
31286
31339
  const filterPosition = this.props.filterPosition;
31287
- const tableZone = this.table?.range.zone;
31340
+ const table = this.table;
31341
+ const tableZone = table?.range.zone;
31288
31342
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
31289
31343
  const sheetId = this.env.model.getters.getActiveSheetId();
31290
31344
  const contentZone = {
31291
31345
  ...tableZone,
31292
- top: tableZone.top + 1
31346
+ top: tableZone.top + table.config.numberOfHeaders
31293
31347
  };
31294
31348
  const sortAnchor = {
31295
31349
  col: filterPosition.col,
@@ -42871,6 +42925,7 @@ stores.inject(MyMetaStore, storeInstance);
42871
42925
  .o-spreadsheet {
42872
42926
  .os-input {
42873
42927
  border-width: 0 0 1px 0;
42928
+ border-style: solid;
42874
42929
  border-color: transparent;
42875
42930
  outline: none;
42876
42931
  text-overflow: ellipsis;
@@ -67827,6 +67882,7 @@ stores.inject(MyMetaStore, storeInstance);
67827
67882
  width: 100%;
67828
67883
  outline: none;
67829
67884
  border-color: ${GRAY_300};
67885
+ border-style: solid;
67830
67886
  color: ${GRAY_900};
67831
67887
 
67832
67888
  &::placeholder {
@@ -72287,8 +72343,8 @@ exports.stores = stores;
72287
72343
  exports.tokenColors = tokenColors;
72288
72344
  exports.tokenize = tokenize;
72289
72345
 
72290
- __info__.version = "18.3.58";
72291
- __info__.date = "2026-07-30T06:52:20.660Z";
72292
- __info__.hash = "a2d77fd";
72346
+ __info__.version = "18.3.60";
72347
+ __info__.date = "2026-08-21T15:28:06.523Z";
72348
+ __info__.hash = "ab0ffc5";
72293
72349
 
72294
72350
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);