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