@odoo/o-spreadsheet 19.0.46 → 19.0.47

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.0.46
6
- * @date 2026-08-10T12:30:50.771Z
7
- * @hash b721d1f
5
+ * @version 19.0.47
6
+ * @date 2026-08-21T15:29:01.277Z
7
+ * @hash 59a9b1b
8
8
  */
9
9
  /* Originates from src/components/top_bar/top_bar.scss */
10
10
  @media (max-width: 1200px) {
@@ -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.0.46
6
- * @date 2026-08-10T12:30:49.160Z
7
- * @hash b721d1f
5
+ * @version 19.0.47
6
+ * @date 2026-08-21T15:28:59.913Z
7
+ * @hash 59a9b1b
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, xml } from "@odoo/owl";
@@ -2919,6 +2919,18 @@ function toString(data) {
2919
2919
  default: return "";
2920
2920
  }
2921
2921
  }
2922
+ /**
2923
+ * Only converts the decimal separator, ignore number format such as % or dates
2924
+ */
2925
+ function toLocaleString(data, locale) {
2926
+ const value = toValue(data);
2927
+ switch (typeof value) {
2928
+ case "string": return value;
2929
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
2930
+ case "boolean": return value ? "TRUE" : "FALSE";
2931
+ default: return "";
2932
+ }
2933
+ }
2922
2934
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2923
2935
  const normalizeString = memoize(function normalizeString(str) {
2924
2936
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -17164,7 +17176,7 @@ const CONCAT = {
17164
17176
  description: _t("Concatenation of two values."),
17165
17177
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
17166
17178
  compute: function(value1, value2) {
17167
- return toString(value1) + toString(value2);
17179
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
17168
17180
  },
17169
17181
  isExported: true
17170
17182
  };
@@ -17996,7 +18008,7 @@ const CLEAN = {
17996
18008
  description: _t("Remove non-printable characters from a piece of text."),
17997
18009
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
17998
18010
  compute: function(text) {
17999
- const _text = toString(text);
18011
+ const _text = toLocaleString(text, this.locale);
18000
18012
  let cleanedStr = "";
18001
18013
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
18002
18014
  return cleanedStr;
@@ -18007,7 +18019,7 @@ const CONCATENATE = {
18007
18019
  description: _t("Appends strings to one another."),
18008
18020
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
18009
18021
  compute: function(...datas) {
18010
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
18022
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
18011
18023
  },
18012
18024
  isExported: true
18013
18025
  };
@@ -18015,7 +18027,7 @@ const EXACT = {
18015
18027
  description: _t("Tests whether two strings are identical."),
18016
18028
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
18017
18029
  compute: function(string1, string2) {
18018
- return toString(string1) === toString(string2);
18030
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
18019
18031
  },
18020
18032
  isExported: true
18021
18033
  };
@@ -18027,8 +18039,8 @@ const FIND = {
18027
18039
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
18028
18040
  ],
18029
18041
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
18030
- const _searchFor = toString(searchFor);
18031
- const _textToSearch = toString(textToSearch);
18042
+ const _searchFor = toLocaleString(searchFor, this.locale);
18043
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
18032
18044
  const _startingAt = toNumber(startingAt, this.locale);
18033
18045
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
18034
18046
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -18046,8 +18058,8 @@ const JOIN = {
18046
18058
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
18047
18059
  ],
18048
18060
  compute: function(delimiter, ...valuesOrArrays) {
18049
- const _delimiter = toString(delimiter);
18050
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
18061
+ const _delimiter = toLocaleString(delimiter, this.locale);
18062
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
18051
18063
  }
18052
18064
  };
18053
18065
  const LEFT = {
@@ -18056,7 +18068,7 @@ const LEFT = {
18056
18068
  compute: function(text, ...args) {
18057
18069
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
18058
18070
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
18059
- return toString(text).substring(0, _numberOfCharacters);
18071
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
18060
18072
  },
18061
18073
  isExported: true
18062
18074
  };
@@ -18064,7 +18076,7 @@ const LEN = {
18064
18076
  description: _t("Length of a string."),
18065
18077
  args: [arg("text (string)", _t("The string whose length will be returned."))],
18066
18078
  compute: function(text) {
18067
- return toString(text).length;
18079
+ return toLocaleString(text, this.locale).length;
18068
18080
  },
18069
18081
  isExported: true
18070
18082
  };
@@ -18072,7 +18084,7 @@ const LOWER = {
18072
18084
  description: _t("Converts a specified string to lowercase."),
18073
18085
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
18074
18086
  compute: function(text) {
18075
- return toString(text).toLowerCase();
18087
+ return toLocaleString(text, this.locale).toLowerCase();
18076
18088
  },
18077
18089
  isExported: true
18078
18090
  };
@@ -18084,7 +18096,7 @@ const MID = {
18084
18096
  arg("extract_length (number)", _t("The length of the segment to extract."))
18085
18097
  ],
18086
18098
  compute: function(text, starting_at, extract_length) {
18087
- const _text = toString(text);
18099
+ const _text = toLocaleString(text, this.locale);
18088
18100
  const _starting_at = toNumber(starting_at, this.locale);
18089
18101
  const _extract_length = toNumber(extract_length, this.locale);
18090
18102
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -18097,7 +18109,7 @@ const PROPER = {
18097
18109
  description: _t("Capitalizes each word in a specified string."),
18098
18110
  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."))],
18099
18111
  compute: function(text) {
18100
- return toString(text).replace(wordRegex, (word) => {
18112
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
18101
18113
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
18102
18114
  });
18103
18115
  },
@@ -18172,9 +18184,9 @@ const REPLACE = {
18172
18184
  compute: function(text, position, length, newText) {
18173
18185
  const _position = toNumber(position, this.locale);
18174
18186
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
18175
- const _text = toString(text);
18187
+ const _text = toLocaleString(text, this.locale);
18176
18188
  const _length = toNumber(length, this.locale);
18177
- const _newText = toString(newText);
18189
+ const _newText = toLocaleString(newText, this.locale);
18178
18190
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
18179
18191
  },
18180
18192
  isExported: true
@@ -18185,7 +18197,7 @@ const RIGHT = {
18185
18197
  compute: function(text, ...args) {
18186
18198
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
18187
18199
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
18188
- const _text = toString(text);
18200
+ const _text = toLocaleString(text, this.locale);
18189
18201
  const stringLength = _text.length;
18190
18202
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
18191
18203
  },
@@ -18199,8 +18211,8 @@ const SEARCH = {
18199
18211
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
18200
18212
  ],
18201
18213
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
18202
- const _searchFor = toString(searchFor).toLowerCase();
18203
- const _textToSearch = toString(textToSearch).toLowerCase();
18214
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
18215
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
18204
18216
  const _startingAt = toNumber(startingAt, this.locale);
18205
18217
  if (_textToSearch === "") return {
18206
18218
  value: CellErrorType.GenericError,
@@ -18230,8 +18242,8 @@ const SPLIT = {
18230
18242
  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."))
18231
18243
  ],
18232
18244
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
18233
- const _text = toString(text);
18234
- const _delimiter = escapeRegExp(toString(delimiter));
18245
+ const _text = toLocaleString(text, this.locale);
18246
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
18235
18247
  const _splitByEach = toBoolean(splitByEach);
18236
18248
  const _removeEmptyText = toBoolean(removeEmptyText);
18237
18249
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -18253,10 +18265,10 @@ const SUBSTITUTE = {
18253
18265
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
18254
18266
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
18255
18267
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
18256
- const _textToSearch = toString(textToSearch);
18257
- const _searchFor = toString(searchFor);
18268
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
18269
+ const _searchFor = toLocaleString(searchFor, this.locale);
18258
18270
  if (_searchFor === "") return _textToSearch;
18259
- const _replaceWith = toString(replaceWith);
18271
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
18260
18272
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
18261
18273
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
18262
18274
  let n = 0;
@@ -18280,10 +18292,10 @@ const TEXTJOIN = {
18280
18292
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
18281
18293
  ],
18282
18294
  compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
18283
- const _delimiter = toString(delimiter);
18295
+ const _delimiter = toLocaleString(delimiter, this.locale);
18284
18296
  const _ignoreEmpty = toBoolean(ignoreEmpty);
18285
18297
  let n = 0;
18286
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
18298
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
18287
18299
  },
18288
18300
  isExported: true
18289
18301
  };
@@ -18336,7 +18348,7 @@ const TRIM = {
18336
18348
  description: _t("Removes space characters."),
18337
18349
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
18338
18350
  compute: function(text) {
18339
- return trimContent(toString(text));
18351
+ return trimContent(toLocaleString(text, this.locale));
18340
18352
  },
18341
18353
  isExported: true
18342
18354
  };
@@ -18344,7 +18356,7 @@ const UPPER = {
18344
18356
  description: _t("Converts a specified string to uppercase."),
18345
18357
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
18346
18358
  compute: function(text) {
18347
- return toString(text).toUpperCase();
18359
+ return toLocaleString(text, this.locale).toUpperCase();
18348
18360
  },
18349
18361
  isExported: true
18350
18362
  };
@@ -18441,7 +18453,7 @@ const HYPERLINK = {
18441
18453
  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."))],
18442
18454
  compute: function(url, linkLabel) {
18443
18455
  const processedUrl = toString(url).trim();
18444
- const processedLabel = toString(linkLabel) || processedUrl;
18456
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
18445
18457
  if (processedUrl === "") return processedLabel;
18446
18458
  return markdownLink(processedLabel, processedUrl);
18447
18459
  },
@@ -33189,12 +33201,13 @@ var FilterMenu = class extends Component {
33189
33201
  }
33190
33202
  sortFilterZone(sortDirection) {
33191
33203
  const filterPosition = this.props.filterPosition;
33192
- const tableZone = this.table?.range.zone;
33204
+ const table = this.table;
33205
+ const tableZone = table?.range.zone;
33193
33206
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
33194
33207
  const sheetId = this.env.model.getters.getActiveSheetId();
33195
33208
  const contentZone = {
33196
33209
  ...tableZone,
33197
- top: tableZone.top + 1
33210
+ top: tableZone.top + table.config.numberOfHeaders
33198
33211
  };
33199
33212
  const sortAnchor = {
33200
33213
  col: filterPosition.col,
@@ -47297,6 +47310,7 @@ css`
47297
47310
  .o-spreadsheet {
47298
47311
  .os-input {
47299
47312
  border-width: 0 0 1px 0;
47313
+ border-style: solid;
47300
47314
  border-color: transparent;
47301
47315
  outline: none;
47302
47316
  text-overflow: ellipsis;
@@ -74833,6 +74847,7 @@ css`
74833
74847
  width: 100%;
74834
74848
  outline: none;
74835
74849
  border-color: ${GRAY_300};
74850
+ border-style: solid;
74836
74851
  color: ${GRAY_900};
74837
74852
 
74838
74853
  &::placeholder {
@@ -79439,6 +79454,6 @@ const chartHelpers = {
79439
79454
  //#endregion
79440
79455
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
79441
79456
 
79442
- __info__.version = "19.0.46";
79443
- __info__.date = "2026-08-10T12:30:49.160Z";
79444
- __info__.hash = "b721d1f";
79457
+ __info__.version = "19.0.47";
79458
+ __info__.date = "2026-08-21T15:28:59.913Z";
79459
+ __info__.hash = "59a9b1b";
@@ -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.0.46
6
- * @date 2026-08-10T12:30:49.160Z
7
- * @hash b721d1f
5
+ * @version 19.0.47
6
+ * @date 2026-08-21T15:28:59.913Z
7
+ * @hash 59a9b1b
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -2921,6 +2921,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2921
2921
  default: return "";
2922
2922
  }
2923
2923
  }
2924
+ /**
2925
+ * Only converts the decimal separator, ignore number format such as % or dates
2926
+ */
2927
+ function toLocaleString(data, locale) {
2928
+ const value = toValue(data);
2929
+ switch (typeof value) {
2930
+ case "string": return value;
2931
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
2932
+ case "boolean": return value ? "TRUE" : "FALSE";
2933
+ default: return "";
2934
+ }
2935
+ }
2924
2936
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2925
2937
  const normalizeString = memoize(function normalizeString(str) {
2926
2938
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -17166,7 +17178,7 @@ stores.inject(MyMetaStore, storeInstance);
17166
17178
  description: _t("Concatenation of two values."),
17167
17179
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
17168
17180
  compute: function(value1, value2) {
17169
- return toString(value1) + toString(value2);
17181
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
17170
17182
  },
17171
17183
  isExported: true
17172
17184
  };
@@ -17998,7 +18010,7 @@ stores.inject(MyMetaStore, storeInstance);
17998
18010
  description: _t("Remove non-printable characters from a piece of text."),
17999
18011
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
18000
18012
  compute: function(text) {
18001
- const _text = toString(text);
18013
+ const _text = toLocaleString(text, this.locale);
18002
18014
  let cleanedStr = "";
18003
18015
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
18004
18016
  return cleanedStr;
@@ -18009,7 +18021,7 @@ stores.inject(MyMetaStore, storeInstance);
18009
18021
  description: _t("Appends strings to one another."),
18010
18022
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
18011
18023
  compute: function(...datas) {
18012
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
18024
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
18013
18025
  },
18014
18026
  isExported: true
18015
18027
  };
@@ -18017,7 +18029,7 @@ stores.inject(MyMetaStore, storeInstance);
18017
18029
  description: _t("Tests whether two strings are identical."),
18018
18030
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
18019
18031
  compute: function(string1, string2) {
18020
- return toString(string1) === toString(string2);
18032
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
18021
18033
  },
18022
18034
  isExported: true
18023
18035
  };
@@ -18029,8 +18041,8 @@ stores.inject(MyMetaStore, storeInstance);
18029
18041
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
18030
18042
  ],
18031
18043
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
18032
- const _searchFor = toString(searchFor);
18033
- const _textToSearch = toString(textToSearch);
18044
+ const _searchFor = toLocaleString(searchFor, this.locale);
18045
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
18034
18046
  const _startingAt = toNumber(startingAt, this.locale);
18035
18047
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
18036
18048
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -18048,8 +18060,8 @@ stores.inject(MyMetaStore, storeInstance);
18048
18060
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
18049
18061
  ],
18050
18062
  compute: function(delimiter, ...valuesOrArrays) {
18051
- const _delimiter = toString(delimiter);
18052
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
18063
+ const _delimiter = toLocaleString(delimiter, this.locale);
18064
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
18053
18065
  }
18054
18066
  };
18055
18067
  const LEFT = {
@@ -18058,7 +18070,7 @@ stores.inject(MyMetaStore, storeInstance);
18058
18070
  compute: function(text, ...args) {
18059
18071
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
18060
18072
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
18061
- return toString(text).substring(0, _numberOfCharacters);
18073
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
18062
18074
  },
18063
18075
  isExported: true
18064
18076
  };
@@ -18066,7 +18078,7 @@ stores.inject(MyMetaStore, storeInstance);
18066
18078
  description: _t("Length of a string."),
18067
18079
  args: [arg("text (string)", _t("The string whose length will be returned."))],
18068
18080
  compute: function(text) {
18069
- return toString(text).length;
18081
+ return toLocaleString(text, this.locale).length;
18070
18082
  },
18071
18083
  isExported: true
18072
18084
  };
@@ -18074,7 +18086,7 @@ stores.inject(MyMetaStore, storeInstance);
18074
18086
  description: _t("Converts a specified string to lowercase."),
18075
18087
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
18076
18088
  compute: function(text) {
18077
- return toString(text).toLowerCase();
18089
+ return toLocaleString(text, this.locale).toLowerCase();
18078
18090
  },
18079
18091
  isExported: true
18080
18092
  };
@@ -18086,7 +18098,7 @@ stores.inject(MyMetaStore, storeInstance);
18086
18098
  arg("extract_length (number)", _t("The length of the segment to extract."))
18087
18099
  ],
18088
18100
  compute: function(text, starting_at, extract_length) {
18089
- const _text = toString(text);
18101
+ const _text = toLocaleString(text, this.locale);
18090
18102
  const _starting_at = toNumber(starting_at, this.locale);
18091
18103
  const _extract_length = toNumber(extract_length, this.locale);
18092
18104
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -18099,7 +18111,7 @@ stores.inject(MyMetaStore, storeInstance);
18099
18111
  description: _t("Capitalizes each word in a specified string."),
18100
18112
  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."))],
18101
18113
  compute: function(text) {
18102
- return toString(text).replace(wordRegex, (word) => {
18114
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
18103
18115
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
18104
18116
  });
18105
18117
  },
@@ -18174,9 +18186,9 @@ stores.inject(MyMetaStore, storeInstance);
18174
18186
  compute: function(text, position, length, newText) {
18175
18187
  const _position = toNumber(position, this.locale);
18176
18188
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
18177
- const _text = toString(text);
18189
+ const _text = toLocaleString(text, this.locale);
18178
18190
  const _length = toNumber(length, this.locale);
18179
- const _newText = toString(newText);
18191
+ const _newText = toLocaleString(newText, this.locale);
18180
18192
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
18181
18193
  },
18182
18194
  isExported: true
@@ -18187,7 +18199,7 @@ stores.inject(MyMetaStore, storeInstance);
18187
18199
  compute: function(text, ...args) {
18188
18200
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
18189
18201
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
18190
- const _text = toString(text);
18202
+ const _text = toLocaleString(text, this.locale);
18191
18203
  const stringLength = _text.length;
18192
18204
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
18193
18205
  },
@@ -18201,8 +18213,8 @@ stores.inject(MyMetaStore, storeInstance);
18201
18213
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
18202
18214
  ],
18203
18215
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
18204
- const _searchFor = toString(searchFor).toLowerCase();
18205
- const _textToSearch = toString(textToSearch).toLowerCase();
18216
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
18217
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
18206
18218
  const _startingAt = toNumber(startingAt, this.locale);
18207
18219
  if (_textToSearch === "") return {
18208
18220
  value: CellErrorType.GenericError,
@@ -18232,8 +18244,8 @@ stores.inject(MyMetaStore, storeInstance);
18232
18244
  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."))
18233
18245
  ],
18234
18246
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
18235
- const _text = toString(text);
18236
- const _delimiter = escapeRegExp(toString(delimiter));
18247
+ const _text = toLocaleString(text, this.locale);
18248
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
18237
18249
  const _splitByEach = toBoolean(splitByEach);
18238
18250
  const _removeEmptyText = toBoolean(removeEmptyText);
18239
18251
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -18255,10 +18267,10 @@ stores.inject(MyMetaStore, storeInstance);
18255
18267
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
18256
18268
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
18257
18269
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
18258
- const _textToSearch = toString(textToSearch);
18259
- const _searchFor = toString(searchFor);
18270
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
18271
+ const _searchFor = toLocaleString(searchFor, this.locale);
18260
18272
  if (_searchFor === "") return _textToSearch;
18261
- const _replaceWith = toString(replaceWith);
18273
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
18262
18274
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
18263
18275
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
18264
18276
  let n = 0;
@@ -18282,10 +18294,10 @@ stores.inject(MyMetaStore, storeInstance);
18282
18294
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
18283
18295
  ],
18284
18296
  compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
18285
- const _delimiter = toString(delimiter);
18297
+ const _delimiter = toLocaleString(delimiter, this.locale);
18286
18298
  const _ignoreEmpty = toBoolean(ignoreEmpty);
18287
18299
  let n = 0;
18288
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
18300
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
18289
18301
  },
18290
18302
  isExported: true
18291
18303
  };
@@ -18338,7 +18350,7 @@ stores.inject(MyMetaStore, storeInstance);
18338
18350
  description: _t("Removes space characters."),
18339
18351
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
18340
18352
  compute: function(text) {
18341
- return trimContent(toString(text));
18353
+ return trimContent(toLocaleString(text, this.locale));
18342
18354
  },
18343
18355
  isExported: true
18344
18356
  };
@@ -18346,7 +18358,7 @@ stores.inject(MyMetaStore, storeInstance);
18346
18358
  description: _t("Converts a specified string to uppercase."),
18347
18359
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
18348
18360
  compute: function(text) {
18349
- return toString(text).toUpperCase();
18361
+ return toLocaleString(text, this.locale).toUpperCase();
18350
18362
  },
18351
18363
  isExported: true
18352
18364
  };
