@odoo/o-spreadsheet 19.1.31 → 19.1.32

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 19.1.31
6
- * @date 2026-08-10T12:30:53.702Z
7
- * @hash 8ac65ec
5
+ * @version 19.1.32
6
+ * @date 2026-08-21T15:29:31.737Z
7
+ * @hash d34d0e7
8
8
  */
9
9
  :root {
10
10
  --os-gray-100: light-dark(#f9fafb, #1b1d26);
@@ -291,6 +291,7 @@
291
291
  .os-input {
292
292
  border-width: 0 0 1px 0;
293
293
  border-color: transparent;
294
+ border-style: solid;
294
295
  outline: none;
295
296
  text-overflow: ellipsis;
296
297
  color: var(--os-text-body);
@@ -542,6 +543,7 @@
542
543
  width: 100%;
543
544
  outline: none;
544
545
  border-color: var(--os-border-color);
546
+ border-style: solid;
545
547
  color: var(--os-gray-900);
546
548
 
547
549
  &::placeholder {
@@ -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 19.1.31
6
- * @date 2026-08-10T12:30:52.454Z
7
- * @hash 8ac65ec
5
+ * @version 19.1.32
6
+ * @date 2026-08-21T15:29:30.638Z
7
+ * @hash d34d0e7
8
8
  */
9
9
 
10
10
  import { App, Component, blockDom, markRaw, onMounted, onPatched, onWillPatch, onWillStart, onWillUnmount, onWillUpdateProps, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, whenReady, xml } from "@odoo/owl";
@@ -3726,6 +3726,18 @@ function toString(data) {
3726
3726
  default: return "";
3727
3727
  }
3728
3728
  }
3729
+ /**
3730
+ * Only converts the decimal separator, ignore number format such as % or dates
3731
+ */
3732
+ function toLocaleString(data, locale) {
3733
+ const value = toValue(data);
3734
+ switch (typeof value) {
3735
+ case "string": return value;
3736
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3737
+ case "boolean": return value ? "TRUE" : "FALSE";
3738
+ default: return "";
3739
+ }
3740
+ }
3729
3741
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3730
3742
  const normalizeString = memoize(function normalizeString(str) {
3731
3743
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -19060,7 +19072,7 @@ const ARRAYTOTEXT = {
19060
19072
  else if (_format === 0) {
19061
19073
  const rowSeparator = this.locale.decimalSeparator === "," ? "/" : ",";
19062
19074
  return transposeMatrix(_array).flatMap((row) => row.map((value) => {
19063
- return isEvaluationError(value.value) ? value.value : toString(value);
19075
+ return isEvaluationError(value.value) ? value.value : toLocaleString(value, this.locale);
19064
19076
  })).join(rowSeparator);
19065
19077
  } else return new EvaluationError(_t("Format must be 0 or 1"));
19066
19078
  },
@@ -25046,7 +25058,7 @@ const CONCAT = {
25046
25058
  description: _t("Concatenation of two values."),
25047
25059
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
25048
25060
  compute: function(value1, value2) {
25049
- return toString(value1) + toString(value2);
25061
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
25050
25062
  },
25051
25063
  isExported: true
25052
25064
  };
@@ -25878,7 +25890,7 @@ const CLEAN = {
25878
25890
  description: _t("Remove non-printable characters from a piece of text."),
25879
25891
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
25880
25892
  compute: function(text) {
25881
- const _text = toString(text);
25893
+ const _text = toLocaleString(text, this.locale);
25882
25894
  let cleanedStr = "";
25883
25895
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
25884
25896
  return cleanedStr;
@@ -25889,7 +25901,7 @@ const CONCATENATE = {
25889
25901
  description: _t("Appends strings to one another."),
25890
25902
  args: [arg("string (string, range<string>, repeating)", _t("String to append in sequence."))],
25891
25903
  compute: function(...datas) {
25892
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
25904
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
25893
25905
  },
25894
25906
  isExported: true
25895
25907
  };
@@ -25897,7 +25909,7 @@ const EXACT = {
25897
25909
  description: _t("Tests whether two strings are identical."),
25898
25910
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
25899
25911
  compute: function(string1, string2) {
25900
- return toString(string1) === toString(string2);
25912
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
25901
25913
  },
25902
25914
  isExported: true
25903
25915
  };
@@ -25909,8 +25921,8 @@ const FIND = {
25909
25921
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
25910
25922
  ],
25911
25923
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
25912
- const _searchFor = toString(searchFor);
25913
- const _textToSearch = toString(textToSearch);
25924
+ const _searchFor = toLocaleString(searchFor, this.locale);
25925
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
25914
25926
  const _startingAt = toNumber(startingAt, this.locale);
25915
25927
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
25916
25928
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -25924,8 +25936,8 @@ const JOIN = {
25924
25936
  description: _t("Concatenates elements of arrays with delimiter."),
25925
25937
  args: [arg("delimiter (string)", _t("The character or string to place between each concatenated value.")), arg("value_or_array (string, range<string>, repeating)", _t("Value to be appended using delimiter."))],
25926
25938
  compute: function(delimiter, ...valuesOrArrays) {
25927
- const _delimiter = toString(delimiter);
25928
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
25939
+ const _delimiter = toLocaleString(delimiter, this.locale);
25940
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
25929
25941
  }
25930
25942
  };
25931
25943
  const LEFT = {
@@ -25934,7 +25946,7 @@ const LEFT = {
25934
25946
  compute: function(text, ...args) {
25935
25947
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
25936
25948
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
25937
- return toString(text).substring(0, _numberOfCharacters);
25949
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
25938
25950
  },
25939
25951
  isExported: true
25940
25952
  };
@@ -25942,7 +25954,7 @@ const LEN = {
25942
25954
  description: _t("Length of a string."),
25943
25955
  args: [arg("text (string)", _t("The string whose length will be returned."))],
25944
25956
  compute: function(text) {
25945
- return toString(text).length;
25957
+ return toLocaleString(text, this.locale).length;
25946
25958
  },
25947
25959
  isExported: true
25948
25960
  };
@@ -25950,7 +25962,7 @@ const LOWER = {
25950
25962
  description: _t("Converts a specified string to lowercase."),
25951
25963
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
25952
25964
  compute: function(text) {
25953
- return toString(text).toLowerCase();
25965
+ return toLocaleString(text, this.locale).toLowerCase();
25954
25966
  },
25955
25967
  isExported: true
25956
25968
  };
@@ -25962,7 +25974,7 @@ const MID = {
25962
25974
  arg("extract_length (number)", _t("The length of the segment to extract."))
25963
25975
  ],
25964
25976
  compute: function(text, starting_at, extract_length) {
25965
- const _text = toString(text);
25977
+ const _text = toLocaleString(text, this.locale);
25966
25978
  const _starting_at = toNumber(starting_at, this.locale);
25967
25979
  const _extract_length = toNumber(extract_length, this.locale);
25968
25980
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -25975,7 +25987,7 @@ const PROPER = {
25975
25987
  description: _t("Capitalizes each word in a specified string."),
25976
25988
  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."))],
25977
25989
  compute: function(text) {
25978
- return toString(text).replace(wordRegex, (word) => {
25990
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
25979
25991
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
25980
25992
  });
25981
25993
  },
@@ -26050,9 +26062,9 @@ const REPLACE = {
26050
26062
  compute: function(text, position, length, newText) {
26051
26063
  const _position = toNumber(position, this.locale);
26052
26064
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
26053
- const _text = toString(text);
26065
+ const _text = toLocaleString(text, this.locale);
26054
26066
  const _length = toNumber(length, this.locale);
26055
- const _newText = toString(newText);
26067
+ const _newText = toLocaleString(newText, this.locale);
26056
26068
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
26057
26069
  },
26058
26070
  isExported: true
@@ -26063,7 +26075,7 @@ const RIGHT = {
26063
26075
  compute: function(text, ...args) {
26064
26076
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
26065
26077
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
26066
- const _text = toString(text);
26078
+ const _text = toLocaleString(text, this.locale);
26067
26079
  const stringLength = _text.length;
26068
26080
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
26069
26081
  },
@@ -26077,8 +26089,8 @@ const SEARCH = {
26077
26089
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
26078
26090
  ],
26079
26091
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
26080
- const _searchFor = toString(searchFor).toLowerCase();
26081
- const _textToSearch = toString(textToSearch).toLowerCase();
26092
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
26093
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
26082
26094
  const _startingAt = toNumber(startingAt, this.locale);
26083
26095
  if (_textToSearch === "") return {
26084
26096
  value: CellErrorType.GenericError,
@@ -26108,8 +26120,8 @@ const SPLIT = {
26108
26120
  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."))
26109
26121
  ],
26110
26122
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
26111
- const _text = toString(text);
26112
- const _delimiter = escapeRegExp(toString(delimiter));
26123
+ const _text = toLocaleString(text, this.locale);
26124
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
26113
26125
  const _splitByEach = toBoolean(splitByEach);
26114
26126
  const _removeEmptyText = toBoolean(removeEmptyText);
26115
26127
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -26131,10 +26143,10 @@ const SUBSTITUTE = {
26131
26143
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
26132
26144
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
26133
26145
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
26134
- const _textToSearch = toString(textToSearch);
26135
- const _searchFor = toString(searchFor);
26146
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
26147
+ const _searchFor = toLocaleString(searchFor, this.locale);
26136
26148
  if (_searchFor === "") return _textToSearch;
26137
- const _replaceWith = toString(replaceWith);
26149
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
26138
26150
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
26139
26151
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
26140
26152
  let n = 0;
@@ -26157,10 +26169,10 @@ const TEXTJOIN = {
26157
26169
  arg("texts (string, range<string>, repeating)", _t("Text item to join."))
26158
26170
  ],
26159
26171
  compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
26160
- const _delimiter = toString(delimiter);
26172
+ const _delimiter = toLocaleString(delimiter, this.locale);
26161
26173
  const _ignoreEmpty = toBoolean(ignoreEmpty);
26162
26174
  let n = 0;
26163
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
26175
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
26164
26176
  },
26165
26177
  isExported: true
26166
26178
  };
@@ -26213,7 +26225,7 @@ const TRIM = {
26213
26225
  description: _t("Removes space characters."),
26214
26226
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
26215
26227
  compute: function(text) {
26216
- return trimContent(toString(text));
26228
+ return trimContent(toLocaleString(text, this.locale));
26217
26229
  },
26218
26230
  isExported: true
26219
26231
  };
@@ -26221,7 +26233,7 @@ const UPPER = {
26221
26233
  description: _t("Converts a specified string to uppercase."),
26222
26234
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
26223
26235
  compute: function(text) {
26224
- return toString(text).toUpperCase();
26236
+ return toLocaleString(text, this.locale).toUpperCase();
26225
26237
  },
26226
26238
  isExported: true
26227
26239
  };
@@ -26318,7 +26330,7 @@ const HYPERLINK = {
26318
26330
  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."))],
26319
26331
  compute: function(url, linkLabel) {
26320
26332
  const processedUrl = toString(url).trim();
26321
- const processedLabel = toString(linkLabel) || processedUrl;
26333
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
26322
26334
  if (processedUrl === "") return processedLabel;
26323
26335
  return markdownLink(processedLabel, processedUrl);
26324
26336
  },
@@ -30642,12 +30654,13 @@ var FilterMenu = class extends Component {
30642
30654
  }
30643
30655
  sortFilterZone(sortDirection) {
30644
30656
  const filterPosition = this.props.filterPosition;
30645
- const tableZone = this.table?.range.zone;
30657
+ const table = this.table;
30658
+ const tableZone = table?.range.zone;
30646
30659
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
30647
30660
  const sheetId = this.env.model.getters.getActiveSheetId();
30648
30661
  const contentZone = {
30649
30662
  ...tableZone,
30650
- top: tableZone.top + 1
30663
+ top: tableZone.top + table.config.numberOfHeaders
30651
30664
  };
30652
30665
  const sortAnchor = {
30653
30666
  col: filterPosition.col,
@@ -79182,6 +79195,6 @@ const chartHelpers = {
79182
79195
  //#endregion
79183
79196
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DEFAULT_LOCALE, DEFAULT_LOCALES, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, categories, chartHelpers, components, constants, convertAstNodes, coreTypes, createAutocompleteArgumentsProvider, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
79184
79197
 
79185
- __info__.version = "19.1.31";
79186
- __info__.date = "2026-08-10T12:30:52.454Z";
79187
- __info__.hash = "8ac65ec";
79198
+ __info__.version = "19.1.32";
79199
+ __info__.date = "2026-08-21T15:29:30.638Z";
79200
+ __info__.hash = "d34d0e7";
@@ -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 19.1.31
6
- * @date 2026-08-10T12:30:52.454Z
7
- * @hash 8ac65ec
5
+ * @version 19.1.32
6
+ * @date 2026-08-21T15:29:30.638Z
7
+ * @hash d34d0e7
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -3728,6 +3728,18 @@ stores.inject(MyMetaStore, storeInstance);
3728
3728
  default: return "";
3729
3729
  }
3730
3730
  }
3731
+ /**
3732
+ * Only converts the decimal separator, ignore number format such as % or dates
3733
+ */
3734
+ function toLocaleString(data, locale) {
3735
+ const value = toValue(data);
3736
+ switch (typeof value) {
3737
+ case "string": return value;
3738
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3739
+ case "boolean": return value ? "TRUE" : "FALSE";
3740
+ default: return "";
3741
+ }
3742
+ }
3731
3743
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3732
3744
  const normalizeString = memoize(function normalizeString(str) {
3733
3745
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -19062,7 +19074,7 @@ stores.inject(MyMetaStore, storeInstance);
19062
19074
  else if (_format === 0) {
19063
19075
  const rowSeparator = this.locale.decimalSeparator === "," ? "/" : ",";
19064
19076
  return transposeMatrix(_array).flatMap((row) => row.map((value) => {
19065
- return isEvaluationError(value.value) ? value.value : toString(value);
19077
+ return isEvaluationError(value.value) ? value.value : toLocaleString(value, this.locale);
19066
19078
  })).join(rowSeparator);
19067
19079
  } else return new EvaluationError(_t("Format must be 0 or 1"));
19068
19080
  },
@@ -25048,7 +25060,7 @@ stores.inject(MyMetaStore, storeInstance);
25048
25060
  description: _t("Concatenation of two values."),
25049
25061
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
25050
25062
  compute: function(value1, value2) {
25051
- return toString(value1) + toString(value2);
25063
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
25052
25064
  },
25053
25065
  isExported: true
25054
25066
  };
@@ -25880,7 +25892,7 @@ stores.inject(MyMetaStore, storeInstance);
25880
25892
  description: _t("Remove non-printable characters from a piece of text."),
25881
25893
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
25882
25894
  compute: function(text) {
25883
- const _text = toString(text);
25895
+ const _text = toLocaleString(text, this.locale);
25884
25896
  let cleanedStr = "";
25885
25897
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
25886
25898
  return cleanedStr;
@@ -25891,7 +25903,7 @@ stores.inject(MyMetaStore, storeInstance);
25891
25903
  description: _t("Appends strings to one another."),
25892
25904
  args: [arg("string (string, range<string>, repeating)", _t("String to append in sequence."))],
25893
25905
  compute: function(...datas) {
25894
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
25906
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
25895
25907
  },
25896
25908
  isExported: true
25897
25909
  };
@@ -25899,7 +25911,7 @@ stores.inject(MyMetaStore, storeInstance);
25899
25911
  description: _t("Tests whether two strings are identical."),
25900
25912
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
25901
25913
  compute: function(string1, string2) {
25902
- return toString(string1) === toString(string2);
25914
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
25903
25915
  },
25904
25916
  isExported: true
25905
25917
  };
@@ -25911,8 +25923,8 @@ stores.inject(MyMetaStore, storeInstance);
25911
25923
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
25912
25924
  ],
25913
25925
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
25914
- const _searchFor = toString(searchFor);
25915
- const _textToSearch = toString(textToSearch);
25926
+ const _searchFor = toLocaleString(searchFor, this.locale);
25927
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
25916
25928
  const _startingAt = toNumber(startingAt, this.locale);
25917
25929
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
25918
25930
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -25926,8 +25938,8 @@ stores.inject(MyMetaStore, storeInstance);
25926
25938
  description: _t("Concatenates elements of arrays with delimiter."),
25927
25939
  args: [arg("delimiter (string)", _t("The character or string to place between each concatenated value.")), arg("value_or_array (string, range<string>, repeating)", _t("Value to be appended using delimiter."))],
25928
25940
  compute: function(delimiter, ...valuesOrArrays) {
25929
- const _delimiter = toString(delimiter);
25930
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
25941
+ const _delimiter = toLocaleString(delimiter, this.locale);
25942
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
25931
25943
  }
25932
25944
  };
25933
25945
  const LEFT = {
@@ -25936,7 +25948,7 @@ stores.inject(MyMetaStore, storeInstance);
25936
25948
  compute: function(text, ...args) {
25937
25949
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
25938
25950
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
25939
- return toString(text).substring(0, _numberOfCharacters);
25951
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
25940
25952
  },
25941
25953
  isExported: true
25942
25954
  };
@@ -25944,7 +25956,7 @@ stores.inject(MyMetaStore, storeInstance);
25944
25956
  description: _t("Length of a string."),
25945
25957
  args: [arg("text (string)", _t("The string whose length will be returned."))],
25946
25958
  compute: function(text) {
25947
- return toString(text).length;
25959
+ return toLocaleString(text, this.locale).length;
25948
25960
  },
25949
25961
  isExported: true
25950
25962
  };
@@ -25952,7 +25964,7 @@ stores.inject(MyMetaStore, storeInstance);
25952
25964
  description: _t("Converts a specified string to lowercase."),
25953
25965
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
25954
25966
  compute: function(text) {
25955
- return toString(text).toLowerCase();
25967
+ return toLocaleString(text, this.locale).toLowerCase();
25956
25968
  },
25957
25969
  isExported: true
25958
25970
  };
@@ -25964,7 +25976,7 @@ stores.inject(MyMetaStore, storeInstance);
25964
25976
  arg("extract_length (number)", _t("The length of the segment to extract."))
25965
25977
  ],
25966
25978
  compute: function(text, starting_at, extract_length) {
25967
- const _text = toString(text);
25979
+ const _text = toLocaleString(text, this.locale);
25968
25980
  const _starting_at = toNumber(starting_at, this.locale);
25969
25981
  const _extract_length = toNumber(extract_length, this.locale);
25970
25982
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -25977,7 +25989,7 @@ stores.inject(MyMetaStore, storeInstance);
25977
25989
  description: _t("Capitalizes each word in a specified string."),
25978
25990
  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."))],
25979
25991
  compute: function(text) {
25980
- return toString(text).replace(wordRegex, (word) => {
25992
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
25981
25993
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
25982
25994
  });
25983
25995
  },
@@ -26052,9 +26064,9 @@ stores.inject(MyMetaStore, storeInstance);
26052
26064
  compute: function(text, position, length, newText) {
26053
26065
  const _position = toNumber(position, this.locale);
26054
26066
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
26055
- const _text = toString(text);
26067
+ const _text = toLocaleString(text, this.locale);
26056
26068
  const _length = toNumber(length, this.locale);
26057
- const _newText = toString(newText);
26069
+ const _newText = toLocaleString(newText, this.locale);
26058
26070
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
26059
26071
  },
26060
26072
  isExported: true
@@ -26065,7 +26077,7 @@ stores.inject(MyMetaStore, storeInstance);
26065
26077
  compute: function(text, ...args) {
26066
26078
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
26067
26079
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
26068
- const _text = toString(text);
26080
+ const _text = toLocaleString(text, this.locale);
26069
26081
  const stringLength = _text.length;
26070
26082
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
26071
26083
  },
@@ -26079,8 +26091,8 @@ stores.inject(MyMetaStore, storeInstance);
26079
26091
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
26080
26092
  ],
26081
26093
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
26082
- const _searchFor = toString(searchFor).toLowerCase();
26083
- const _textToSearch = toString(textToSearch).toLowerCase();
26094
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
26095
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
26084
26096
  const _startingAt = toNumber(startingAt, this.locale);
26085
26097
  if (_textToSearch === "") return {
26086
26098
  value: CellErrorType.GenericError,
@@ -26110,8 +26122,8 @@ stores.inject(MyMetaStore, storeInstance);
26110
26122
  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."))
26111
26123
  ],
26112
26124
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
26113
- const _text = toString(text);
26114
- const _delimiter = escapeRegExp(toString(delimiter));
26125
+ const _text = toLocaleString(text, this.locale);
26126
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
26115
26127
  const _splitByEach = toBoolean(splitByEach);
26116
26128
  const _removeEmptyText = toBoolean(removeEmptyText);
26117
26129
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -26133,10 +26145,10 @@ stores.inject(MyMetaStore, storeInstance);
26133
26145
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
26134
26146
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
26135
26147
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
26136
- const _textToSearch = toString(textToSearch);
26137
- const _searchFor = toString(searchFor);
26148
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
26149
+ const _searchFor = toLocaleString(searchFor, this.locale);
26138
26150
  if (_searchFor === "") return _textToSearch;
26139
- const _replaceWith = toString(replaceWith);
26151
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
26140
26152
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
26141
26153
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
26142
26154
  let n = 0;
@@ -26159,10 +26171,10 @@ stores.inject(MyMetaStore, storeInstance);
26159
26171
  arg("texts (string, range<string>, repeating)", _t("Text item to join."))
26160
26172
  ],
26161
26173
  compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
26162
- const _delimiter = toString(delimiter);
26174
+ const _delimiter = toLocaleString(delimiter, this.locale);
26163
26175
  const _ignoreEmpty = toBoolean(ignoreEmpty);
26164
26176
  let n = 0;
26165
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
26177
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
26166
26178
  },
26167
26179
  isExported: true
26168
26180
  };
@@ -26215,7 +26227,7 @@ stores.inject(MyMetaStore, storeInstance);
26215
26227
  description: _t("Removes space characters."),
26216
26228
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
26217
26229
  compute: function(text) {
26218
- return trimContent(toString(text));
26230
+ return trimContent(toLocaleString(text, this.locale));
26219
26231
  },
26220
26232
  isExported: true
26221
26233
  };
@@ -26223,7 +26235,7 @@ stores.inject(MyMetaStore, storeInstance);
26223
26235
  description: _t("Converts a specified string to uppercase."),
26224
26236
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
26225
26237
  compute: function(text) {
26226
- return toString(text).toUpperCase();
26238
+ return toLocaleString(text, this.locale).toUpperCase();
26227
26239
  },
26228
26240
  isExported: true
26229
26241
  };
@@ -26320,7 +26332,7 @@ stores.inject(MyMetaStore, storeInstance);
26320
26332
  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."))],
26321
26333
  compute: function(url, linkLabel) {
26322
26334
  const processedUrl = toString(url).trim();
26323
- const processedLabel = toString(linkLabel) || processedUrl;
26335
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
26324
26336
  if (processedUrl === "") return processedLabel;
26325
26337
  return markdownLink(processedLabel, processedUrl);
26326
26338
  },
@@ -30644,12 +30656,13 @@ stores.inject(MyMetaStore, storeInstance);
30644
30656
  }
30645
30657
  sortFilterZone(sortDirection) {
30646
30658
  const filterPosition = this.props.filterPosition;
30647
- const tableZone = this.table?.range.zone;
30659
+ const table = this.table;
30660
+ const tableZone = table?.range.zone;
30648
30661
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
30649
30662
  const sheetId = this.env.model.getters.getActiveSheetId();
30650
30663
  const contentZone = {
30651
30664
  ...tableZone,
30652
- top: tableZone.top + 1
30665
+ top: tableZone.top + table.config.numberOfHeaders
30653
30666
  };
30654
30667
  const sortAnchor = {
30655
30668
  col: filterPosition.col,
@@ -79237,8 +79250,8 @@ exports.stores = stores;
79237
79250
  exports.tokenColors = tokenColors;
79238
79251
  exports.tokenize = tokenize;
79239
79252
 
79240
- __info__.version = "19.1.31";
79241
- __info__.date = "2026-08-10T12:30:52.454Z";
79242
- __info__.hash = "8ac65ec";
79253
+ __info__.version = "19.1.32";
79254
+ __info__.date = "2026-08-21T15:29:30.638Z";
79255
+ __info__.hash = "d34d0e7";
79243
79256
 
79244
79257
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);