@odoo/o-spreadsheet 18.0.78 → 18.0.80

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.0.78
6
- * @date 2026-08-10T12:30:26.634Z
7
- * @hash 3fdaa53
5
+ * @version 18.0.80
6
+ * @date 2026-08-29T09:13:05.057Z
7
+ * @hash af324f5
8
8
  */
9
9
 
10
10
  import { Component, markRaw, onMounted, onPatched, onWillPatch, onWillStart, onWillUnmount, onWillUpdateProps, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, xml } from "@odoo/owl";
@@ -3272,6 +3272,18 @@ function toString(data) {
3272
3272
  default: return "";
3273
3273
  }
3274
3274
  }
3275
+ /**
3276
+ * Only converts the decimal separator, ignore number format such as % or dates
3277
+ */
3278
+ function toLocaleString(data, locale) {
3279
+ const value = toValue(data);
3280
+ switch (typeof value) {
3281
+ case "string": return value;
3282
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3283
+ case "boolean": return value ? "TRUE" : "FALSE";
3284
+ default: return "";
3285
+ }
3286
+ }
3275
3287
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3276
3288
  const normalizeString = memoize(function normalizeString(str) {
3277
3289
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -21713,7 +21725,7 @@ const CONCAT = {
21713
21725
  description: _t("Concatenation of two values."),
21714
21726
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
21715
21727
  compute: function(value1, value2) {
21716
- return toString(value1) + toString(value2);
21728
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
21717
21729
  },
21718
21730
  isExported: true
21719
21731
  };
@@ -22515,7 +22527,7 @@ const CLEAN = {
22515
22527
  description: _t("Remove non-printable characters from a piece of text."),
22516
22528
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
22517
22529
  compute: function(text) {
22518
- const _text = toString(text);
22530
+ const _text = toLocaleString(text, this.locale);
22519
22531
  let cleanedStr = "";
22520
22532
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
22521
22533
  return cleanedStr;
@@ -22526,7 +22538,7 @@ const CONCATENATE = {
22526
22538
  description: _t("Appends strings to one another."),
22527
22539
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
22528
22540
  compute: function(...datas) {
22529
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
22541
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
22530
22542
  },
22531
22543
  isExported: true
22532
22544
  };
@@ -22534,7 +22546,7 @@ const EXACT = {
22534
22546
  description: _t("Tests whether two strings are identical."),
22535
22547
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
22536
22548
  compute: function(string1, string2) {
22537
- return toString(string1) === toString(string2);
22549
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
22538
22550
  },
22539
22551
  isExported: true
22540
22552
  };
@@ -22546,13 +22558,13 @@ const FIND = {
22546
22558
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
22547
22559
  ],
22548
22560
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
22549
- const _searchFor = toString(searchFor);
22550
- const _textToSearch = toString(textToSearch);
22561
+ const _searchFor = toLocaleString(searchFor, this.locale);
22562
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
22551
22563
  const _startingAt = toNumber(startingAt, this.locale);
22552
22564
  assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
22553
22565
  assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
22554
22566
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
22555
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
22567
+ assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
22556
22568
  return result + 1;
22557
22569
  },
22558
22570
  isExported: true
@@ -22565,8 +22577,8 @@ const JOIN = {
22565
22577
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
22566
22578
  ],
22567
22579
  compute: function(delimiter, ...valuesOrArrays) {
22568
- const _delimiter = toString(delimiter);
22569
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
22580
+ const _delimiter = toLocaleString(delimiter, this.locale);
22581
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
22570
22582
  }
22571
22583
  };
22572
22584
  const LEFT = {
@@ -22575,7 +22587,7 @@ const LEFT = {
22575
22587
  compute: function(text, ...args) {
22576
22588
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
22577
22589
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
22578
- return toString(text).substring(0, _numberOfCharacters);
22590
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
22579
22591
  },
22580
22592
  isExported: true
22581
22593
  };
@@ -22583,7 +22595,7 @@ const LEN = {
22583
22595
  description: _t("Length of a string."),
22584
22596
  args: [arg("text (string)", _t("The string whose length will be returned."))],
22585
22597
  compute: function(text) {
22586
- return toString(text).length;
22598
+ return toLocaleString(text, this.locale).length;
22587
22599
  },
22588
22600
  isExported: true
22589
22601
  };
@@ -22591,7 +22603,7 @@ const LOWER = {
22591
22603
  description: _t("Converts a specified string to lowercase."),
22592
22604
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
22593
22605
  compute: function(text) {
22594
- return toString(text).toLowerCase();
22606
+ return toLocaleString(text, this.locale).toLowerCase();
22595
22607
  },
22596
22608
  isExported: true
22597
22609
  };
@@ -22603,7 +22615,7 @@ const MID = {
22603
22615
  arg("extract_length (number)", _t("The length of the segment to extract."))
22604
22616
  ],
22605
22617
  compute: function(text, starting_at, extract_length) {
22606
- const _text = toString(text);
22618
+ const _text = toLocaleString(text, this.locale);
22607
22619
  const _starting_at = toNumber(starting_at, this.locale);
22608
22620
  const _extract_length = toNumber(extract_length, this.locale);
22609
22621
  assert(() => _starting_at >= 1, _t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -22616,7 +22628,7 @@ const PROPER = {
22616
22628
  description: _t("Capitalizes each word in a specified string."),
22617
22629
  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."))],
22618
22630
  compute: function(text) {
22619
- return toString(text).replace(wordRegex, (word) => {
22631
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
22620
22632
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
22621
22633
  });
22622
22634
  },
@@ -22633,9 +22645,9 @@ const REPLACE = {
22633
22645
  compute: function(text, position, length, newText) {
22634
22646
  const _position = toNumber(position, this.locale);
22635
22647
  assert(() => _position >= 1, _t("The position (%s) must be greater than or equal to 1.", _position.toString()));
22636
- const _text = toString(text);
22648
+ const _text = toLocaleString(text, this.locale);
22637
22649
  const _length = toNumber(length, this.locale);
22638
- const _newText = toString(newText);
22650
+ const _newText = toLocaleString(newText, this.locale);
22639
22651
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
22640
22652
  },
22641
22653
  isExported: true
@@ -22646,7 +22658,7 @@ const RIGHT = {
22646
22658
  compute: function(text, ...args) {
22647
22659
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
22648
22660
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
22649
- const _text = toString(text);
22661
+ const _text = toLocaleString(text, this.locale);
22650
22662
  const stringLength = _text.length;
22651
22663
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
22652
22664
  },
@@ -22660,8 +22672,8 @@ const SEARCH = {
22660
22672
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
22661
22673
  ],
22662
22674
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
22663
- const _searchFor = toString(searchFor).toLowerCase();
22664
- const _textToSearch = toString(textToSearch).toLowerCase();
22675
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
22676
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
22665
22677
  const _startingAt = toNumber(startingAt, this.locale);
22666
22678
  if (_textToSearch === "") return {
22667
22679
  value: CellErrorType.GenericError,
@@ -22691,8 +22703,8 @@ const SPLIT = {
22691
22703
  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."))
22692
22704
  ],
22693
22705
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
22694
- const _text = toString(text);
22695
- const _delimiter = escapeRegExp(toString(delimiter));
22706
+ const _text = toLocaleString(text, this.locale);
22707
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
22696
22708
  const _splitByEach = toBoolean(splitByEach);
22697
22709
  const _removeEmptyText = toBoolean(removeEmptyText);
22698
22710
  assert(() => _delimiter.length > 0, _t("The _delimiter (%s) must be not be empty.", _delimiter));
@@ -22714,10 +22726,10 @@ const SUBSTITUTE = {
22714
22726
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
22715
22727
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
22716
22728
  assert(() => _occurrenceNumber >= 0, _t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber.toString()));
22717
- const _textToSearch = toString(textToSearch);
22718
- const _searchFor = toString(searchFor);
22729
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
22730
+ const _searchFor = toLocaleString(searchFor, this.locale);
22719
22731
  if (_searchFor === "") return _textToSearch;
22720
- const _replaceWith = toString(replaceWith);
22732
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
22721
22733
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
22722
22734
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
22723
22735
  let n = 0;
@@ -22734,10 +22746,10 @@ const TEXTJOIN = {
22734
22746
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
22735
22747
  ],
22736
22748
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
22737
- const _delimiter = toString(delimiter);
22749
+ const _delimiter = toLocaleString(delimiter, this.locale);
22738
22750
  const _ignoreEmpty = toBoolean(ignoreEmpty);
22739
22751
  let n = 0;
22740
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
22752
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
22741
22753
  },
22742
22754
  isExported: true
22743
22755
  };
@@ -22745,7 +22757,7 @@ const TRIM = {
22745
22757
  description: _t("Removes space characters."),
22746
22758
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
22747
22759
  compute: function(text) {
22748
- return trimContent(toString(text));
22760
+ return trimContent(toLocaleString(text, this.locale));
22749
22761
  },
22750
22762
  isExported: true
22751
22763
  };
@@ -22753,7 +22765,7 @@ const UPPER = {
22753
22765
  description: _t("Converts a specified string to uppercase."),
22754
22766
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
22755
22767
  compute: function(text) {
22756
- return toString(text).toUpperCase();
22768
+ return toLocaleString(text, this.locale).toUpperCase();
22757
22769
  },
22758
22770
  isExported: true
22759
22771
  };
@@ -22785,7 +22797,7 @@ const HYPERLINK = {
22785
22797
  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."))],
22786
22798
  compute: function(url, linkLabel) {
22787
22799
  const processedUrl = toString(url).trim();
22788
- const processedLabel = toString(linkLabel) || processedUrl;
22800
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
22789
22801
  if (processedUrl === "") return processedLabel;
22790
22802
  return markdownLink(processedLabel, processedUrl);
22791
22803
  },
@@ -27971,12 +27983,13 @@ var FilterMenu = class extends Component {
27971
27983
  }
27972
27984
  sortFilterZone(sortDirection) {
27973
27985
  const filterPosition = this.props.filterPosition;
27974
- const tableZone = this.table?.range.zone;
27986
+ const table = this.table;
27987
+ const tableZone = table?.range.zone;
27975
27988
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
27976
27989
  const sheetId = this.env.model.getters.getActiveSheetId();
27977
27990
  const contentZone = {
27978
27991
  ...tableZone,
27979
- top: tableZone.top + 1
27992
+ top: tableZone.top + table.config.numberOfHeaders
27980
27993
  };
27981
27994
  this.env.model.dispatch("SORT_CELLS", {
27982
27995
  sheetId,
@@ -32559,6 +32572,12 @@ css`
32559
32572
  background-color: ${ACTION_COLOR};
32560
32573
  border-color: ${ACTION_COLOR};
32561
32574
  }
32575
+
32576
+ &:focus {
32577
+ outline: none;
32578
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
32579
+ border-color: ${ACTION_COLOR};
32580
+ }
32562
32581
  }
32563
32582
  }
32564
32583
  `;
@@ -38090,6 +38109,7 @@ css`
38090
38109
  .os-input {
38091
38110
  box-sizing: border-box;
38092
38111
  border-width: 0 0 1px 0;
38112
+ border-style: solid;
38093
38113
  border-color: transparent;
38094
38114
  outline: none;
38095
38115
  text-overflow: ellipsis;
@@ -60651,6 +60671,9 @@ css`
60651
60671
  position: absolute;
60652
60672
  cursor: pointer;
60653
60673
  }
60674
+ .o-grid.o-dashboard-grid {
60675
+ background: #ffffff;
60676
+ }
60654
60677
  `;
60655
60678
  var SpreadsheetDashboard = class extends Component {
60656
60679
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -62063,6 +62086,7 @@ css`
62063
62086
  width: 100%;
62064
62087
  outline: none;
62065
62088
  border-color: ${GRAY_300};
62089
+ border-style: solid;
62066
62090
  color: ${GRAY_900};
62067
62091
 
62068
62092
  &::placeholder {
@@ -66388,6 +66412,6 @@ const constants = {
66388
66412
  //#endregion
66389
66413
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
66390
66414
 
66391
- __info__.version = "18.0.78";
66392
- __info__.date = "2026-08-10T12:30:26.634Z";
66393
- __info__.hash = "3fdaa53";
66415
+ __info__.version = "18.0.80";
66416
+ __info__.date = "2026-08-29T09:13:05.057Z";
66417
+ __info__.hash = "af324f5";
@@ -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.0.78
6
- * @date 2026-08-10T12:30:26.634Z
7
- * @hash 3fdaa53
5
+ * @version 18.0.80
6
+ * @date 2026-08-29T09:13:05.057Z
7
+ * @hash af324f5
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -3274,6 +3274,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3274
3274
  default: return "";
3275
3275
  }
3276
3276
  }
3277
+ /**
3278
+ * Only converts the decimal separator, ignore number format such as % or dates
3279
+ */
3280
+ function toLocaleString(data, locale) {
3281
+ const value = toValue(data);
3282
+ switch (typeof value) {
3283
+ case "string": return value;
3284
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3285
+ case "boolean": return value ? "TRUE" : "FALSE";
3286
+ default: return "";
3287
+ }
3288
+ }
3277
3289
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3278
3290
  const normalizeString = memoize(function normalizeString(str) {
3279
3291
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -21715,7 +21727,7 @@ stores.inject(MyMetaStore, storeInstance);
21715
21727
  description: _t("Concatenation of two values."),
21716
21728
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
21717
21729
  compute: function(value1, value2) {
21718
- return toString(value1) + toString(value2);
21730
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
21719
21731
  },
21720
21732
  isExported: true
21721
21733
  };
@@ -22517,7 +22529,7 @@ stores.inject(MyMetaStore, storeInstance);
22517
22529
  description: _t("Remove non-printable characters from a piece of text."),
22518
22530
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
22519
22531
  compute: function(text) {
22520
- const _text = toString(text);
22532
+ const _text = toLocaleString(text, this.locale);
22521
22533
  let cleanedStr = "";
22522
22534
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
22523
22535
  return cleanedStr;
@@ -22528,7 +22540,7 @@ stores.inject(MyMetaStore, storeInstance);
22528
22540
  description: _t("Appends strings to one another."),
22529
22541
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
22530
22542
  compute: function(...datas) {
22531
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
22543
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
22532
22544
  },
22533
22545
  isExported: true
22534
22546
  };
@@ -22536,7 +22548,7 @@ stores.inject(MyMetaStore, storeInstance);
22536
22548
  description: _t("Tests whether two strings are identical."),
22537
22549
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
22538
22550
  compute: function(string1, string2) {
22539
- return toString(string1) === toString(string2);
22551
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
22540
22552
  },
22541
22553
  isExported: true
22542
22554
  };
@@ -22548,13 +22560,13 @@ stores.inject(MyMetaStore, storeInstance);
22548
22560
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
22549
22561
  ],
22550
22562
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
22551
- const _searchFor = toString(searchFor);
22552
- const _textToSearch = toString(textToSearch);
22563
+ const _searchFor = toLocaleString(searchFor, this.locale);
22564
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
22553
22565
  const _startingAt = toNumber(startingAt, this.locale);
22554
22566
  assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
22555
22567
  assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
22556
22568
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
22557
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
22569
+ assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
22558
22570
  return result + 1;
22559
22571
  },
22560
22572
  isExported: true
@@ -22567,8 +22579,8 @@ stores.inject(MyMetaStore, storeInstance);
22567
22579
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
22568
22580
  ],
22569
22581
  compute: function(delimiter, ...valuesOrArrays) {
22570
- const _delimiter = toString(delimiter);
22571
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
22582
+ const _delimiter = toLocaleString(delimiter, this.locale);
22583
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
22572
22584
  }
22573
22585
  };
22574
22586
  const LEFT = {
@@ -22577,7 +22589,7 @@ stores.inject(MyMetaStore, storeInstance);
22577
22589
  compute: function(text, ...args) {
22578
22590
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
22579
22591
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
22580
- return toString(text).substring(0, _numberOfCharacters);
22592
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
22581
22593
  },
22582
22594
  isExported: true
22583
22595
  };
@@ -22585,7 +22597,7 @@ stores.inject(MyMetaStore, storeInstance);
22585
22597
  description: _t("Length of a string."),
22586
22598
  args: [arg("text (string)", _t("The string whose length will be returned."))],
22587
22599
  compute: function(text) {
22588
- return toString(text).length;
22600
+ return toLocaleString(text, this.locale).length;
22589
22601
  },
22590
22602
  isExported: true
22591
22603
  };
@@ -22593,7 +22605,7 @@ stores.inject(MyMetaStore, storeInstance);
22593
22605
  description: _t("Converts a specified string to lowercase."),
22594
22606
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
22595
22607
  compute: function(text) {
22596
- return toString(text).toLowerCase();
22608
+ return toLocaleString(text, this.locale).toLowerCase();
22597
22609
  },
22598
22610
  isExported: true
22599
22611
  };
@@ -22605,7 +22617,7 @@ stores.inject(MyMetaStore, storeInstance);
22605
22617
  arg("extract_length (number)", _t("The length of the segment to extract."))
22606
22618
  ],
22607
22619
  compute: function(text, starting_at, extract_length) {
22608
- const _text = toString(text);
22620
+ const _text = toLocaleString(text, this.locale);
22609
22621
  const _starting_at = toNumber(starting_at, this.locale);
22610
22622
  const _extract_length = toNumber(extract_length, this.locale);
22611
22623
  assert(() => _starting_at >= 1, _t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -22618,7 +22630,7 @@ stores.inject(MyMetaStore, storeInstance);
22618
22630
  description: _t("Capitalizes each word in a specified string."),
22619
22631
  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."))],
22620
22632
  compute: function(text) {
22621
- return toString(text).replace(wordRegex, (word) => {
22633
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
22622
22634
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
22623
22635
  });
22624
22636
  },
@@ -22635,9 +22647,9 @@ stores.inject(MyMetaStore, storeInstance);
22635
22647
  compute: function(text, position, length, newText) {
22636
22648
  const _position = toNumber(position, this.locale);
22637
22649
  assert(() => _position >= 1, _t("The position (%s) must be greater than or equal to 1.", _position.toString()));
22638
- const _text = toString(text);
22650
+ const _text = toLocaleString(text, this.locale);
22639
22651
  const _length = toNumber(length, this.locale);
22640
- const _newText = toString(newText);
22652
+ const _newText = toLocaleString(newText, this.locale);
22641
22653
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
22642
22654
  },
22643
22655
  isExported: true
@@ -22648,7 +22660,7 @@ stores.inject(MyMetaStore, storeInstance);
22648
22660
  compute: function(text, ...args) {
22649
22661
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
22650
22662
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
22651
- const _text = toString(text);
22663
+ const _text = toLocaleString(text, this.locale);
22652
22664
  const stringLength = _text.length;
22653
22665
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
22654
22666
  },
@@ -22662,8 +22674,8 @@ stores.inject(MyMetaStore, storeInstance);
22662
22674
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
22663
22675
  ],
22664
22676
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
22665
- const _searchFor = toString(searchFor).toLowerCase();
22666
- const _textToSearch = toString(textToSearch).toLowerCase();
22677
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
22678
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
22667
22679
  const _startingAt = toNumber(startingAt, this.locale);
22668
22680
  if (_textToSearch === "") return {
22669
22681
  value: CellErrorType.GenericError,
@@ -22693,8 +22705,8 @@ stores.inject(MyMetaStore, storeInstance);
22693
22705
  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."))
22694
22706
  ],
22695
22707
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
22696
- const _text = toString(text);
22697
- const _delimiter = escapeRegExp(toString(delimiter));
22708
+ const _text = toLocaleString(text, this.locale);
22709
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
22698
22710
  const _splitByEach = toBoolean(splitByEach);
22699
22711
  const _removeEmptyText = toBoolean(removeEmptyText);
22700
22712
  assert(() => _delimiter.length > 0, _t("The _delimiter (%s) must be not be empty.", _delimiter));
@@ -22716,10 +22728,10 @@ stores.inject(MyMetaStore, storeInstance);
22716
22728
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
22717
22729
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
22718
22730
  assert(() => _occurrenceNumber >= 0, _t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber.toString()));
22719
- const _textToSearch = toString(textToSearch);
22720
- const _searchFor = toString(searchFor);
22731
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
22732
+ const _searchFor = toLocaleString(searchFor, this.locale);
22721
22733
  if (_searchFor === "") return _textToSearch;
22722
- const _replaceWith = toString(replaceWith);
22734
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
22723
22735
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
22724
22736
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
22725
22737
  let n = 0;
@@ -22736,10 +22748,10 @@ stores.inject(MyMetaStore, storeInstance);
22736
22748
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
22737
22749
  ],
22738
22750
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
22739
- const _delimiter = toString(delimiter);
22751
+ const _delimiter = toLocaleString(delimiter, this.locale);
22740
22752
  const _ignoreEmpty = toBoolean(ignoreEmpty);
22741
22753
  let n = 0;
22742
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
22754
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
22743
22755
  },
22744
22756
  isExported: true
22745
22757
  };
