@odoo/o-spreadsheet 18.4.50 → 18.4.52

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.4.50
6
- * @date 2026-07-30T06:52:55.061Z
7
- * @hash 0a41352
5
+ * @version 18.4.52
6
+ * @date 2026-08-21T15:29:26.810Z
7
+ * @hash 5715a49
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -2850,6 +2850,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2850
2850
  default: return "";
2851
2851
  }
2852
2852
  }
2853
+ /**
2854
+ * Only converts the decimal separator, ignore number format such as % or dates
2855
+ */
2856
+ function toLocaleString(data, locale) {
2857
+ const value = toValue(data);
2858
+ switch (typeof value) {
2859
+ case "string": return value;
2860
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
2861
+ case "boolean": return value ? "TRUE" : "FALSE";
2862
+ default: return "";
2863
+ }
2864
+ }
2853
2865
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2854
2866
  const normalizeString = memoize(function normalizeString(str) {
2855
2867
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -3039,19 +3051,58 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3039
3051
  }
3040
3052
  }
3041
3053
  if (countVectorizedCol === 1 && countVectorizedRow === 1) return formula(...args);
3042
- const getArgOffset = (i, j) => args.map((arg, index) => {
3043
- switch (vectorArgsType?.[index]) {
3044
- case "matrix": return arg[i][j];
3045
- case "horizontal": return arg[i][0];
3046
- case "vertical": return arg[0][j];
3047
- case void 0: return arg;
3054
+ const argsBuffer = new Array(args.length);
3055
+ const argGetters = [];
3056
+ const vectorizedIndices = [];
3057
+ for (let k = 0; k < args.length; k++) {
3058
+ const arg = args[k];
3059
+ switch (vectorArgsType?.[k]) {
3060
+ case "matrix":
3061
+ argGetters.push((i, j) => arg[i][j]);
3062
+ vectorizedIndices.push(k);
3063
+ break;
3064
+ case "horizontal":
3065
+ argGetters.push((i) => arg[i][0]);
3066
+ vectorizedIndices.push(k);
3067
+ break;
3068
+ case "vertical":
3069
+ argGetters.push((_i, j) => arg[0][j]);
3070
+ vectorizedIndices.push(k);
3071
+ break;
3072
+ case void 0:
3073
+ argsBuffer[k] = arg;
3074
+ break;
3048
3075
  }
3049
- });
3050
- return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
3051
- if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
3052
- const singleCellComputeResult = formula(...getArgOffset(col, row));
3053
- return isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
3054
- });
3076
+ }
3077
+ const nbVectorized = vectorizedIndices.length;
3078
+ let callFormula;
3079
+ switch (argsBuffer.length) {
3080
+ case 1:
3081
+ callFormula = () => formula(argsBuffer[0]);
3082
+ break;
3083
+ case 2:
3084
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1]);
3085
+ break;
3086
+ case 3:
3087
+ callFormula = () => formula(argsBuffer[0], argsBuffer[1], argsBuffer[2]);
3088
+ break;
3089
+ default: callFormula = () => formula(...argsBuffer);
3090
+ }
3091
+ const result = new Array(countVectorizedCol);
3092
+ for (let col = 0; col < countVectorizedCol; col++) {
3093
+ const column = new Array(countVectorizedRow);
3094
+ result[col] = column;
3095
+ for (let row = 0; row < countVectorizedRow; row++) {
3096
+ if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
3097
+ column[row] = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
3098
+ continue;
3099
+ }
3100
+ for (let k = 0; k < nbVectorized; k++) argsBuffer[vectorizedIndices[k]] = argGetters[k](col, row);
3101
+ const singleCellComputeResult = callFormula();
3102
+ column[row] = isMatrix(singleCellComputeResult) ? singleCellComputeResult[0][0] : singleCellComputeResult;
3103
+ }
3104
+ }
3105
+ return result;
3055
3106
  }
