@odoo/o-spreadsheet 18.4.51 → 18.4.53

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.51
6
- * @date 2026-08-10T12:29:38.748Z
7
- * @hash fd60cf8
5
+ * @version 18.4.53
6
+ * @date 2026-08-29T09:14:02.893Z
7
+ * @hash 8ca564d
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 18.4.51
6
- * @date 2026-08-10T12:29:37.148Z
7
- * @hash fd60cf8
5
+ * @version 18.4.53
6
+ * @date 2026-08-29T09:14:01.099Z
7
+ * @hash 8ca564d
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";
@@ -2848,6 +2848,18 @@ function toString(data) {
2848
2848
  default: return "";
2849
2849
  }
2850
2850
  }
2851
+ /**
2852
+ * Only converts the decimal separator, ignore number format such as % or dates
2853
+ */
2854
+ function toLocaleString(data, locale) {
2855
+ const value = toValue(data);
2856
+ switch (typeof value) {
2857
+ case "string": return value;
2858
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
2859
+ case "boolean": return value ? "TRUE" : "FALSE";
2860
+ default: return "";
2861
+ }
2862
+ }
2851
2863
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2852
2864
  const normalizeString = memoize(function normalizeString(str) {
2853
2865
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -15988,7 +16000,7 @@ const CONCAT = {
15988
16000
  description: _t("Concatenation of two values."),
15989
16001
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
15990
16002
  compute: function(value1, value2) {
15991
- return toString(value1) + toString(value2);
16003
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
15992
16004
  },
15993
16005
  isExported: true
15994
16006
  };
@@ -16799,7 +16811,7 @@ const CLEAN = {
16799
16811
  description: _t("Remove non-printable characters from a piece of text."),
16800
16812
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
16801
16813
  compute: function(text) {
16802
- const _text = toString(text);
16814
+ const _text = toLocaleString(text, this.locale);
16803
16815
  let cleanedStr = "";
16804
16816
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
16805
16817
  return cleanedStr;
@@ -16810,7 +16822,7 @@ const CONCATENATE = {
16810
16822
  description: _t("Appends strings to one another."),
16811
16823
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
16812
16824
  compute: function(...datas) {
16813
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
16825
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
16814
16826
  },
16815
16827
  isExported: true
16816
16828
  };
@@ -16818,7 +16830,7 @@ const EXACT = {
16818
16830
  description: _t("Tests whether two strings are identical."),
16819
16831
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
16820
16832
  compute: function(string1, string2) {
16821
- return toString(string1) === toString(string2);
16833
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
16822
16834
  },
16823
16835
  isExported: true
16824
16836
  };
@@ -16830,8 +16842,8 @@ const FIND = {
16830
16842
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16831
16843
  ],
16832
16844
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16833
- const _searchFor = toString(searchFor);
16834
- const _textToSearch = toString(textToSearch);
16845
+ const _searchFor = toLocaleString(searchFor, this.locale);
16846
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16835
16847
  const _startingAt = toNumber(startingAt, this.locale);
16836
16848
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
16837
16849
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -16849,8 +16861,8 @@ const JOIN = {
16849
16861
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
16850
16862
  ],
16851
16863
  compute: function(delimiter, ...valuesOrArrays) {
16852
- const _delimiter = toString(delimiter);
16853
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
16864
+ const _delimiter = toLocaleString(delimiter, this.locale);
16865
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
16854
16866
  }
16855
16867
  };
16856
16868
  const LEFT = {
@@ -16859,7 +16871,7 @@ const LEFT = {
16859
16871
  compute: function(text, ...args) {
16860
16872
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16861
16873
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
16862
- return toString(text).substring(0, _numberOfCharacters);
16874
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
16863
16875
  },
16864
16876
  isExported: true
16865
16877
  };
@@ -16867,7 +16879,7 @@ const LEN = {
16867
16879
  description: _t("Length of a string."),
16868
16880
  args: [arg("text (string)", _t("The string whose length will be returned."))],
16869
16881
  compute: function(text) {
16870
- return toString(text).length;
16882
+ return toLocaleString(text, this.locale).length;
16871
16883
  },
16872
16884
  isExported: true
16873
16885
  };
@@ -16875,7 +16887,7 @@ const LOWER = {
16875
16887
  description: _t("Converts a specified string to lowercase."),
16876
16888
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
16877
16889
  compute: function(text) {
16878
- return toString(text).toLowerCase();
16890
+ return toLocaleString(text, this.locale).toLowerCase();
16879
16891
  },
16880
16892
  isExported: true
16881
16893
  };
@@ -16887,7 +16899,7 @@ const MID = {
16887
16899
  arg("extract_length (number)", _t("The length of the segment to extract."))
16888
16900
  ],
16889
16901
  compute: function(text, starting_at, extract_length) {
16890
- const _text = toString(text);
16902
+ const _text = toLocaleString(text, this.locale);
16891
16903
  const _starting_at = toNumber(starting_at, this.locale);
16892
16904
  const _extract_length = toNumber(extract_length, this.locale);
16893
16905
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -16900,7 +16912,7 @@ const PROPER = {
16900
16912
  description: _t("Capitalizes each word in a specified string."),
16901
16913
  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."))],
16902
16914
  compute: function(text) {
16903
- return toString(text).replace(wordRegex, (word) => {
16915
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
16904
16916
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
16905
16917
  });
16906
16918
  },
@@ -16917,9 +16929,9 @@ const REPLACE = {
16917
16929
  compute: function(text, position, length, newText) {
16918
16930
  const _position = toNumber(position, this.locale);
16919
16931
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
16920
- const _text = toString(text);
16932
+ const _text = toLocaleString(text, this.locale);
16921
16933
  const _length = toNumber(length, this.locale);
16922
- const _newText = toString(newText);
16934
+ const _newText = toLocaleString(newText, this.locale);
16923
16935
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
16924
16936
  },
16925
16937
  isExported: true
@@ -16930,7 +16942,7 @@ const RIGHT = {
16930
16942
  compute: function(text, ...args) {
16931
16943
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16932
16944
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
16933
- const _text = toString(text);
16945
+ const _text = toLocaleString(text, this.locale);
16934
16946
  const stringLength = _text.length;
16935
16947
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
16936
16948
  },
@@ -16944,8 +16956,8 @@ const SEARCH = {
16944
16956
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16945
16957
  ],
16946
16958
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16947
- const _searchFor = toString(searchFor).toLowerCase();
16948
- const _textToSearch = toString(textToSearch).toLowerCase();
16959
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
16960
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
16949
16961
  const _startingAt = toNumber(startingAt, this.locale);
16950
16962
  if (_textToSearch === "") return {
16951
16963
  value: CellErrorType.GenericError,
@@ -16975,8 +16987,8 @@ const SPLIT = {
16975
16987
  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."))
16976
16988
  ],
16977
16989
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
16978
- const _text = toString(text);
16979
- const _delimiter = escapeRegExp(toString(delimiter));
16990
+ const _text = toLocaleString(text, this.locale);
16991
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
16980
16992
  const _splitByEach = toBoolean(splitByEach);
16981
16993
  const _removeEmptyText = toBoolean(removeEmptyText);
16982
16994
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -16998,10 +17010,10 @@ const SUBSTITUTE = {
16998
17010
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
16999
17011
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
17000
17012
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
17001
- const _textToSearch = toString(textToSearch);
17002
- const _searchFor = toString(searchFor);
17013
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
17014
+ const _searchFor = toLocaleString(searchFor, this.locale);
17003
17015
  if (_searchFor === "") return _textToSearch;
17004
- const _replaceWith = toString(replaceWith);
17016
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
17005
17017
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
17006
17018
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
17007
17019
  let n = 0;
@@ -17018,10 +17030,10 @@ const TEXTJOIN = {
17018
17030
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
17019
17031
  ],
17020
17032
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
17021
- const _delimiter = toString(delimiter);
17033
+ const _delimiter = toLocaleString(delimiter, this.locale);
17022
17034
  const _ignoreEmpty = toBoolean(ignoreEmpty);
17023
17035
  let n = 0;
17024
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
17036
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
17025
17037
  },
17026
17038
  isExported: true
17027
17039
  };
@@ -17068,7 +17080,7 @@ const TRIM = {
17068
17080
  description: _t("Removes space characters."),
17069
17081
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
17070
17082
  compute: function(text) {
17071
- return trimContent(toString(text));
17083
+ return trimContent(toLocaleString(text, this.locale));
17072
17084
  },
17073
17085
  isExported: true
17074
17086
  };
@@ -17076,7 +17088,7 @@ const UPPER = {
17076
17088
  description: _t("Converts a specified string to uppercase."),
17077
17089
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
17078
17090
  compute: function(text) {
17079
- return toString(text).toUpperCase();
17091
+ return toLocaleString(text, this.locale).toUpperCase();
17080
17092
  },
17081
17093
  isExported: true
17082
17094
  };
@@ -17108,7 +17120,7 @@ const HYPERLINK = {
17108
17120
  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."))],
17109
17121
  compute: function(url, linkLabel) {
17110
17122
  const processedUrl = toString(url).trim();
17111
- const processedLabel = toString(linkLabel) || processedUrl;
17123
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
17112
17124
  if (processedUrl === "") return processedLabel;
17113
17125
  return markdownLink(processedLabel, processedUrl);
17114
17126
  },
@@ -18313,6 +18325,7 @@ function getRadarChartDatasets(definition, args) {
18313
18325
  hidden,
18314
18326
  borderColor,
18315
18327
  backgroundColor: borderColor,
18328
+ pointBackgroundColor: borderColor,
18316
18329
  pointRadius: definition.hideDataMarkers ? 0 : 3
18317
18330
  };
18318
18331
  if (fill) {
@@ -31292,12 +31305,13 @@ var FilterMenu = class extends Component {
31292
31305
  }
31293
31306
  sortFilterZone(sortDirection) {
31294
31307
  const filterPosition = this.props.filterPosition;
31295
- const tableZone = this.table?.range.zone;
31308
+ const table = this.table;
31309
+ const tableZone = table?.range.zone;
31296
31310
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
31297
31311
  const sheetId = this.env.model.getters.getActiveSheetId();
31298
31312
  const contentZone = {
31299
31313
  ...tableZone,
31300
- top: tableZone.top + 1
31314
+ top: tableZone.top + table.config.numberOfHeaders
31301
31315
  };
31302
31316
  const sortAnchor = {
31303
31317
  col: filterPosition.col,
@@ -44312,6 +44326,12 @@ css`
44312
44326
  background-color: ${ACTION_COLOR};
44313
44327
  border-color: ${ACTION_COLOR};
44314
44328
  }
44329
+
44330
+ &:focus {
44331
+ outline: none;
44332
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
44333
+ border-color: ${ACTION_COLOR};
44334
+ }
44315
44335
  }
44316
44336
  }
44317
44337
  `;
@@ -47968,6 +47988,7 @@ css`
47968
47988
  .o-spreadsheet {
47969
47989
  .os-input {
47970
47990
  border-width: 0 0 1px 0;
47991
+ border-style: solid;
47971
47992
  border-color: transparent;
47972
47993
  outline: none;
47973
47994
  text-overflow: ellipsis;
@@ -69017,6 +69038,9 @@ css`
69017
69038
  position: absolute;
69018
69039
  cursor: pointer;
69019
69040
  }
69041
+ .o-grid.o-dashboard-grid {
69042
+ background: #ffffff;
69043
+ }
69020
69044
  `;
69021
69045
  var SpreadsheetDashboard = class extends Component {
69022
69046
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -70947,6 +70971,7 @@ css`
70947
70971
  width: 100%;
70948
70972
  outline: none;
70949
70973
  border-color: ${GRAY_300};
70974
+ border-style: solid;
70950
70975
  color: ${GRAY_900};
70951
70976
 
70952
70977
  &::placeholder {
@@ -75421,6 +75446,6 @@ const chartHelpers = {
75421
75446
  //#endregion
75422
75447
  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, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75423
75448
 
75424
- __info__.version = "18.4.51";
75425
- __info__.date = "2026-08-10T12:29:37.148Z";
75426
- __info__.hash = "fd60cf8";
75449
+ __info__.version = "18.4.53";
75450
+ __info__.date = "2026-08-29T09:14:01.099Z";
75451
+ __info__.hash = "8ca564d";
@@ -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.51
6
- * @date 2026-08-10T12:29:37.148Z
7
- * @hash fd60cf8
5
+ * @version 18.4.53
6
+ * @date 2026-08-29T09:14:01.099Z
7
+ * @hash 8ca564d
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, "");
@@ -15990,7 +16002,7 @@ stores.inject(MyMetaStore, storeInstance);
15990
16002
  description: _t("Concatenation of two values."),
15991
16003
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
15992
16004
  compute: function(value1, value2) {
15993
- return toString(value1) + toString(value2);
16005
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
15994
16006
  },
15995
16007
  isExported: true
15996
16008
  };
@@ -16801,7 +16813,7 @@ stores.inject(MyMetaStore, storeInstance);
16801
16813
  description: _t("Remove non-printable characters from a piece of text."),
16802
16814
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
16803
16815
  compute: function(text) {
16804
- const _text = toString(text);
16816
+ const _text = toLocaleString(text, this.locale);
16805
16817
  let cleanedStr = "";
16806
16818
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
16807
16819
  return cleanedStr;
@@ -16812,7 +16824,7 @@ stores.inject(MyMetaStore, storeInstance);
16812
16824
  description: _t("Appends strings to one another."),
16813
16825
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
16814
16826
  compute: function(...datas) {
16815
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
16827
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
16816
16828
  },
16817
16829
  isExported: true
16818
16830
  };
@@ -16820,7 +16832,7 @@ stores.inject(MyMetaStore, storeInstance);
16820
16832
  description: _t("Tests whether two strings are identical."),
16821
16833
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
16822
16834
  compute: function(string1, string2) {
16823
- return toString(string1) === toString(string2);
16835
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
16824
16836
  },
16825
16837
  isExported: true
16826
16838
  };
@@ -16832,8 +16844,8 @@ stores.inject(MyMetaStore, storeInstance);
16832
16844
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16833
16845
  ],
16834
16846
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16835
- const _searchFor = toString(searchFor);
16836
- const _textToSearch = toString(textToSearch);
16847
+ const _searchFor = toLocaleString(searchFor, this.locale);
16848
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16837
16849
  const _startingAt = toNumber(startingAt, this.locale);
16838
16850
  if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
16839
16851
  if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
@@ -16851,8 +16863,8 @@ stores.inject(MyMetaStore, storeInstance);
16851
16863
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
16852
16864
  ],
16853
16865
  compute: function(delimiter, ...valuesOrArrays) {
16854
- const _delimiter = toString(delimiter);
16855
- 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), "");
16856
16868
  }
16857
16869
  };
16858
16870
  const LEFT = {
@@ -16861,7 +16873,7 @@ stores.inject(MyMetaStore, storeInstance);
16861
16873
  compute: function(text, ...args) {
16862
16874
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16863
16875
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
16864
- return toString(text).substring(0, _numberOfCharacters);
16876
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
16865
16877
  },
16866
16878
  isExported: true
16867
16879
  };
@@ -16869,7 +16881,7 @@ stores.inject(MyMetaStore, storeInstance);
16869
16881
  description: _t("Length of a string."),
16870
16882
  args: [arg("text (string)", _t("The string whose length will be returned."))],
16871
16883
  compute: function(text) {
16872
- return toString(text).length;
16884
+ return toLocaleString(text, this.locale).length;
16873
16885
  },
16874
16886
  isExported: true
16875
16887
  };
@@ -16877,7 +16889,7 @@ stores.inject(MyMetaStore, storeInstance);
16877
16889
  description: _t("Converts a specified string to lowercase."),
16878
16890
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
16879
16891
  compute: function(text) {
16880
- return toString(text).toLowerCase();
16892
+ return toLocaleString(text, this.locale).toLowerCase();
16881
16893
  },
16882
16894
  isExported: true
16883
16895
  };
@@ -16889,7 +16901,7 @@ stores.inject(MyMetaStore, storeInstance);
16889
16901
  arg("extract_length (number)", _t("The length of the segment to extract."))
16890
16902
  ],
16891
16903
  compute: function(text, starting_at, extract_length) {
16892
- const _text = toString(text);
16904
+ const _text = toLocaleString(text, this.locale);
16893
16905
  const _starting_at = toNumber(starting_at, this.locale);
16894
16906
  const _extract_length = toNumber(extract_length, this.locale);
16895
16907
  if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -16902,7 +16914,7 @@ stores.inject(MyMetaStore, storeInstance);
16902
16914
  description: _t("Capitalizes each word in a specified string."),
16903
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."))],
16904
16916
  compute: function(text) {
16905
- return toString(text).replace(wordRegex, (word) => {
16917
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
16906
16918
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
16907
16919
  });
16908
16920
  },
@@ -16919,9 +16931,9 @@ stores.inject(MyMetaStore, storeInstance);
16919
16931
  compute: function(text, position, length, newText) {
16920
16932
  const _position = toNumber(position, this.locale);
16921
16933
  if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
16922
- const _text = toString(text);
16934
+ const _text = toLocaleString(text, this.locale);
16923
16935
  const _length = toNumber(length, this.locale);
16924
- const _newText = toString(newText);
16936
+ const _newText = toLocaleString(newText, this.locale);
16925
16937
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
16926
16938
  },
16927
16939
  isExported: true
@@ -16932,7 +16944,7 @@ stores.inject(MyMetaStore, storeInstance);
16932
16944
  compute: function(text, ...args) {
16933
16945
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16934
16946
  if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
16935
- const _text = toString(text);
16947
+ const _text = toLocaleString(text, this.locale);
16936
16948
  const stringLength = _text.length;
16937
16949
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
16938
16950
  },
@@ -16946,8 +16958,8 @@ stores.inject(MyMetaStore, storeInstance);
16946
16958
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16947
16959
  ],
16948
16960
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16949
- const _searchFor = toString(searchFor).toLowerCase();
16950
- const _textToSearch = toString(textToSearch).toLowerCase();
16961
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
16962
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
16951
16963
  const _startingAt = toNumber(startingAt, this.locale);
16952
16964
  if (_textToSearch === "") return {
16953
16965
  value: CellErrorType.GenericError,
@@ -16977,8 +16989,8 @@ stores.inject(MyMetaStore, storeInstance);
16977
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."))
16978
16990
  ],
16979
16991
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
16980
- const _text = toString(text);
16981
- const _delimiter = escapeRegExp(toString(delimiter));
16992
+ const _text = toLocaleString(text, this.locale);
16993
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
16982
16994
  const _splitByEach = toBoolean(splitByEach);
16983
16995
  const _removeEmptyText = toBoolean(removeEmptyText);
16984
16996
  if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
@@ -17000,10 +17012,10 @@ stores.inject(MyMetaStore, storeInstance);
17000
17012
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
17001
17013
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
17002
17014
  if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
17003
- const _textToSearch = toString(textToSearch);
17004
- const _searchFor = toString(searchFor);
17015
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
17016
+ const _searchFor = toLocaleString(searchFor, this.locale);
17005
17017
  if (_searchFor === "") return _textToSearch;
17006
- const _replaceWith = toString(replaceWith);
17018
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
17007
17019
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
17008
17020
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
17009
17021
  let n = 0;
@@ -17020,10 +17032,10 @@ stores.inject(MyMetaStore, storeInstance);
17020
17032
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
17021
17033
  ],
17022
17034
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
17023
- const _delimiter = toString(delimiter);
17035
+ const _delimiter = toLocaleString(delimiter, this.locale);
17024
17036
  const _ignoreEmpty = toBoolean(ignoreEmpty);
17025
17037
  let n = 0;
17026
- 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, "");
17027
17039
  },
