@odoo/o-spreadsheet 18.3.59 → 18.3.61

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.3.59
6
- * @date 2026-08-10T12:30:18.612Z
7
- * @hash c215f06
5
+ * @version 18.3.61
6
+ * @date 2026-08-29T09:13:33.631Z
7
+ * @hash c786d2f
8
8
  */
9
9
  /* Originates from src/components/top_bar/top_bar.scss */
10
10
  @media (max-width: 900px) {
@@ -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.3.59
6
- * @date 2026-08-10T12:30:17.132Z
7
- * @hash c215f06
5
+ * @version 18.3.61
6
+ * @date 2026-08-29T09:13:32.113Z
7
+ * @hash c786d2f
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";
@@ -3457,6 +3457,18 @@ function toString(data) {
3457
3457
  default: return "";
3458
3458
  }
3459
3459
  }
3460
+ /**
3461
+ * Only converts the decimal separator, ignore number format such as % or dates
3462
+ */
3463
+ function toLocaleString(data, locale) {
3464
+ const value = toValue(data);
3465
+ switch (typeof value) {
3466
+ case "string": return value;
3467
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3468
+ case "boolean": return value ? "TRUE" : "FALSE";
3469
+ default: return "";
3470
+ }
3471
+ }
3460
3472
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3461
3473
  const normalizeString = memoize(function normalizeString(str) {
3462
3474
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -15703,7 +15715,7 @@ const CONCAT = {
15703
15715
  description: _t("Concatenation of two values."),
15704
15716
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
15705
15717
  compute: function(value1, value2) {
15706
- return toString(value1) + toString(value2);
15718
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
15707
15719
  },
15708
15720
  isExported: true
15709
15721
  };
@@ -16513,7 +16525,7 @@ const CLEAN = {
16513
16525
  description: _t("Remove non-printable characters from a piece of text."),
16514
16526
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
16515
16527
  compute: function(text) {
16516
- const _text = toString(text);
16528
+ const _text = toLocaleString(text, this.locale);
16517
16529
  let cleanedStr = "";
16518
16530
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
16519
16531
  return cleanedStr;
@@ -16524,7 +16536,7 @@ const CONCATENATE = {
16524
16536
  description: _t("Appends strings to one another."),
16525
16537
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
16526
16538
  compute: function(...datas) {
16527
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
16539
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
16528
16540
  },
16529
16541
  isExported: true
16530
16542
  };
@@ -16532,7 +16544,7 @@ const EXACT = {
16532
16544
  description: _t("Tests whether two strings are identical."),
16533
16545
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
16534
16546
  compute: function(string1, string2) {
16535
- return toString(string1) === toString(string2);
16547
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
16536
16548
  },
16537
16549
  isExported: true
16538
16550
  };
@@ -16544,13 +16556,13 @@ const FIND = {
16544
16556
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16545
16557
  ],
16546
16558
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16547
- const _searchFor = toString(searchFor);
16548
- const _textToSearch = toString(textToSearch);
16559
+ const _searchFor = toLocaleString(searchFor, this.locale);
16560
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16549
16561
  const _startingAt = toNumber(startingAt, this.locale);
16550
16562
  assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
16551
16563
  assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
16552
16564
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
16553
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
16565
+ assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
16554
16566
  return result + 1;
16555
16567
  },
16556
16568
  isExported: true
@@ -16563,8 +16575,8 @@ const JOIN = {
16563
16575
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
16564
16576
  ],
16565
16577
  compute: function(delimiter, ...valuesOrArrays) {
16566
- const _delimiter = toString(delimiter);
16567
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
16578
+ const _delimiter = toLocaleString(delimiter, this.locale);
16579
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
16568
16580
  }
16569
16581
  };
16570
16582
  const LEFT = {
@@ -16573,7 +16585,7 @@ const LEFT = {
16573
16585
  compute: function(text, ...args) {
16574
16586
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16575
16587
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
16576
- return toString(text).substring(0, _numberOfCharacters);
16588
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
16577
16589
  },
16578
16590
  isExported: true
16579
16591
  };
@@ -16581,7 +16593,7 @@ const LEN = {
16581
16593
  description: _t("Length of a string."),
16582
16594
  args: [arg("text (string)", _t("The string whose length will be returned."))],
16583
16595
  compute: function(text) {
16584
- return toString(text).length;
16596
+ return toLocaleString(text, this.locale).length;
16585
16597
  },
16586
16598
  isExported: true
16587
16599
  };
@@ -16589,7 +16601,7 @@ const LOWER = {
16589
16601
  description: _t("Converts a specified string to lowercase."),
16590
16602
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
16591
16603
  compute: function(text) {
16592
- return toString(text).toLowerCase();
16604
+ return toLocaleString(text, this.locale).toLowerCase();
16593
16605
  },
16594
16606
  isExported: true
16595
16607
  };
@@ -16601,7 +16613,7 @@ const MID = {
16601
16613
  arg("extract_length (number)", _t("The length of the segment to extract."))
16602
16614
  ],
16603
16615
  compute: function(text, starting_at, extract_length) {
16604
- const _text = toString(text);
16616
+ const _text = toLocaleString(text, this.locale);
16605
16617
  const _starting_at = toNumber(starting_at, this.locale);
16606
16618
  const _extract_length = toNumber(extract_length, this.locale);
16607
16619
  assert(() => _starting_at >= 1, _t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -16614,7 +16626,7 @@ const PROPER = {
16614
16626
  description: _t("Capitalizes each word in a specified string."),
16615
16627
  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."))],
16616
16628
  compute: function(text) {
16617
- return toString(text).replace(wordRegex, (word) => {
16629
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
16618
16630
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
16619
16631
  });
16620
16632
  },
@@ -16631,9 +16643,9 @@ const REPLACE = {
16631
16643
  compute: function(text, position, length, newText) {
16632
16644
  const _position = toNumber(position, this.locale);
16633
16645
  assert(() => _position >= 1, _t("The position (%s) must be greater than or equal to 1.", _position.toString()));
16634
- const _text = toString(text);
16646
+ const _text = toLocaleString(text, this.locale);
16635
16647
  const _length = toNumber(length, this.locale);
16636
- const _newText = toString(newText);
16648
+ const _newText = toLocaleString(newText, this.locale);
16637
16649
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
16638
16650
  },
16639
16651
  isExported: true
@@ -16644,7 +16656,7 @@ const RIGHT = {
16644
16656
  compute: function(text, ...args) {
16645
16657
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16646
16658
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
16647
- const _text = toString(text);
16659
+ const _text = toLocaleString(text, this.locale);
16648
16660
  const stringLength = _text.length;
16649
16661
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
16650
16662
  },
@@ -16658,8 +16670,8 @@ const SEARCH = {
16658
16670
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16659
16671
  ],
16660
16672
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16661
- const _searchFor = toString(searchFor).toLowerCase();
16662
- const _textToSearch = toString(textToSearch).toLowerCase();
16673
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
16674
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
16663
16675
  const _startingAt = toNumber(startingAt, this.locale);
16664
16676
  if (_textToSearch === "") return {
16665
16677
  value: CellErrorType.GenericError,
@@ -16689,8 +16701,8 @@ const SPLIT = {
16689
16701
  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."))
16690
16702
  ],
16691
16703
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
16692
- const _text = toString(text);
16693
- const _delimiter = escapeRegExp(toString(delimiter));
16704
+ const _text = toLocaleString(text, this.locale);
16705
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
16694
16706
  const _splitByEach = toBoolean(splitByEach);
16695
16707
  const _removeEmptyText = toBoolean(removeEmptyText);
16696
16708
  assert(() => _delimiter.length > 0, _t("The _delimiter (%s) must be not be empty.", _delimiter));
@@ -16712,10 +16724,10 @@ const SUBSTITUTE = {
16712
16724
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
16713
16725
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
16714
16726
  assert(() => _occurrenceNumber >= 0, _t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber.toString()));
16715
- const _textToSearch = toString(textToSearch);
16716
- const _searchFor = toString(searchFor);
16727
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16728
+ const _searchFor = toLocaleString(searchFor, this.locale);
16717
16729
  if (_searchFor === "") return _textToSearch;
16718
- const _replaceWith = toString(replaceWith);
16730
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
16719
16731
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
16720
16732
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
16721
16733
  let n = 0;
@@ -16732,10 +16744,10 @@ const TEXTJOIN = {
16732
16744
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
16733
16745
  ],
16734
16746
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
16735
- const _delimiter = toString(delimiter);
16747
+ const _delimiter = toLocaleString(delimiter, this.locale);
16736
16748
  const _ignoreEmpty = toBoolean(ignoreEmpty);
16737
16749
  let n = 0;
16738
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
16750
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
16739
16751
  },
16740
16752
  isExported: true
16741
16753
  };
@@ -16743,7 +16755,7 @@ const TRIM = {
16743
16755
  description: _t("Removes space characters."),
16744
16756
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
16745
16757
  compute: function(text) {
16746
- return trimContent(toString(text));
16758
+ return trimContent(toLocaleString(text, this.locale));
16747
16759
  },
16748
16760
  isExported: true
16749
16761
  };
@@ -16751,7 +16763,7 @@ const UPPER = {
16751
16763
  description: _t("Converts a specified string to uppercase."),
16752
16764
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
16753
16765
  compute: function(text) {
16754
- return toString(text).toUpperCase();
16766
+ return toLocaleString(text, this.locale).toUpperCase();
16755
16767
  },
16756
16768
  isExported: true
16757
16769
  };
@@ -16783,7 +16795,7 @@ const HYPERLINK = {
16783
16795
  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."))],
16784
16796
  compute: function(url, linkLabel) {
16785
16797
  const processedUrl = toString(url).trim();
16786
- const processedLabel = toString(linkLabel) || processedUrl;
16798
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
16787
16799
  if (processedUrl === "") return processedLabel;
16788
16800
  return markdownLink(processedLabel, processedUrl);
16789
16801
  },
@@ -17964,7 +17976,8 @@ function getRadarChartDatasets(definition, args) {
17964
17976
  data,
17965
17977
  hidden,
17966
17978
  borderColor,
17967
- backgroundColor: borderColor
17979
+ backgroundColor: borderColor,
17980
+ pointBackgroundColor: borderColor
17968
17981
  };
17969
17982
  if (fill) {
17970
17983
  dataset.backgroundColor = setColorAlpha(borderColor, LINE_FILL_TRANSPARENCY);
@@ -31323,12 +31336,13 @@ var FilterMenu = class extends Component {
31323
31336
  }
31324
31337
  sortFilterZone(sortDirection) {
31325
31338
  const filterPosition = this.props.filterPosition;
31326
- const tableZone = this.table?.range.zone;
31339
+ const table = this.table;
31340
+ const tableZone = table?.range.zone;
31327
31341
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
31328
31342
  const sheetId = this.env.model.getters.getActiveSheetId();
31329
31343
  const contentZone = {
31330
31344
  ...tableZone,
31331
- top: tableZone.top + 1
31345
+ top: tableZone.top + table.config.numberOfHeaders
31332
31346
  };
31333
31347
  const sortAnchor = {
31334
31348
  col: filterPosition.col,
@@ -37313,6 +37327,12 @@ css`
37313
37327
  background-color: ${ACTION_COLOR};
37314
37328
  border-color: ${ACTION_COLOR};
37315
37329
  }
37330
+
37331
+ &:focus {
37332
+ outline: none;
37333
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
37334
+ border-color: ${ACTION_COLOR};
37335
+ }
37316
37336
  }
37317
37337
  }
37318
37338
  `;
@@ -42910,6 +42930,7 @@ css`
42910
42930
  .o-spreadsheet {
42911
42931
  .os-input {
42912
42932
  border-width: 0 0 1px 0;
42933
+ border-style: solid;
42913
42934
  border-color: transparent;
42914
42935
  outline: none;
42915
42936
  text-overflow: ellipsis;
@@ -66202,6 +66223,9 @@ css`
66202
66223
  position: absolute;
66203
66224
  cursor: pointer;
66204
66225
  }
66226
+ .o-grid.o-dashboard-grid {
66227
+ background: #ffffff;
66228
+ }
66205
66229
  `;
66206
66230
  var SpreadsheetDashboard = class extends Component {
66207
66231
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -67866,6 +67890,7 @@ css`
67866
67890
  width: 100%;
67867
67891
  outline: none;
67868
67892
  border-color: ${GRAY_300};
67893
+ border-style: solid;
67869
67894
  color: ${GRAY_900};
67870
67895
 
67871
67896
  &::placeholder {
@@ -72279,6 +72304,6 @@ const chartHelpers = {
72279
72304
  //#endregion
72280
72305
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, 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 };
72281
72306
 
72282
- __info__.version = "18.3.59";
72283
- __info__.date = "2026-08-10T12:30:17.132Z";
72284
- __info__.hash = "c215f06";
72307
+ __info__.version = "18.3.61";
72308
+ __info__.date = "2026-08-29T09:13:32.113Z";
72309
+ __info__.hash = "c786d2f";
@@ -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.3.59
6
- * @date 2026-08-10T12:30:17.132Z
7
- * @hash c215f06
5
+ * @version 18.3.61
6
+ * @date 2026-08-29T09:13:32.113Z
7
+ * @hash c786d2f
8
8
  */
9
9
 
10
10
  (function(exports, _odoo_owl) {
@@ -3459,6 +3459,18 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3459
3459
  default: return "";
3460
3460
  }
3461
3461
  }
3462
+ /**
3463
+ * Only converts the decimal separator, ignore number format such as % or dates
3464
+ */
3465
+ function toLocaleString(data, locale) {
3466
+ const value = toValue(data);
3467
+ switch (typeof value) {
3468
+ case "string": return value;
3469
+ case "number": return value.toString().replace(".", locale.decimalSeparator);
3470
+ case "boolean": return value ? "TRUE" : "FALSE";
3471
+ default: return "";
3472
+ }
3473
+ }
3462
3474
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3463
3475
  const normalizeString = memoize(function normalizeString(str) {
3464
3476
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -15705,7 +15717,7 @@ stores.inject(MyMetaStore, storeInstance);
15705
15717
  description: _t("Concatenation of two values."),
15706
15718
  args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
15707
15719
  compute: function(value1, value2) {
15708
- return toString(value1) + toString(value2);
15720
+ return toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale);
15709
15721
  },
15710
15722
  isExported: true
15711
15723
  };
@@ -16515,7 +16527,7 @@ stores.inject(MyMetaStore, storeInstance);
16515
16527
  description: _t("Remove non-printable characters from a piece of text."),
16516
16528
  args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
16517
16529
  compute: function(text) {
16518
- const _text = toString(text);
16530
+ const _text = toLocaleString(text, this.locale);
16519
16531
  let cleanedStr = "";
16520
16532
  for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
16521
16533
  return cleanedStr;
@@ -16526,7 +16538,7 @@ stores.inject(MyMetaStore, storeInstance);
16526
16538
  description: _t("Appends strings to one another."),
16527
16539
  args: [arg("string1 (string, range<string>)", _t("The initial string.")), arg("string2 (string, range<string>, repeating)", _t("More strings to append in sequence."))],
16528
16540
  compute: function(...datas) {
16529
- return reduceAny(datas, (acc, a) => acc + toString(a), "");
16541
+ return reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "");
16530
16542
  },
16531
16543
  isExported: true
16532
16544
  };
@@ -16534,7 +16546,7 @@ stores.inject(MyMetaStore, storeInstance);
16534
16546
  description: _t("Tests whether two strings are identical."),
16535
16547
  args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
16536
16548
  compute: function(string1, string2) {
16537
- return toString(string1) === toString(string2);
16549
+ return toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale);
16538
16550
  },
16539
16551
  isExported: true
16540
16552
  };
@@ -16546,13 +16558,13 @@ stores.inject(MyMetaStore, storeInstance);
16546
16558
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16547
16559
  ],
16548
16560
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16549
- const _searchFor = toString(searchFor);
16550
- const _textToSearch = toString(textToSearch);
16561
+ const _searchFor = toLocaleString(searchFor, this.locale);
16562
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16551
16563
  const _startingAt = toNumber(startingAt, this.locale);
16552
16564
  assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
16553
16565
  assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
16554
16566
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
16555
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor.toString(), _textToSearch));
16567
+ assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
16556
16568
  return result + 1;
16557
16569
  },
16558
16570
  isExported: true
@@ -16565,8 +16577,8 @@ stores.inject(MyMetaStore, storeInstance);
16565
16577
  arg("value_or_array2 (string, range<string>, repeating)", _t("More values to be appended using delimiter."))
16566
16578
  ],
16567
16579
  compute: function(delimiter, ...valuesOrArrays) {
16568
- const _delimiter = toString(delimiter);
16569
- return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toString(a), "");
16580
+ const _delimiter = toLocaleString(delimiter, this.locale);
16581
+ return reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "");
16570
16582
  }
16571
16583
  };