@@ -18443,7 +18455,7 @@ stores.inject(MyMetaStore, storeInstance);
18443
18455
  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."))],
18444
18456
  compute: function(url, linkLabel) {
18445
18457
  const processedUrl = toString(url).trim();
18446
- const processedLabel = toString(linkLabel) || processedUrl;
18458
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
18447
18459
  if (processedUrl === "") return processedLabel;
18448
18460
  return markdownLink(processedLabel, processedUrl);
18449
18461
  },
@@ -33191,12 +33203,13 @@ stores.inject(MyMetaStore, storeInstance);
33191
33203
  }
33192
33204
  sortFilterZone(sortDirection) {
33193
33205
  const filterPosition = this.props.filterPosition;
33194
- const tableZone = this.table?.range.zone;
33206
+ const table = this.table;
33207
+ const tableZone = table?.range.zone;
33195
33208
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
33196
33209
  const sheetId = this.env.model.getters.getActiveSheetId();
33197
33210
  const contentZone = {
33198
33211
  ...tableZone,
33199
- top: tableZone.top + 1
33212
+ top: tableZone.top + table.config.numberOfHeaders
33200
33213
  };
33201
33214
  const sortAnchor = {
33202
33215
  col: filterPosition.col,
@@ -47299,6 +47312,7 @@ stores.inject(MyMetaStore, storeInstance);
47299
47312
  .o-spreadsheet {
47300
47313
  .os-input {
47301
47314
  border-width: 0 0 1px 0;
47315
+ border-style: solid;
47302
47316
  border-color: transparent;
47303
47317
  outline: none;
47304
47318
  text-overflow: ellipsis;
@@ -74835,6 +74849,7 @@ stores.inject(MyMetaStore, storeInstance);
74835
74849
  width: 100%;
74836
74850
  outline: none;
74837
74851
  border-color: ${GRAY_300};
74852
+ border-style: solid;
74838
74853
  color: ${GRAY_900};
74839
74854
 
74840
74855
  &::placeholder {
@@ -79492,8 +79507,8 @@ exports.stores = stores;
79492
79507
  exports.tokenColors = tokenColors;
79493
79508
  exports.tokenize = tokenize;
79494
79509
 
79495
- __info__.version = "19.0.46";
79496
- __info__.date = "2026-08-10T12:30:49.160Z";
79497
- __info__.hash = "b721d1f";
79510
+ __info__.version = "19.0.47";
79511
+ __info__.date = "2026-08-21T15:28:59.913Z";
79512
+ __info__.hash = "59a9b1b";
79498
79513
 
79499
79514
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);