@odoo/o-spreadsheet 18.0.20 → 18.0.21

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.20
6
- * @date 2025-03-19T08:21:32.426Z
7
- * @hash 3f48d8b
5
+ * @version 18.0.21
6
+ * @date 2025-03-26T12:49:46.872Z
7
+ * @hash c687e1c
8
8
  */
9
9
 
10
10
  'use strict';
@@ -1113,7 +1113,10 @@ function rgbaStringToHex(color) {
1113
1113
  }
1114
1114
  else if (stringVals.length === 4) {
1115
1115
  const alpha = parseFloat(stringVals.pop() || "1");
1116
- alphaHex = Math.round((alpha || 1) * 255);
1116
+ if (isNaN(alpha)) {
1117
+ throw new Error("invalid alpha value");
1118
+ }
1119
+ alphaHex = Math.round(alpha * 255);
1117
1120
  }
1118
1121
  const vals = stringVals.map((val) => parseInt(val, 10));
1119
1122
  if (alphaHex !== 255) {
@@ -6837,7 +6840,7 @@ function isValidLocale(locale) {
6837
6840
  */
6838
6841
  function canonicalizeNumberContent(content, locale) {
6839
6842
  return content.startsWith("=")
6840
- ? canonicalizeFormula$1(content, locale)
6843
+ ? canonicalizeFormula(content, locale)
6841
6844
  : canonicalizeNumberLiteral(content, locale);
6842
6845
  }
6843
6846
  /**
@@ -6852,7 +6855,7 @@ function canonicalizeNumberContent(content, locale) {
6852
6855
  */
6853
6856
  function canonicalizeContent(content, locale) {
6854
6857
  return content.startsWith("=")
6855
- ? canonicalizeFormula$1(content, locale)
6858
+ ? canonicalizeFormula(content, locale)
6856
6859
  : canonicalizeLiteral(content, locale);
6857
6860
  }
6858
6861
  /**
@@ -6868,15 +6871,21 @@ function localizeContent(content, locale) {
6868
6871
  ? localizeFormula(content, locale)
6869
6872
  : localizeLiteral(content, locale);
6870
6873
  }
6874
+ /** Change a number string to its canonical form (en_US locale) */
6875
+ function canonicalizeNumberValue(content, locale) {
6876
+ return content.startsWith("=")
6877
+ ? canonicalizeFormula(content, locale)
6878
+ : canonicalizeNumberLiteral(content, locale);
6879
+ }
6871
6880
  /** Change a formula to its canonical form (en_US locale) */
6872
- function canonicalizeFormula$1(formula, locale) {
6873
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
6881
+ function canonicalizeFormula(formula, locale) {
6882
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
6874
6883
  }
6875
6884
  /** Change a formula from the canonical form to the given locale */
6876
6885
  function localizeFormula(formula, locale) {
6877
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
6886
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
6878
6887
  }
6879
- function _localizeFormula$1(formula, fromLocale, toLocale) {
6888
+ function _localizeFormula(formula, fromLocale, toLocale) {
6880
6889
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
6881
6890
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
6882
6891
  return formula;
@@ -7032,37 +7041,6 @@ function getDateTimeFormat(locale) {
7032
7041
  return locale.dateFormat + " " + locale.timeFormat;
7033
7042
  }
7034
7043
 
7035
- /** Change a number string to its canonical form (en_US locale) */
7036
- function canonicalizeNumberValue(content, locale) {
7037
- return content.startsWith("=")
7038
- ? canonicalizeFormula(content, locale)
7039
- : canonicalizeNumberLiteral(content, locale);
7040
- }
7041
- /** Change a formula to its canonical form (en_US locale) */
7042
- function canonicalizeFormula(formula, locale) {
7043
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7044
- }
7045
- function _localizeFormula(formula, fromLocale, toLocale) {
7046
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7047
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7048
- return formula;
7049
- }
7050
- const tokens = tokenize(formula, fromLocale);
7051
- let localizedFormula = "";
7052
- for (const token of tokens) {
7053
- if (token.type === "NUMBER") {
7054
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7055
- }
7056
- else if (token.type === "ARG_SEPARATOR") {
7057
- localizedFormula += toLocale.formulaArgSeparator;
7058
- }
7059
- else {
7060
- localizedFormula += token.value;
7061
- }
7062
- }
7063
- return localizedFormula;
7064
- }
7065
-
7066
7044
  function boolAnd(args) {
7067
7045
  let foundBoolean = false;
7068
7046
  let acc = true;
@@ -10073,9 +10051,9 @@ const XLSX_CHART_TYPES = [
10073
10051
  /** In XLSX color format (no #) */
10074
10052
  const AUTO_COLOR = "000000";
10075
10053
  const XLSX_ICONSET_MAP = {
10076
- arrow: "3Arrows",
10054
+ arrows: "3Arrows",
10077
10055
  smiley: "3Symbols",
10078
- dot: "3TrafficLights1",
10056
+ dots: "3TrafficLights1",
10079
10057
  };
10080
10058
  const NAMESPACE = {
10081
10059
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -10747,6 +10725,7 @@ const ICON_SET_CONVERSION_MAP = {
10747
10725
  };
10748
10726
  /** Map between legend position in XLSX file and human readable position */
10749
10727
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
10728
+ none: "none",
10750
10729
  b: "bottom",
10751
10730
  t: "top",
10752
10731
  l: "left",
@@ -13503,7 +13482,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
13503
13482
  default: "ffffff",
13504
13483
  }).asString(),
13505
13484
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
13506
- default: "b",
13485
+ default: "none",
13507
13486
  }).asString()],
13508
13487
  stacked: barChartGrouping === "stacked",
13509
13488
  fontColor: "000000",
@@ -13537,7 +13516,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
13537
13516
  default: "ffffff",
13538
13517
  }).asString(),