17028
17040
  isExported: true
17029
17041
  };
@@ -17070,7 +17082,7 @@ stores.inject(MyMetaStore, storeInstance);
17070
17082
  description: _t("Removes space characters."),
17071
17083
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
17072
17084
  compute: function(text) {
17073
- return trimContent(toString(text));
17085
+ return trimContent(toLocaleString(text, this.locale));
17074
17086
  },
17075
17087
  isExported: true
17076
17088
  };
@@ -17078,7 +17090,7 @@ stores.inject(MyMetaStore, storeInstance);
17078
17090
  description: _t("Converts a specified string to uppercase."),
17079
17091
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
17080
17092
  compute: function(text) {
17081
- return toString(text).toUpperCase();
17093
+ return toLocaleString(text, this.locale).toUpperCase();
17082
17094
  },
17083
17095
  isExported: true
17084
17096
  };
@@ -17110,7 +17122,7 @@ stores.inject(MyMetaStore, storeInstance);
17110
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."))],
17111
17123
  compute: function(url, linkLabel) {
17112
17124
  const processedUrl = toString(url).trim();
17113
- const processedLabel = toString(linkLabel) || processedUrl;
17125
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
17114
17126
  if (processedUrl === "") return processedLabel;
17115
17127
  return markdownLink(processedLabel, processedUrl);
17116
17128
  },