16572
16584
  const LEFT = {
@@ -16575,7 +16587,7 @@ stores.inject(MyMetaStore, storeInstance);
16575
16587
  compute: function(text, ...args) {
16576
16588
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16577
16589
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
16578
- return toString(text).substring(0, _numberOfCharacters);
16590
+ return toLocaleString(text, this.locale).substring(0, _numberOfCharacters);
16579
16591
  },
16580
16592
  isExported: true
16581
16593
  };
@@ -16583,7 +16595,7 @@ stores.inject(MyMetaStore, storeInstance);
16583
16595
  description: _t("Length of a string."),
16584
16596
  args: [arg("text (string)", _t("The string whose length will be returned."))],
16585
16597
  compute: function(text) {
16586
- return toString(text).length;
16598
+ return toLocaleString(text, this.locale).length;
16587
16599
  },
16588
16600
  isExported: true
16589
16601
  };
@@ -16591,7 +16603,7 @@ stores.inject(MyMetaStore, storeInstance);
16591
16603
  description: _t("Converts a specified string to lowercase."),
16592
16604
  args: [arg("text (string)", _t("The string to convert to lowercase."))],
16593
16605
  compute: function(text) {
16594
- return toString(text).toLowerCase();
16606
+ return toLocaleString(text, this.locale).toLowerCase();
16595
16607
  },