13539
13518
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
13540
- default: "b",
13519
+ default: "none",
13541
13520
  }).asString()],
13542
13521
  stacked: barChartGrouping === "stacked",
13543
13522
  fontColor: "000000",
@@ -16729,7 +16708,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
16729
16708
  }
16730
16709
  }
16731
16710
  else if (dataSets.length === 1) {
16732
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
16711
+ const dataLength = getData(getters, dataSets[0]).length;
16712
+ for (let i = 0; i < dataLength; i++) {
16733
16713
  labels.formattedValues.push("");
16734
16714
  labels.values.push("");
16735
16715
  }
@@ -25975,11 +25955,26 @@ const SEARCH = {
25975
25955
  const _searchFor = toString(searchFor).toLowerCase();
25976
25956
  const _textToSearch = toString(textToSearch).toLowerCase();
25977
25957
  const _startingAt = toNumber(startingAt, this.locale);
25978
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
25979
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
25958
+ if (_textToSearch === "") {
25959
+ return {
25960
+ value: CellErrorType.GenericError,
25961
+ message: _t("The text_to_search must be non-empty."),
25962
+ };
25963
+ }
25964
+ if (_startingAt < 1) {
25965
+ return {
25966
+ value: CellErrorType.GenericError,
25967
+ message: _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt),
25968
+ };
25969
+ }
25980
25970
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
25981
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
25982
- return result + 1;
25971
+ if (result === -1) {
25972
+ return {
25973
+ value: CellErrorType.GenericError,
25974
+ message: _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch),
25975
+ };
25976
+ }
25977
+ return { value: result + 1 };
25983
25978
  },
25984
25979
  isExported: true,
25985
25980
  };
@@ -27829,11 +27824,14 @@ function compileTokens(tokens) {
27829
27824
  }
27830
27825
  }