@@ -18315,6 +18327,7 @@ stores.inject(MyMetaStore, storeInstance);
18315
18327
  hidden,
18316
18328
  borderColor,
18317
18329
  backgroundColor: borderColor,
18330
+ pointBackgroundColor: borderColor,
18318
18331
  pointRadius: definition.hideDataMarkers ? 0 : 3
18319
18332
  };
18320
18333
  if (fill) {
@@ -31294,12 +31307,13 @@ stores.inject(MyMetaStore, storeInstance);
31294
31307
  }
31295
31308
  sortFilterZone(sortDirection) {
31296
31309
  const filterPosition = this.props.filterPosition;
31297
- const tableZone = this.table?.range.zone;
31310
+ const table = this.table;
31311
+ const tableZone = table?.range.zone;
31298
31312
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
31299
31313
  const sheetId = this.env.model.getters.getActiveSheetId();
31300
31314
  const contentZone = {
31301
31315
  ...tableZone,
31302
- top: tableZone.top + 1
31316
+ top: tableZone.top + table.config.numberOfHeaders
31303
31317
  };
31304
31318
  const sortAnchor = {
31305
31319
  col: filterPosition.col,
@@ -44314,6 +44328,12 @@ stores.inject(MyMetaStore, storeInstance);
44314
44328
  background-color: ${ACTION_COLOR};
44315
44329
  border-color: ${ACTION_COLOR};
44316
44330
  }
44331
+
44332
+ &:focus {
44333
+ outline: none;
44334
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
44335
+ border-color: ${ACTION_COLOR};
44336
+ }
44317
44337
  }