16596
16608
  isExported: true
16597
16609
  };
@@ -16603,7 +16615,7 @@ stores.inject(MyMetaStore, storeInstance);
16603
16615
  arg("extract_length (number)", _t("The length of the segment to extract."))
16604
16616
  ],
16605
16617
  compute: function(text, starting_at, extract_length) {
16606
- const _text = toString(text);
16618
+ const _text = toLocaleString(text, this.locale);
16607
16619
  const _starting_at = toNumber(starting_at, this.locale);
16608
16620
  const _extract_length = toNumber(extract_length, this.locale);
16609
16621
  assert(() => _starting_at >= 1, _t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
@@ -16616,7 +16628,7 @@ stores.inject(MyMetaStore, storeInstance);
16616
16628
  description: _t("Capitalizes each word in a specified string."),
16617
16629
  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."))],
16618
16630
  compute: function(text) {
16619
- return toString(text).replace(wordRegex, (word) => {
16631
+ return toLocaleString(text, this.locale).replace(wordRegex, (word) => {
16620
16632
  return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
16621
16633
  });
16622
16634
  },
@@ -16633,9 +16645,9 @@ stores.inject(MyMetaStore, storeInstance);
16633
16645
  compute: function(text, position, length, newText) {
16634
16646
  const _position = toNumber(position, this.locale);
16635
16647
  assert(() => _position >= 1, _t("The position (%s) must be greater than or equal to 1.", _position.toString()));
16636
- const _text = toString(text);
16648
+ const _text = toLocaleString(text, this.locale);
16637
16649
  const _length = toNumber(length, this.locale);
16638
- const _newText = toString(newText);
16650
+ const _newText = toLocaleString(newText, this.locale);
16639
16651
  return _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length);
16640
16652
  },
16641
16653
  isExported: true
@@ -16646,7 +16658,7 @@ stores.inject(MyMetaStore, storeInstance);
16646
16658
  compute: function(text, ...args) {
16647
16659
  const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
16648
16660
  assert(() => _numberOfCharacters >= 0, _t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters.toString()));
16649
- const _text = toString(text);
16661
+ const _text = toLocaleString(text, this.locale);
16650
16662
  const stringLength = _text.length;
16651
16663
  return _text.substring(stringLength - _numberOfCharacters, stringLength);
16652
16664
  },
@@ -16660,8 +16672,8 @@ stores.inject(MyMetaStore, storeInstance);
16660
16672
  arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
16661
16673
  ],
16662
16674
  compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
16663
- const _searchFor = toString(searchFor).toLowerCase();
16664
- const _textToSearch = toString(textToSearch).toLowerCase();
16675
+ const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
16676
+ const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
16665
16677
  const _startingAt = toNumber(startingAt, this.locale);
16666
16678
  if (_textToSearch === "") return {
16667
16679
  value: CellErrorType.GenericError,
@@ -16691,8 +16703,8 @@ stores.inject(MyMetaStore, storeInstance);
16691
16703
  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."))
16692
16704
  ],
16693
16705
  compute: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
16694
- const _text = toString(text);
16695
- const _delimiter = escapeRegExp(toString(delimiter));
16706
+ const _text = toLocaleString(text, this.locale);
16707
+ const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
16696
16708
  const _splitByEach = toBoolean(splitByEach);
16697
16709
  const _removeEmptyText = toBoolean(removeEmptyText);
16698
16710
  assert(() => _delimiter.length > 0, _t("The _delimiter (%s) must be not be empty.", _delimiter));
@@ -16714,10 +16726,10 @@ stores.inject(MyMetaStore, storeInstance);
16714
16726
  compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
16715
16727
  const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
16716
16728
  assert(() => _occurrenceNumber >= 0, _t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber.toString()));
