@odoo/o-spreadsheet 19.0.46 → 19.0.48

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.48
6
+ * @date 2026-08-29T09:14:14.219Z
7
+ * @hash 0a6c168
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.48
6
+ * @date 2026-08-29T09:14:12.472Z
7
+ * @hash 0a6c168
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
  },
@@ -19705,6 +19717,7 @@ function getRadarChartDatasets(definition, args) {
19705
19717
  hidden,
19706
19718
  borderColor,
19707
19719
  backgroundColor: borderColor,
19720
+ pointBackgroundColor: borderColor,
19708
19721
  pointRadius: definition.hideDataMarkers ? 0 : 3
19709
19722
  };
19710
19723
  if (fill) {
@@ -33189,12 +33202,13 @@ var FilterMenu = class extends Component {
33189
33202
  }
33190
33203
  sortFilterZone(sortDirection) {
33191
33204
  const filterPosition = this.props.filterPosition;
33192
- const tableZone = this.table?.range.zone;
33205
+ const table = this.table;
33206
+ const tableZone = table?.range.zone;
33193
33207
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
33194
33208
  const sheetId = this.env.model.getters.getActiveSheetId();
33195
33209
  const contentZone = {
33196
33210
  ...tableZone,
33197
- top: tableZone.top + 1
33211
+ top: tableZone.top + table.config.numberOfHeaders
33198
33212
  };
33199
33213
  const sortAnchor = {
33200
33214
  col: filterPosition.col,
@@ -47297,6 +47311,7 @@ css`
47297
47311
  .o-spreadsheet {
47298
47312
  .os-input {
47299
47313
  border-width: 0 0 1px 0;
47314
+ border-style: solid;
47300
47315
  border-color: transparent;
47301
47316
  outline: none;
47302
47317
  text-overflow: ellipsis;
@@ -48649,6 +48664,12 @@ css`
48649
48664
  background-color: ${ACTION_COLOR};
48650
48665
  border-color: ${ACTION_COLOR};
48651
48666
  }
48667
+
48668
+ &:focus {
48669
+ outline: none;
48670
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
48671
+ border-color: ${ACTION_COLOR};
48672
+ }
48652
48673
  }
48653
48674
  }
48654
48675
  `;
@@ -72900,6 +72921,9 @@ css`
72900
72921
  position: absolute;
72901
72922
  cursor: pointer;
72902
72923
  }
72924
+ .o-grid.o-dashboard-grid {
72925
+ background: #ffffff;
72926
+ }
72903
72927
  `;
72904
72928
  var SpreadsheetDashboard = class extends Component {
72905
72929
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -74833,6 +74857,7 @@ css`
74833
74857
  width: 100%;
74834
74858
  outline: none;
74835
74859
  border-color: ${GRAY_300};
74860
+ border-style: solid;
74836
74861
  color: ${GRAY_900};
74837
74862
 
74838
74863
  &::placeholder {
@@ -79439,6 +79464,6 @@ const chartHelpers = {
79439
79464
  //#endregion
79440
79465
  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
79466
 
79442
- __info__.version = "19.0.46";
79443
- __info__.date = "2026-08-10T12:30:49.160Z";
79444
- __info__.hash = "b721d1f";
79467
+ __info__.version = "19.0.48";
79468
+ __info__.date = "2026-08-29T09:14:12.472Z";
79469
+ __info__.hash = "0a6c168";
@@ -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.48
6
+ * @date 2026-08-29T09:14:12.472Z
7
+ * @hash 0a6c168
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
  },
@@ -19707,6 +19719,7 @@ stores.inject(MyMetaStore, storeInstance);
19707
19719
  hidden,
19708
19720
  borderColor,
19709
19721
  backgroundColor: borderColor,
19722
+ pointBackgroundColor: borderColor,
19710
19723
  pointRadius: definition.hideDataMarkers ? 0 : 3
19711
19724
  };
19712
19725
  if (fill) {
@@ -33191,12 +33204,13 @@ stores.inject(MyMetaStore, storeInstance);
33191
33204
  }
33192
33205
  sortFilterZone(sortDirection) {
33193
33206
  const filterPosition = this.props.filterPosition;
33194
- const tableZone = this.table?.range.zone;
33207
+ const table = this.table;
33208
+ const tableZone = table?.range.zone;
33195
33209
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
33196
33210
  const sheetId = this.env.model.getters.getActiveSheetId();
33197
33211
  const contentZone = {
33198
33212
  ...tableZone,
33199
- top: tableZone.top + 1
33213
+ top: tableZone.top + table.config.numberOfHeaders
33200
33214
  };
33201
33215
  const sortAnchor = {
33202
33216
  col: filterPosition.col,
@@ -47299,6 +47313,7 @@ stores.inject(MyMetaStore, storeInstance);
47299
47313
  .o-spreadsheet {
47300
47314
  .os-input {
47301
47315
  border-width: 0 0 1px 0;
47316
+ border-style: solid;
47302
47317
  border-color: transparent;
47303
47318
  outline: none;
47304
47319
  text-overflow: ellipsis;
@@ -48651,6 +48666,12 @@ stores.inject(MyMetaStore, storeInstance);
48651
48666
  background-color: ${ACTION_COLOR};
48652
48667
  border-color: ${ACTION_COLOR};
48653
48668
  }
48669
+
48670
+ &:focus {
48671
+ outline: none;
48672
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
48673
+ border-color: ${ACTION_COLOR};
48674
+ }
48654
48675
  }
48655
48676
  }
48656
48677
  `;
@@ -72902,6 +72923,9 @@ stores.inject(MyMetaStore, storeInstance);
72902
72923
  position: absolute;
72903
72924
  cursor: pointer;
72904
72925
  }
72926
+ .o-grid.o-dashboard-grid {
72927
+ background: #ffffff;
72928
+ }
72905
72929
  `;
72906
72930
  var SpreadsheetDashboard = class extends _odoo_owl.Component {
72907
72931
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -74835,6 +74859,7 @@ stores.inject(MyMetaStore, storeInstance);
74835
74859
  width: 100%;
74836
74860
  outline: none;
74837
74861
  border-color: ${GRAY_300};
74862
+ border-style: solid;
74838
74863
  color: ${GRAY_900};
74839
74864
 
74840
74865
  &::placeholder {
@@ -79492,8 +79517,8 @@ exports.stores = stores;
79492
79517
  exports.tokenColors = tokenColors;
79493
79518
  exports.tokenize = tokenize;
79494
79519
 
79495
- __info__.version = "19.0.46";
79496
- __info__.date = "2026-08-10T12:30:49.160Z";
79497
- __info__.hash = "b721d1f";
79520
+ __info__.version = "19.0.48";
79521
+ __info__.date = "2026-08-29T09:14:12.472Z";
79522
+ __info__.hash = "0a6c168";
79498
79523
 
79499
79524
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);