44318
44338
  }
44319
44339
  `;
@@ -47970,6 +47990,7 @@ stores.inject(MyMetaStore, storeInstance);
47970
47990
  .o-spreadsheet {
47971
47991
  .os-input {
47972
47992
  border-width: 0 0 1px 0;
47993
+ border-style: solid;
47973
47994
  border-color: transparent;
47974
47995
  outline: none;
47975
47996
  text-overflow: ellipsis;
@@ -69019,6 +69040,9 @@ stores.inject(MyMetaStore, storeInstance);
69019
69040
  position: absolute;
69020
69041
  cursor: pointer;
69021
69042
  }
69043
+ .o-grid.o-dashboard-grid {
69044
+ background: #ffffff;
69045
+ }
69022
69046
  `;
69023
69047
  var SpreadsheetDashboard = class extends _odoo_owl.Component {
69024
69048
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -70949,6 +70973,7 @@ stores.inject(MyMetaStore, storeInstance);
70949
70973
  width: 100%;
70950
70974
  outline: none;
70951
70975
  border-color: ${GRAY_300};
70976
+ border-style: solid;
70952
70977
  color: ${GRAY_900};
70953
70978
 
70954
70979
  &::placeholder {
@@ -75472,8 +75497,8 @@ exports.stores = stores;
75472
75497
  exports.tokenColors = tokenColors;
75473
75498
  exports.tokenize = tokenize;
75474
75499
 
75475
- __info__.version = "18.4.51";
75476
- __info__.date = "2026-08-10T12:29:37.148Z";
75477
- __info__.hash = "fd60cf8";
75500
+ __info__.version = "18.4.53";
75501
+ __info__.date = "2026-08-29T09:14:01.099Z";
75502
+ __info__.hash = "8ca564d";
75478
75503
 
75479
75504
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);