16717
- const _textToSearch = toString(textToSearch);
16718
- const _searchFor = toString(searchFor);
16729
+ const _textToSearch = toLocaleString(textToSearch, this.locale);
16730
+ const _searchFor = toLocaleString(searchFor, this.locale);
16719
16731
  if (_searchFor === "") return _textToSearch;
16720
- const _replaceWith = toString(replaceWith);
16732
+ const _replaceWith = toLocaleString(replaceWith, this.locale);
16721
16733
  const reg = new RegExp(escapeRegExp(_searchFor), "g");
16722
16734
  if (_occurrenceNumber === 0) return _textToSearch.replace(reg, _replaceWith);
16723
16735
  let n = 0;
@@ -16734,10 +16746,10 @@ stores.inject(MyMetaStore, storeInstance);
16734
16746
  arg("text2 (string, range<string>, repeating)", _t("Additional text item(s)."))
16735
16747
  ],
16736
16748
  compute: function(delimiter, ignoreEmpty, ...textsOrArrays) {
16737
- const _delimiter = toString(delimiter);
16749
+ const _delimiter = toLocaleString(delimiter, this.locale);
16738
16750
  const _ignoreEmpty = toBoolean(ignoreEmpty);
16739
16751
  let n = 0;
16740
- return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toString(a) === "") ? (n++ ? acc + _delimiter : "") + toString(a) : acc, "");
16752
+ return reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "");
16741
16753
  },