@@ -22747,7 +22759,7 @@ stores.inject(MyMetaStore, storeInstance);
22747
22759
  description: _t("Removes space characters."),
22748
22760
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
22749
22761
  compute: function(text) {
22750
- return trimContent(toString(text));
22762
+ return trimContent(toLocaleString(text, this.locale));
22751
22763
  },
22752
22764
  isExported: true
22753
22765
  };
@@ -22755,7 +22767,7 @@ stores.inject(MyMetaStore, storeInstance);
22755
22767
  description: _t("Converts a specified string to uppercase."),
22756
22768
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
22757
22769
  compute: function(text) {
22758
- return toString(text).toUpperCase();
22770
+ return toLocaleString(text, this.locale).toUpperCase();
22759
22771
  },
22760
22772
  isExported: true
22761
22773
  };
@@ -22787,7 +22799,7 @@ stores.inject(MyMetaStore, storeInstance);
22787
22799
  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."))],
22788
22800
  compute: function(url, linkLabel) {
22789
22801
  const processedUrl = toString(url).trim();
22790
- const processedLabel = toString(linkLabel) || processedUrl;
22802
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
22791
22803
  if (processedUrl === "") return processedLabel;
22792
22804
  return markdownLink(processedLabel, processedUrl);
22793
22805
  },