27831
27826
  function compileTokensOrThrow(tokens) {
27832
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
27833
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
27827
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
27828
+ const cacheKey = compilationCacheKey(tokens);
27834
27829
  if (!functionCache[cacheKey]) {
27835
27830
  const ast = parseTokens([...tokens]);
27836
27831
  const scope = new Scope();
27832
+ let stringCount = 0;
27833
+ let numberCount = 0;
27834
+ let dependencyCount = 0;
27837
27835
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
27838
27836
  throw new BadExpressionError(_t("Invalid formula"));
27839
27837
  }
@@ -27906,16 +27904,15 @@ function compileTokensOrThrow(tokens) {
27906
27904
  case "BOOLEAN":
27907
27905
  return code.return(`{ value: ${ast.value} }`);
27908
27906
  case "NUMBER":
27909
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
27907
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
27910
27908
  case "STRING":
27911
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
27909
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
27912
27910
  case "REFERENCE":
27913
- const referenceIndex = dependencies.indexOf(ast.value);
27914
27911
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
27915
- return code.return(`range(deps[${referenceIndex}])`);
27912
+ return code.return(`range(deps[${dependencyCount++}])`);
27916
27913
  }
27917
27914
  else {
27918
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
27915
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
27919
27916
  }
27920
27917
  case "FUNCALL":
27921
27918
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -27947,7 +27944,7 @@ function compileTokensOrThrow(tokens) {
27947
27944
  const compiledFormula = {
27948
27945
  execute: functionCache[cacheKey],
27949
27946
  dependencies,
27950
- constantValues,
27947
+ literalValues,
27951
27948
  symbols,
27952
27949
  tokens,
27953
27950
  isBadExpression: false,
@@ -27959,33 +27956,31 @@ function compileTokensOrThrow(tokens) {
27959
27956
  * References, numbers and strings are replaced with placeholders because
27960
27957
  * the compiled formula does not depend on their actual value.
27961
27958
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
27962
- *
27963
27959
  * Spaces are also ignored to compute the cache key.
27964
27960
  *
27965
- * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|0|+|1|+SUM(|N0|,|N0|,|S0|)`
27961
+ * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|C|+|C|+SUM(|N|,|N|,|S|)`
27966
27962
  */
27967
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
27963
+ function compilationCacheKey(tokens) {
27968
27964
  let cacheKey = "";
27969
27965
  for (const token of tokens) {
27970
27966
  switch (token.type) {
27971
27967
  case "STRING":
27972
- const value = removeStringQuotes(token.value);
27973
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
27968
+ cacheKey += "|S|";
27974
27969
  break;
27975
27970
  case "NUMBER":
27976
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
27971
+ cacheKey += "|N|";
27977
27972
  break;
27978
27973
  case "REFERENCE":
27979
27974
  case "INVALID_REFERENCE":
27980
27975
  if (token.value.includes(":")) {
27981
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
27976
+ cacheKey += "|R|";
27982
27977
  }
27983
27978
  else {
27984
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
27979
+ cacheKey += "|C|";
27985
27980
  }
27986
27981
  break;
27987
27982
  case "SPACE":
27988
- cacheKey += "";
27983
+ // ignore spaces
27989
27984
  break;
27990
27985
  default:
27991
27986
  cacheKey += token.value;
@@ -27998,7 +27993,7 @@ function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
27998
27993
  * Return formula arguments which are references, strings and numbers.
27999
27994
  */
28000
27995
  function formulaArguments(tokens) {
28001
- const constantValues = {
27996
+ const literalValues = {
28002
27997
  numbers: [],
28003
27998
  strings: [],
28004
27999
  };
@@ -28012,15 +28007,11 @@ function formulaArguments(tokens) {
28012
28007
  break;
28013
28008
  case "STRING":
28014
28009
  const value = removeStringQuotes(token.value);
28015
- if (!constantValues.strings.includes(value)) {
28016
- constantValues.strings.push(value);
28017
- }
28010
+ literalValues.strings.push({ value });
28018
28011
  break;
28019
28012
  case "NUMBER": {
28020
28013
  const value = parseNumber(token.value, DEFAULT_LOCALE);
28021
- if (!constantValues.numbers.includes(value)) {
28022
- constantValues.numbers.push(value);
28023
- }
28014
+ literalValues.numbers.push({ value });
28024
28015
  break;
28025
28016
  }
28026
28017
  case "SYMBOL": {
@@ -28031,7 +28022,7 @@ function formulaArguments(tokens) {
28031
28022
  }
28032
28023
  return {
28033
28024
  dependencies,
28034
- constantValues,
28025
+ literalValues,
28035
28026
  symbols,
28036
28027
  };
28037
28028
  }
@@ -68299,7 +68290,9 @@ css /* scss */ `
68299
68290
  border: 1px solid;
68300
68291
  font-family: ${DEFAULT_FONT};
68301
68292
 
68302
- .o-composer:empty:not(:focus):not(.active)::before {
68293
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
68294
+ .o-composer:empty:not(:focus):not(.active)::before,
68295
+ &.o-topbar-composer-readonly .o-composer:empty::before {
68303
68296
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
68304
68297
  position: relative;
68305
68298
  top: 20%;
@@ -71660,10 +71653,14 @@ function addIconSetRule(cf, rule) {
71660
71653
  continue;
71661
71654
  }
71662
71655
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
71656
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
71657
+ if (isIconSetReversed(rule.icons)) {
71658
+ iconSetAttrs.push(["reverse", "1"]);
71659
+ }
71663
71660
  conditionalFormats.push(escapeXml /*xml*/ `
71664
71661
  <conditionalFormatting sqref="${range}">
71665
71662
  <cfRule ${formatAttributes(ruleAttributes)}>
71666
- <iconSet iconSet="${getIconSet(rule.icons)}">
71663
+ <iconSet ${formatAttributes(iconSetAttrs)}>
71667
71664
  ${joinXmlNodes(cfValueObjectNodes)}
71668
71665
  </iconSet>
71669
71666
  </cfRule>
@@ -71681,9 +71678,21 @@ function commonCfAttributes(cf) {
71681
71678
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
71682
71679
  ];
71683
71680
  }
71681
+ function isIconSetReversed(iconSet) {
71682
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
71683
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
71684
+ }
71684
71685
  function getIconSet(iconSet) {
71685
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
71686
- "dots"];
71686
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
71687
+ }
71688
+ /**
71689
+ * Partial detection based on "upper" point only.
71690
+ * We support any arbitrary icon in the set, while excel doesn't allow
71691
+ * mixing icons from different types.
71692
+ */
71693
+ function detectIconsType(iconSet) {
71694
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
71695
+ return type;
71687
71696
  }
71688
71697
  function thresholdAttributes(threshold, position) {
71689
71698
  const type = getExcelThresholdType(threshold.type, position);
@@ -73607,6 +73616,6 @@ exports.tokenColors = tokenColors;
73607
73616
  exports.tokenize = tokenize;
73608
73617
 
73609
73618
 
73610
- __info__.version = "18.0.20";
73611
- __info__.date = "2025-03-19T08:21:32.426Z";
73612
- __info__.hash = "3f48d8b";
73619
+ __info__.version = "18.0.21";
73620
+ __info__.date = "2025-03-26T12:49:46.872Z";
73621
+ __info__.hash = "c687e1c";