16742
16754
  isExported: true
16743
16755
  };
@@ -16745,7 +16757,7 @@ stores.inject(MyMetaStore, storeInstance);
16745
16757
  description: _t("Removes space characters."),
16746
16758
  args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
16747
16759
  compute: function(text) {
16748
- return trimContent(toString(text));
16760
+ return trimContent(toLocaleString(text, this.locale));
16749
16761
  },
16750
16762
  isExported: true
16751
16763
  };
@@ -16753,7 +16765,7 @@ stores.inject(MyMetaStore, storeInstance);
16753
16765
  description: _t("Converts a specified string to uppercase."),
16754
16766
  args: [arg("text (string)", _t("The string to convert to uppercase."))],
16755
16767
  compute: function(text) {
16756
- return toString(text).toUpperCase();
16768
+ return toLocaleString(text, this.locale).toUpperCase();
16757
16769
  },
16758
16770
  isExported: true
16759
16771
  };
@@ -16785,7 +16797,7 @@ stores.inject(MyMetaStore, storeInstance);
16785
16797
  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."))],
16786
16798
  compute: function(url, linkLabel) {
16787
16799
  const processedUrl = toString(url).trim();
16788
- const processedLabel = toString(linkLabel) || processedUrl;
16800
+ const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
16789
16801
  if (processedUrl === "") return processedLabel;
16790
16802
  return markdownLink(processedLabel, processedUrl);
16791
16803
  },