@@ -27973,12 +27985,13 @@ stores.inject(MyMetaStore, storeInstance);
27973
27985
  }
27974
27986
  sortFilterZone(sortDirection) {
27975
27987
  const filterPosition = this.props.filterPosition;
27976
- const tableZone = this.table?.range.zone;
27988
+ const table = this.table;
27989
+ const tableZone = table?.range.zone;
27977
27990
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
27978
27991
  const sheetId = this.env.model.getters.getActiveSheetId();
27979
27992
  const contentZone = {
27980
27993
  ...tableZone,
27981
- top: tableZone.top + 1
27994
+ top: tableZone.top + table.config.numberOfHeaders
27982
27995
  };
27983
27996
  this.env.model.dispatch("SORT_CELLS", {
27984
27997
  sheetId,
@@ -32561,6 +32574,12 @@ stores.inject(MyMetaStore, storeInstance);
32561
32574
  background-color: ${ACTION_COLOR};
32562
32575
  border-color: ${ACTION_COLOR};
32563
32576
  }
32577
+
32578
+ &:focus {
32579
+ outline: none;
32580
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
32581
+ border-color: ${ACTION_COLOR};
32582
+ }
32564
32583
  }
32565
32584
  }
32566
32585
  `;
@@ -38092,6 +38111,7 @@ stores.inject(MyMetaStore, storeInstance);
38092
38111
  .os-input {
38093
38112
  box-sizing: border-box;
38094
38113
  border-width: 0 0 1px 0;
38114
+ border-style: solid;
38095
38115
  border-color: transparent;
38096
38116
  outline: none;
38097
38117
  text-overflow: ellipsis;
@@ -60653,6 +60673,9 @@ stores.inject(MyMetaStore, storeInstance);
60653
60673
  position: absolute;
60654
60674
  cursor: pointer;
60655
60675
  }
60676
+ .o-grid.o-dashboard-grid {
60677
+ background: #ffffff;
60678
+ }
60656
60679
  `;
60657
60680
  var SpreadsheetDashboard = class extends _odoo_owl.Component {
60658
60681
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -62065,6 +62088,7 @@ stores.inject(MyMetaStore, storeInstance);
62065
62088
  width: 100%;
62066
62089
  outline: none;
62067
62090
  border-color: ${GRAY_300};
62091
+ border-style: solid;
62068
62092
  color: ${GRAY_900};
62069
62093
 
62070
62094
  &::placeholder {
@@ -66434,8 +66458,8 @@ exports.stores = stores;
66434
66458
  exports.tokenColors = tokenColors;
66435
66459
  exports.tokenize = tokenize;
66436
66460
 
66437
- __info__.version = "18.0.78";
66438
- __info__.date = "2026-08-10T12:30:26.634Z";
66439
- __info__.hash = "3fdaa53";
66461
+ __info__.version = "18.0.80";
66462
+ __info__.date = "2026-08-29T09:13:05.057Z";
66463
+ __info__.hash = "af324f5";
66440
66464
 
66441
66465
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);