3056
3107
  /**
3057
3108
  * This function allows to visit arguments and stop the visit if necessary.
@@ -15951,7 +16002,7 @@ stores.inject(MyMetaStore, storeInstance);
15951
16002
  description: _t("Concatenation of two values."),
15952
16003
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
15953
16004
  compute: function(value1, value2) {
15954
- return toString(value1) + toString(value2);
16005
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
15955
16006
  },
15956
16007
  isExported: true
15957
16008
  };
@@ -16762,7 +16813,7 @@ stores.inject(MyMetaStore, storeInstance);
16762
16813
  description: _t("Remove non-printable characters from a piece of text."),
16763
16814
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
16764
16815
  compute: function(text) {
16765
- const _text = toString(text);
16816
+ const _text = toLocaleString(text, this.locale);
16766
16817
  let cleanedStr = "";
16767
16818
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
16768
16819
  return cleanedStr;
@@ -16773,7 +16824,7 @@ stores.inject(MyMetaStore, storeInstance);
16773
16824
  description: _t("Appends strings to one another."),
16774
16825
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
16775
16826
  compute: function(...datas) {
16776
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
16827
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
16777
16828
  },
16778
16829
  isExported: true
16779
16830
  };
@@ -16781,7 +16832,7 @@ stores.inject(MyMetaStore, storeInstance);
16781
16832
  description: _t("Tests whether two strings are identical."),
16782
16833
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
16783
16834
  compute: function(string1, string2) {
16784
- return toString(string1) === toString(string2);
16835
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
16785
16836
  },
16786
16837
  isExported: true
16787
16838
  };
@@ -16793,8 +16844,8 @@ stores.inject(MyMetaStore, storeInstance);
16793
16844
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16794
16845
  ],
16795
16846
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16796
- const _searchFor = toString(searchFor);
16797
- const _textToSearch = toString(textToSearch);
16847
+ const _searchFor = toLocaleString(searchFor, this.locale);
16848
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16798
16849
  const _startingAt = toNumber(startingAt, this.locale);
16799
16850
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
16800
16851
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -16812,8 +16863,8 @@ stores.inject(MyMetaStore, storeInstance);
16812
16863
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
16813
16864
  ],
16814
16865
  compute: function(delimiter, ...valuesOrArrays) {
16815
- const _delimiter = toString(delimiter);
16816
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
16866
+ const _delimiter = toLocaleString(delimiter, this.locale);
16867
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
16817
16868
  }
16818
16869
  };
16819
16870
  const LEFT = {
@@ -16822,7 +16873,7 @@ stores.inject(MyMetaStore, storeInstance);
16822
16873
  compute: function(text, ...args) {
16823
16874
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16824
16875
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
16825
- return toString(text).substring(0, _numberOfCharacters);
16876
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
16826
16877
  },
16827
16878
  isExported: true
16828
16879
  };
@@ -16830,7 +16881,7 @@ stores.inject(MyMetaStore, storeInstance);
16830
16881
  description: _t("Length of a string."),
16831
16882
  args: [arg("text (string)", _t("The string whose length will be returned."))],
16832
16883
  compute: function(text) {
16833
- return toString(text).length;
16884
+ return toLocaleString(text, this.locale).length;
16834
16885
  },
16835
16886
  isExported: true
16836
16887
  };
@@ -16838,7 +16889,7 @@ stores.inject(MyMetaStore, storeInstance);
16838
16889
  description: _t("Converts a specified string to lowercase."),
16839
16890
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
16840
16891
  compute: function(text) {
16841
- return toString(text).toLowerCase();
16892
+ return toLocaleString(text, this.locale).toLowerCase();
16842
16893
  },
16843
16894
  isExported: true
16844
16895
  };
@@ -16850,7 +16901,7 @@ stores.inject(MyMetaStore, storeInstance);
16850
16901
  arg("extract_length (number)", _t("The length of the segment to extract."))
16851
16902
  ],
16852
16903
  compute: function(text, starting_at, extract_length) {
16853
- const _text = toString(text);
16904
+ const _text = toLocaleString(text, this.locale);
16854
16905
  const _starting_at = toNumber(starting_at, this.locale);
16855
16906
  const _extract_length = toNumber(extract_length, this.locale);
16856
16907
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -16863,7 +16914,7 @@ stores.inject(MyMetaStore, storeInstance);
16863
16914
  description: _t("Capitalizes each word in a specified string."),
16864
16915
  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."))],
16865
16916
  compute: function(text) {
16866
- return toString(text).replace(wordRegex, (word) => {
16917
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
16867
16918
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
16868
16919
  });
16869
16920
  },
@@ -16880,9 +16931,9 @@ stores.inject(MyMetaStore, storeInstance);
16880
16931
  compute: function(text, position, length, newText) {
16881
16932
  const _position = toNumber(position, this.locale);
16882
16933
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
16883
- const _text = toString(text);
16934
+ const _text = toLocaleString(text, this.locale);
16884
16935
  const _length = toNumber(length, this.locale);
16885
- const _newText = toString(newText);
16936
+ const _newText = toLocaleString(newText, this.locale);
16886
16937
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
16887
16938
  },
16888
16939
  isExported: true
@@ -16893,7 +16944,7 @@ stores.inject(MyMetaStore, storeInstance);
16893
16944
  compute: function(text, ...args) {
16894
16945
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16895
16946
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
16896
- const _text = toString(text);
16947
+ const _text = toLocaleString(text, this.locale);
16897
16948
  const stringLength = _text.length;
16898
16949
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
16899
16950
  },
@@ -16907,8 +16958,8 @@ stores.inject(MyMetaStore, storeInstance);
16907
16958
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16908
16959
  ],
16909
16960
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16910
- const _searchFor = toString(searchFor).toLowerCase();
16911
- const _textToSearch = toString(textToSearch).toLowerCase();
16961
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
16962
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
16912
16963
  const _startingAt = toNumber(startingAt, this.locale);
16913
16964
  if (_textToSearch === "") return {
16914
16965
  value: CellErrorType.GenericError,
@@ -16938,8 +16989,8 @@ stores.inject(MyMetaStore, storeInstance);
16938
16989
  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."))
16939
16990
  ],
16940
16991
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
16941
- const _text = toString(text);
16942
- const _delimiter = escapeRegExp(toString(delimiter));
16992
+ const _text = toLocaleString(text, this.locale);
16993
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
16943
16994
  const _splitByEach = toBoolean(splitByEach);
16944
16995
  const _removeEmptyText = toBoolean(removeEmptyText);
16945
16996
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -16961,10 +17012,10 @@ stores.inject(MyMetaStore, storeInstance);
16961
17012
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
16962
17013
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
16963
17014
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
16964
- const _textToSearch = toString(textToSearch);
16965
- const _searchFor = toString(searchFor);
17015
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
17016
+ const _searchFor = toLocaleString(searchFor, this.locale);
16966
17017
  if (_searchFor === "") return _textToSearch;
16967
- const _replaceWith = toString(replaceWith);
17018
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
16968
17019
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
16969
17020
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
16970
17021
  let n = 0;
@@ -16981,10 +17032,10 @@ stores.inject(MyMetaStore, storeInstance);
16981
17032
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
16982
17033
  ],
16983
17034
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
16984
- const _delimiter = toString(delimiter);
17035
+ const _delimiter = toLocaleString(delimiter, this.locale);
16985
17036
  const _ignoreEmpty = toBoolean(ignoreEmpty);
16986
17037
  let n = 0;
16987
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
17038
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
16988
17039
  },
16989
17040
  isExported: true
16990
17041
  };
@@ -17031,7 +17082,7 @@ stores.inject(MyMetaStore, storeInstance);
17031
17082
  description: _t("Removes space characters."),
17032
17083
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
17033
17084
  compute: function(text) {
17034
- return trimContent(toString(text));
17085
+ return trimContent(toLocaleString(text, this.locale));
17035
17086
  },
17036
17087
  isExported: true
17037
17088
  };
@@ -17039,7 +17090,7 @@ stores.inject(MyMetaStore, storeInstance);
17039
17090
  description: _t("Converts a specified string to uppercase."),
17040
17091
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
17041
17092
  compute: function(text) {
17042
- return toString(text).toUpperCase();
17093
+ return toLocaleString(text, this.locale).toUpperCase();
17043
17094
  },
17044
17095
  isExported: true
17045
17096
  };
@@ -17071,7 +17122,7 @@ stores.inject(MyMetaStore, storeInstance);
17071
17122
  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."))],
17072
17123
  compute: function(url, linkLabel) {
17073
17124
  const processedUrl = toString(url).trim();
17074
- const processedLabel = toString(linkLabel) || processedUrl;
17125
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
17075
17126
  if (processedUrl === "") return processedLabel;
17076
17127
  return markdownLink(processedLabel, processedUrl);
17077
17128
  },
@@ -17178,12 +17229,15 @@ stores.inject(MyMetaStore, storeInstance);
17178
17229
  }
17179
17230
  }
17180
17231
  function createComputeFunction(descr) {
17232
+ let currentArgDefinitions = [];
17181
17233
  function vectorizedCompute(...args) {
17182
17234
  const acceptToVectorize = [];
17235
+ currentArgDefinitions = new Array(args.length);
17183
17236
  const getArgToFocus = argTargeting(descr, args.length);
17184
17237
  for (let i = 0; i < args.length; i++) {
17185
17238
  const argIndex = getArgToFocus(i) ?? -1;
17186
17239
  const argDefinition = descr.args[argIndex];
17240
+ currentArgDefinitions[i] = argDefinition;
17187
17241
  const arg = args[i];
17188
17242
  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()));
17189
17243
  acceptToVectorize.push(!argDefinition.acceptMatrix);
@@ -17198,8 +17252,7 @@ stores.inject(MyMetaStore, storeInstance);
17198
17252
  function errorHandlingCompute(...args) {
17199
17253
  for (let i = 0; i < args.length; i++) {
17200
17254
  const arg = args[i];
17201
- const getArgToFocus = argTargeting(descr, args.length);
17202
- if (!descr.args[getArgToFocus(i) || i].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
17255
+ if (!currentArgDefinitions[i].acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) return arg;
17203
17256
  }
17204
17257
  try {
17205
17258
  return computeFunctionToObject.apply(this, args);
@@ -31253,12 +31306,13 @@ stores.inject(MyMetaStore, storeInstance);
31253
31306
  }
31254
31307
  sortFilterZone(sortDirection) {
31255
31308
  const filterPosition = this.props.filterPosition;
31256
- const tableZone = this.table?.range.zone;
31309
+ const table = this.table;
31310
+ const tableZone = table?.range.zone;
31257
31311
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
31258
31312
  const sheetId = this.env.model.getters.getActiveSheetId();
31259
31313
  const contentZone = {
31260
31314
  ...tableZone,
31261
- top: tableZone.top + 1
31315
+ top: tableZone.top + table.config.numberOfHeaders
31262
31316
  };
31263
31317
  const sortAnchor = {
31264
31318
  col: filterPosition.col,
@@ -47929,6 +47983,7 @@ stores.inject(MyMetaStore, storeInstance);
47929
47983
  .o-spreadsheet {
47930
47984
  .os-input {
47931
47985
  border-width: 0 0 1px 0;
47986
+ border-style: solid;
47932
47987
  border-color: transparent;
47933
47988
  outline: none;
47934
47989
  text-overflow: ellipsis;
@@ -70908,6 +70963,7 @@ stores.inject(MyMetaStore, storeInstance);
70908
70963
  width: 100%;
70909
70964
  outline: none;
70910
70965
  border-color: ${GRAY_300};
70966
+ border-style: solid;
70911
70967
  color: ${GRAY_900};
70912
70968
 
70913
70969
  &::placeholder {
@@ -75431,8 +75487,8 @@ exports.stores = stores;
75431
75487
  exports.tokenColors = tokenColors;
75432
75488
  exports.tokenize = tokenize;
75433
75489
 
75434
- __info__.version = "18.4.50";
75435
- __info__.date = "2026-07-30T06:52:55.061Z";
75436
- __info__.hash = "0a41352";
75490
+ __info__.version = "18.4.52";
75491
+ __info__.date = "2026-08-21T15:29:26.810Z";
75492
+ __info__.hash = "5715a49";
75437
75493
 
75438
75494
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);