@@ -17966,7 +17978,8 @@ stores.inject(MyMetaStore, storeInstance);
17966
17978
  data,
17967
17979
  hidden,
17968
17980
  borderColor,
17969
- backgroundColor: borderColor
17981
+ backgroundColor: borderColor,
17982
+ pointBackgroundColor: borderColor
17970
17983
  };
17971
17984
  if (fill) {
17972
17985
  dataset.backgroundColor = setColorAlpha(borderColor, LINE_FILL_TRANSPARENCY);
@@ -31325,12 +31338,13 @@ stores.inject(MyMetaStore, storeInstance);
31325
31338
  }
31326
31339
  sortFilterZone(sortDirection) {
31327
31340
  const filterPosition = this.props.filterPosition;
31328
- const tableZone = this.table?.range.zone;
31341
+ const table = this.table;
31342
+ const tableZone = table?.range.zone;
31329
31343
  if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
31330
31344
  const sheetId = this.env.model.getters.getActiveSheetId();
31331
31345
  const contentZone = {
31332
31346
  ...tableZone,
31333
- top: tableZone.top + 1
31347
+ top: tableZone.top + table.config.numberOfHeaders
31334
31348
  };
31335
31349
  const sortAnchor = {
31336
31350
  col: filterPosition.col,
@@ -37315,6 +37329,12 @@ stores.inject(MyMetaStore, storeInstance);
37315
37329
  background-color: ${ACTION_COLOR};
37316
37330
  border-color: ${ACTION_COLOR};
37317
37331
  }
37332
+
37333
+ &:focus {
37334
+ outline: none;
37335
+ box-shadow: 0 0 0 0.25rem rgba(113, 75, 103, 0.25);
37336
+ border-color: ${ACTION_COLOR};
37337
+ }
37318
37338
  }
37319
37339
  }
37320
37340
  `;
@@ -42912,6 +42932,7 @@ stores.inject(MyMetaStore, storeInstance);
42912
42932
  .o-spreadsheet {
42913
42933
  .os-input {
42914
42934
  border-width: 0 0 1px 0;
42935
+ border-style: solid;
42915
42936
  border-color: transparent;
42916
42937
  outline: none;
42917
42938
  text-overflow: ellipsis;
@@ -66204,6 +66225,9 @@ stores.inject(MyMetaStore, storeInstance);
66204
66225
  position: absolute;
66205
66226
  cursor: pointer;
66206
66227
  }
66228
+ .o-grid.o-dashboard-grid {
66229
+ background: #ffffff;
66230
+ }
66207
66231
  `;
66208
66232
  var SpreadsheetDashboard = class extends _odoo_owl.Component {
66209
66233
  static template = "o-spreadsheet-SpreadsheetDashboard";
@@ -67868,6 +67892,7 @@ stores.inject(MyMetaStore, storeInstance);
67868
67892
  width: 100%;
67869
67893
  outline: none;
67870
67894
  border-color: ${GRAY_300};
67895
+ border-style: solid;
67871
67896
  color: ${GRAY_900};
67872
67897
 
67873
67898
  &::placeholder {
@@ -72328,8 +72353,8 @@ exports.stores = stores;
72328
72353
  exports.tokenColors = tokenColors;
72329
72354
  exports.tokenize = tokenize;
72330
72355
 
72331
- __info__.version = "18.3.59";
72332
- __info__.date = "2026-08-10T12:30:17.132Z";
72333
- __info__.hash = "c215f06";
72356
+ __info__.version = "18.3.61";
72357
+ __info__.date = "2026-08-29T09:13:32.113Z";
72358
+ __info__.hash = "c786d2f";
72334
72359
 
72335
72360
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);