@odoo/o-spreadsheet 18.2.4 → 18.2.5

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.2.4
6
- * @date 2025-03-19T08:20:57.717Z
7
- * @hash 958936a
5
+ * @version 18.2.5
6
+ * @date 2025-03-26T12:47:44.113Z
7
+ * @hash 4675edd
8
8
  */
9
9
 
10
10
  'use strict';
@@ -1133,7 +1133,10 @@ function rgbaStringToHex(color) {
1133
1133
  }
1134
1134
  else if (stringVals.length === 4) {
1135
1135
  const alpha = parseFloat(stringVals.pop() || "1");
1136
- alphaHex = Math.round((alpha || 1) * 255);
1136
+ if (isNaN(alpha)) {
1137
+ throw new Error("invalid alpha value");
1138
+ }
1139
+ alphaHex = Math.round(alpha * 255);
1137
1140
  }
1138
1141
  const vals = stringVals.map((val) => parseInt(val, 10));
1139
1142
  if (alphaHex !== 255) {
@@ -7001,7 +7004,7 @@ function isValidLocale(locale) {
7001
7004
  */
7002
7005
  function canonicalizeNumberContent(content, locale) {
7003
7006
  return content.startsWith("=")
7004
- ? canonicalizeFormula$1(content, locale)
7007
+ ? canonicalizeFormula(content, locale)
7005
7008
  : canonicalizeNumberLiteral(content, locale);
7006
7009
  }
7007
7010
  /**
@@ -7016,7 +7019,7 @@ function canonicalizeNumberContent(content, locale) {
7016
7019
  */
7017
7020
  function canonicalizeContent(content, locale) {
7018
7021
  return content.startsWith("=")
7019
- ? canonicalizeFormula$1(content, locale)
7022
+ ? canonicalizeFormula(content, locale)
7020
7023
  : canonicalizeLiteral(content, locale);
7021
7024
  }
7022
7025
  /**
@@ -7032,15 +7035,21 @@ function localizeContent(content, locale) {
7032
7035
  ? localizeFormula(content, locale)
7033
7036
  : localizeLiteral(content, locale);
7034
7037
  }
7038
+ /** Change a number string to its canonical form (en_US locale) */
7039
+ function canonicalizeNumberValue(content, locale) {
7040
+ return content.startsWith("=")
7041
+ ? canonicalizeFormula(content, locale)
7042
+ : canonicalizeNumberLiteral(content, locale);
7043
+ }
7035
7044
  /** Change a formula to its canonical form (en_US locale) */
7036
- function canonicalizeFormula$1(formula, locale) {
7037
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
7045
+ function canonicalizeFormula(formula, locale) {
7046
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7038
7047
  }
7039
7048
  /** Change a formula from the canonical form to the given locale */
7040
7049
  function localizeFormula(formula, locale) {
7041
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
7050
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
7042
7051
  }
7043
- function _localizeFormula$1(formula, fromLocale, toLocale) {
7052
+ function _localizeFormula(formula, fromLocale, toLocale) {
7044
7053
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7045
7054
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7046
7055
  return formula;
@@ -7195,37 +7204,6 @@ function getDateTimeFormat(locale) {
7195
7204
  return locale.dateFormat + " " + locale.timeFormat;
7196
7205
  }
7197
7206
 
7198
- /** Change a number string to its canonical form (en_US locale) */
7199
- function canonicalizeNumberValue(content, locale) {
7200
- return content.startsWith("=")
7201
- ? canonicalizeFormula(content, locale)
7202
- : canonicalizeNumberLiteral(content, locale);
7203
- }
7204
- /** Change a formula to its canonical form (en_US locale) */
7205
- function canonicalizeFormula(formula, locale) {
7206
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7207
- }
7208
- function _localizeFormula(formula, fromLocale, toLocale) {
7209
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7210
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7211
- return formula;
7212
- }
7213
- const tokens = tokenize(formula, fromLocale);
7214
- let localizedFormula = "";
7215
- for (const token of tokens) {
7216
- if (token.type === "NUMBER") {
7217
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7218
- }
7219
- else if (token.type === "ARG_SEPARATOR") {
7220
- localizedFormula += toLocale.formulaArgSeparator;
7221
- }
7222
- else {
7223
- localizedFormula += token.value;
7224
- }
7225
- }
7226
- return localizedFormula;
7227
- }
7228
-
7229
7207
  function boolAnd(args) {
7230
7208
  let foundBoolean = false;
7231
7209
  let acc = true;
@@ -9601,6 +9579,7 @@ class ComposerFocusStore extends SpreadsheetStore {
9601
9579
  }
9602
9580
 
9603
9581
  const TREND_LINE_XAXIS_ID = "x1";
9582
+ const MOVING_AVERAGE_TREND_LINE_XAXIS_ID = "xMovingAverage";
9604
9583
  /**
9605
9584
  * This file contains helpers that are common to different charts (mainly
9606
9585
  * line, bar and pie charts)
@@ -9951,6 +9930,9 @@ function truncateLabel(label) {
9951
9930
  }
9952
9931
  return label;
9953
9932
  }
9933
+ function isTrendLineAxis(axisID) {
9934
+ return axisID === TREND_LINE_XAXIS_ID || axisID === MOVING_AVERAGE_TREND_LINE_XAXIS_ID;
9935
+ }
9954
9936
 
9955
9937
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9956
9938
  const chartShowValuesPlugin = {
@@ -9995,7 +9977,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
9995
9977
  const yMin = chart.chartArea.top;
9996
9978
  const textsPositions = {};
9997
9979
  for (const dataset of chart._metasets) {
9998
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID || dataset.hidden) {
9980
+ if (isTrendLineAxis(dataset.axisID) || dataset.hidden) {
9999
9981
  continue;
10000
9982
  }
10001
9983
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -10038,7 +10020,7 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
10038
10020
  const xMin = chart.chartArea.left;
10039
10021
  const textsPositions = {};
10040
10022
  for (const dataset of chart._metasets) {
10041
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
10023
+ if (isTrendLineAxis(dataset.axisID)) {
10042
10024
  return; // ignore trend lines
10043
10025
  }
10044
10026
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -20546,11 +20528,26 @@ const SEARCH = {
20546
20528
  const _searchFor = toString(searchFor).toLowerCase();
20547
20529
  const _textToSearch = toString(textToSearch).toLowerCase();
20548
20530
  const _startingAt = toNumber(startingAt, this.locale);
20549
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
20550
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
20531
+ if (_textToSearch === "") {
20532
+ return {
20533
+ value: CellErrorType.GenericError,
20534
+ message: _t("The text_to_search must be non-empty."),
20535
+ };
20536
+ }
20537
+ if (_startingAt < 1) {
20538
+ return {
20539
+ value: CellErrorType.GenericError,
20540
+ message: _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt),
20541
+ };
20542
+ }
20551
20543
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
20552
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
20553
- return result + 1;
20544
+ if (result === -1) {
20545
+ return {
20546
+ value: CellErrorType.GenericError,
20547
+ message: _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch),
20548
+ };
20549
+ }
20550
+ return { value: result + 1 };
20554
20551
  },
20555
20552
  isExported: true,
20556
20553
  };
@@ -21885,11 +21882,14 @@ function compileTokens(tokens) {
21885
21882
  }
21886
21883
  }
21887
21884
  function compileTokensOrThrow(tokens) {
21888
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
21889
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
21885
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
21886
+ const cacheKey = compilationCacheKey(tokens);
21890
21887
  if (!functionCache[cacheKey]) {
21891
21888
  const ast = parseTokens([...tokens]);
21892
21889
  const scope = new Scope();
21890
+ let stringCount = 0;
21891
+ let numberCount = 0;
21892
+ let dependencyCount = 0;
21893
21893
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
21894
21894
  throw new BadExpressionError(_t("Invalid formula"));
21895
21895
  }
@@ -21963,16 +21963,15 @@ function compileTokensOrThrow(tokens) {
21963
21963
  case "BOOLEAN":
21964
21964
  return code.return(`{ value: ${ast.value} }`);
21965
21965
  case "NUMBER":
21966
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
21966
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
21967
21967
  case "STRING":
21968
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
21968
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
21969
21969
  case "REFERENCE":
21970
- const referenceIndex = dependencies.indexOf(ast.value);
21971
21970
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
21972
- return code.return(`range(deps[${referenceIndex}])`);
21971
+ return code.return(`range(deps[${dependencyCount++}])`);
21973
21972
  }
21974
21973
  else {
21975
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
21974
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
21976
21975
  }
21977
21976
  case "FUNCALL":
21978
21977
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -22004,7 +22003,7 @@ function compileTokensOrThrow(tokens) {
22004
22003
  const compiledFormula = {
22005
22004
  execute: functionCache[cacheKey],
22006
22005
  dependencies,
22007
- constantValues,
22006
+ literalValues,
22008
22007
  symbols,
22009
22008
  tokens,
22010
22009
  isBadExpression: false,
@@ -22017,33 +22016,31 @@ function compileTokensOrThrow(tokens) {
22017
22016
  * References, numbers and strings are replaced with placeholders because
22018
22017
  * the compiled formula does not depend on their actual value.
22019
22018
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
22020
- *
22021
22019
  * Spaces are also ignored to compute the cache key.
22022
22020
  *
22023
- * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|0|+|1|+SUM(|N0|,|N0|,|S0|)`
22021
+ * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|C|+|C|+SUM(|N|,|N|,|S|)`
22024
22022
  */
22025
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
22023
+ function compilationCacheKey(tokens) {
22026
22024
  let cacheKey = "";
22027
22025
  for (const token of tokens) {
22028
22026
  switch (token.type) {
22029
22027
  case "STRING":
22030
- const value = removeStringQuotes(token.value);
22031
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
22028
+ cacheKey += "|S|";
22032
22029
  break;
22033
22030
  case "NUMBER":
22034
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
22031
+ cacheKey += "|N|";
22035
22032
  break;
22036
22033
  case "REFERENCE":
22037
22034
  case "INVALID_REFERENCE":
22038
22035
  if (token.value.includes(":")) {
22039
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
22036
+ cacheKey += "|R|";
22040
22037
  }
22041
22038
  else {
22042
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
22039
+ cacheKey += "|C|";
22043
22040
  }
22044
22041
  break;
22045
22042
  case "SPACE":
22046
- cacheKey += "";
22043
+ // ignore spaces
22047
22044
  break;
22048
22045
  default:
22049
22046
  cacheKey += token.value;
@@ -22056,7 +22053,7 @@ function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
22056
22053
  * Return formula arguments which are references, strings and numbers.
22057
22054
  */
22058
22055
  function formulaArguments(tokens) {
22059
- const constantValues = {
22056
+ const literalValues = {
22060
22057
  numbers: [],
22061
22058
  strings: [],
22062
22059
  };
@@ -22070,15 +22067,11 @@ function formulaArguments(tokens) {
22070
22067
  break;
22071
22068
  case "STRING":
22072
22069
  const value = removeStringQuotes(token.value);
22073
- if (!constantValues.strings.includes(value)) {
22074
- constantValues.strings.push(value);
22075
- }
22070
+ literalValues.strings.push({ value });
22076
22071
  break;
22077
22072
  case "NUMBER": {
22078
22073
  const value = parseNumber(token.value, DEFAULT_LOCALE);
22079
- if (!constantValues.numbers.includes(value)) {
22080
- constantValues.numbers.push(value);
22081
- }
22074
+ literalValues.numbers.push({ value });
22082
22075
  break;
22083
22076
  }
22084
22077
  case "SYMBOL": {
@@ -22089,7 +22082,7 @@ function formulaArguments(tokens) {
22089
22082
  }
22090
22083
  return {
22091
22084
  dependencies,
22092
- constantValues,
22085
+ literalValues,
22093
22086
  symbols,
22094
22087
  };
22095
22088
  }
@@ -23010,9 +23003,9 @@ const XLSX_CHART_TYPES = [
23010
23003
  /** In XLSX color format (no #) */
23011
23004
  const AUTO_COLOR = "000000";
23012
23005
  const XLSX_ICONSET_MAP = {
23013
- arrow: "3Arrows",
23006
+ arrows: "3Arrows",
23014
23007
  smiley: "3Symbols",
23015
- dot: "3TrafficLights1",
23008
+ dots: "3TrafficLights1",
23016
23009
  };
23017
23010
  const NAMESPACE = {
23018
23011
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -23543,6 +23536,7 @@ const ICON_SET_CONVERSION_MAP = {
23543
23536
  };
23544
23537
  /** Map between legend position in XLSX file and human readable position */
23545
23538
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
23539
+ none: "none",
23546
23540
  b: "bottom",
23547
23541
  t: "top",
23548
23542
  l: "left",
@@ -26305,7 +26299,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26305
26299
  default: "ffffff",
26306
26300
  }).asString(),
26307
26301
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
26308
- default: "b",
26302
+ default: "none",
26309
26303
  }).asString()],
26310
26304
  stacked: barChartGrouping === "stacked",
26311
26305
  fontColor: "000000",
@@ -26339,7 +26333,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26339
26333
  default: "ffffff",
26340
26334
  }).asString(),
26341
26335
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
26342
- default: "b",
26336
+ default: "none",
26343
26337
  }).asString()],
26344
26338
  stacked: barChartGrouping === "stacked",
26345
26339
  fontColor: "000000",
@@ -28951,7 +28945,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
28951
28945
  }
28952
28946
  }
28953
28947
  else if (dataSets.length === 1) {
28954
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
28948
+ const dataLength = getData(getters, dataSets[0]).length;
28949
+ for (let i = 0; i < dataLength; i++) {
28955
28950
  labels.formattedValues.push("");
28956
28951
  labels.values.push("");
28957
28952
  }
@@ -29134,7 +29129,7 @@ function getLineChartDatasets(definition, args) {
29134
29129
  function getScatterChartDatasets(definition, args) {
29135
29130
  const dataSets = getLineChartDatasets(definition, args);
29136
29131
  for (const dataSet of dataSets) {
29137
- if (dataSet.xAxisID !== TREND_LINE_XAXIS_ID) {
29132
+ if (!isTrendLineAxis(dataSet.xAxisID)) {
29138
29133
  dataSet.showLine = false;
29139
29134
  }
29140
29135
  }
@@ -29261,7 +29256,9 @@ function getTrendingLineDataSet(dataset, config, data) {
29261
29256
  const borderColor = config.color || lightenColor(rgbaToHex(defaultBorderColor), 0.5);
29262
29257
  return {
29263
29258
  type: "line",
29264
- xAxisID: TREND_LINE_XAXIS_ID,
29259
+ xAxisID: config.type === "trailingMovingAverage"
29260
+ ? MOVING_AVERAGE_TREND_LINE_XAXIS_ID
29261
+ : TREND_LINE_XAXIS_ID,
29265
29262
  yAxisID: dataset.yAxisID,
29266
29263
  label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
29267
29264
  data,
@@ -29336,22 +29333,19 @@ function getPieChartLegend(definition, args) {
29336
29333
  const { dataSetsValues } = args;
29337
29334
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
29338
29335
  const colors = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
29336
+ const fontColor = chartFontColor(definition.background);
29339
29337
  return {
29340
29338
  ...getLegendDisplayOptions(definition),
29341
29339
  labels: {
29342
- color: chartFontColor(definition.background),
29343
29340
  usePointStyle: true,
29344
- //@ts-ignore
29345
- generateLabels: (c) =>
29346
- //@ts-ignore
29347
- c.data.labels.map((label, index) => ({
29341
+ generateLabels: (c) => c.data.labels?.map((label, index) => ({
29348
29342
  text: truncateLabel(String(label)),
29349
29343
  strokeStyle: colors[index],
29350
29344
  fillStyle: colors[index],
29351
29345
  pointStyle: "rect",
29352
- hidden: false,
29353
29346
  lineWidth: 2,
29354
- })),
29347
+ fontColor,
29348
+ })) || [],
29355
29349
  filter: (legendItem, data) => {
29356
29350
  return "datasetIndex" in legendItem
29357
29351
  ? !data.datasets[legendItem.datasetIndex].hidden
@@ -29484,7 +29478,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29484
29478
  color: fontColor,
29485
29479
  usePointStyle: true,
29486
29480
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29487
- if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29481
+ if (isTrendLineAxis(dataset["xAxisID"])) {
29488
29482
  return {
29489
29483
  text: truncateLabel(dataset.label),
29490
29484
  fontColor,
@@ -29542,6 +29536,11 @@ function getBarChartScales(definition, args) {
29542
29536
  offset: false,
29543
29537
  display: false,
29544
29538
  };
29539
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29540
+ ...scales.x,
29541
+ offset: false,
29542
+ display: false,
29543
+ };
29545
29544
  }
29546
29545
  return scales;
29547
29546
  }
@@ -29575,6 +29574,10 @@ function getLineChartScales(definition, args) {
29575
29574
  ...scales.x,
29576
29575
  display: false,
29577
29576
  };
29577
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29578
+ ...scales.x,
29579
+ display: false,
29580
+ };
29578
29581
  if (axisType === "category" || axisType === "time") {
29579
29582
  /* We add a second x axis here to draw the trend lines, with the labels length being
29580
29583
  * set so that the second axis points match the classical x axis
@@ -29583,6 +29586,8 @@ function getLineChartScales(definition, args) {
29583
29586
  scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29584
29587
  scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29585
29588
  scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29589
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID]["type"] = "category";
29590
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID]["offset"] = false;
29586
29591
  }
29587
29592
  }
29588
29593
  return scales;
@@ -29901,9 +29906,7 @@ function getBarChartTooltip(definition, args) {
29901
29906
  external: customTooltipHandler,
29902
29907
  callbacks: {
29903
29908
  title: function (tooltipItems) {
29904
- return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
29905
- ? undefined
29906
- : "";
29909
+ return tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID)) ? undefined : "";
29907
29910
  },
29908
29911
  beforeLabel: (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label,
29909
29912
  label: function (tooltipItem) {
@@ -29930,7 +29933,7 @@ function getLineChartTooltip(definition, args) {
29930
29933
  if (axisType === "linear") {
29931
29934
  tooltip.callbacks.label = (tooltipItem) => {
29932
29935
  const dataSetPoint = tooltipItem.parsed.y;
29933
- let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29936
+ let label = isTrendLineAxis(tooltipItem.dataset.xAxisID)
29934
29937
  ? ""
29935
29938
  : tooltipItem.parsed.x;
29936
29939
  if (typeof label === "string" && isNumber(label, locale)) {
@@ -29952,8 +29955,7 @@ function getLineChartTooltip(definition, args) {
29952
29955
  }
29953
29956
  tooltip.callbacks.beforeLabel = (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label;
29954
29957
  tooltip.callbacks.title = function (tooltipItems) {
29955
- const displayTooltipTitle = axisType !== "linear" &&
29956
- tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
29958
+ const displayTooltipTitle = axisType !== "linear" && tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID));
29957
29959
  return displayTooltipTitle ? undefined : "";
29958
29960
  };
29959
29961
  return tooltip;
@@ -34207,6 +34209,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34207
34209
  CHART_COMMON_OPTIONS: CHART_COMMON_OPTIONS,
34208
34210
  GaugeChart: GaugeChart,
34209
34211
  LineChart: LineChart,
34212
+ MOVING_AVERAGE_TREND_LINE_XAXIS_ID: MOVING_AVERAGE_TREND_LINE_XAXIS_ID,
34210
34213
  PieChart: PieChart,
34211
34214
  ScorecardChart: ScorecardChart$1,
34212
34215
  TREND_LINE_XAXIS_ID: TREND_LINE_XAXIS_ID,
@@ -34236,6 +34239,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34236
34239
  getDefinedAxis: getDefinedAxis,
34237
34240
  getPieColors: getPieColors,
34238
34241
  getSmartChartDefinition: getSmartChartDefinition,
34242
+ isTrendLineAxis: isTrendLineAxis,
34239
34243
  shouldRemoveFirstLabel: shouldRemoveFirstLabel,
34240
34244
  toExcelDataset: toExcelDataset,
34241
34245
  toExcelLabelRange: toExcelLabelRange,
@@ -36283,9 +36287,7 @@ class FormulaFingerprintStore extends SpreadsheetStore {
36283
36287
  }
36284
36288
  }
36285
36289
  }
36286
- // removes the index placeholders from the normalized formula
36287
- // =|N0|+|N1|+|N0| -> =|N|+|N|+|N|
36288
- const normalizedFormula = cell.compiledFormula.normalizedFormula.replace(/(|\w)(\d)(|)/g, "$1$3");
36290
+ const normalizedFormula = cell.compiledFormula.normalizedFormula;
36289
36291
  return hash(fingerprintVector) + normalizedFormula;
36290
36292
  }
36291
36293
  getLiteralFingerprint(position) {
@@ -39630,9 +39632,11 @@ class SeriesDesignEditor extends owl.Component {
39630
39632
  if (!runtime || !("chartJsConfig" in runtime)) {
39631
39633
  return [];
39632
39634
  }
39633
- return runtime.chartJsConfig.data.datasets.map((d) => d.label);
39635
+ return runtime.chartJsConfig.data.datasets
39636
+ .filter((d) => !isTrendLineAxis(d["xAxisID"] ?? ""))
39637
+ .map((d) => d.label);
39634
39638
  }
39635
- updateSerieEditor(ev) {
39639
+ updateEditedSeries(ev) {
39636
39640
  this.state.index = ev.target.selectedIndex;
39637
39641
  }
39638
39642
  updateDataSeriesColor(color) {
@@ -39645,7 +39649,7 @@ class SeriesDesignEditor extends owl.Component {
39645
39649
  };
39646
39650
  this.props.updateChart(this.props.figureId, { dataSets });
39647
39651
  }
39648
- getDataSerieColor() {
39652
+ getDataSeriesColor() {
39649
39653
  const dataSets = this.props.definition.dataSets;
39650
39654
  if (!dataSets?.[this.state.index])
39651
39655
  return "";
@@ -39665,7 +39669,7 @@ class SeriesDesignEditor extends owl.Component {
39665
39669
  };
39666
39670
  this.props.updateChart(this.props.figureId, { dataSets });
39667
39671
  }
39668
- getDataSerieLabel() {
39672
+ getDataSeriesLabel() {
39669
39673
  const dataSets = this.props.definition.dataSets;
39670
39674
  return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
39671
39675
  }
@@ -39778,7 +39782,7 @@ class SeriesWithAxisDesignEditor extends owl.Component {
39778
39782
  }
39779
39783
  this.updateTrendLineValue(index, { window });
39780
39784
  }
39781
- getDataSerieColor(index) {
39785
+ getDataSeriesColor(index) {
39782
39786
  const dataSets = this.props.definition.dataSets;
39783
39787
  if (!dataSets?.[index])
39784
39788
  return "";
@@ -39789,7 +39793,7 @@ class SeriesWithAxisDesignEditor extends owl.Component {
39789
39793
  }
39790
39794
  getTrendLineColor(index) {
39791
39795
  return (this.getTrendLineConfiguration(index)?.color ??
39792
- setColorAlpha(this.getDataSerieColor(index), 0.5));
39796
+ setColorAlpha(this.getDataSeriesColor(index), 0.5));
39793
39797
  }
39794
39798
  updateTrendLineColor(index, color) {
39795
39799
  this.updateTrendLineValue(index, { color });
@@ -70769,7 +70773,9 @@ css /* scss */ `
70769
70773
  border: 1px solid;
70770
70774
  font-family: ${DEFAULT_FONT};
70771
70775
 
70772
- .o-composer:empty:not(:focus):not(.active)::before {
70776
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
70777
+ .o-composer:empty:not(:focus):not(.active)::before,
70778
+ &.o-topbar-composer-readonly .o-composer:empty::before {
70773
70779
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
70774
70780
  position: relative;
70775
70781
  top: 20%;
@@ -74095,10 +74101,14 @@ function addIconSetRule(cf, rule) {
74095
74101
  continue;
74096
74102
  }
74097
74103
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
74104
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
74105
+ if (isIconSetReversed(rule.icons)) {
74106
+ iconSetAttrs.push(["reverse", "1"]);
74107
+ }
74098
74108
  conditionalFormats.push(escapeXml /*xml*/ `
74099
74109
  <conditionalFormatting sqref="${range}">
74100
74110
  <cfRule ${formatAttributes(ruleAttributes)}>
74101
- <iconSet iconSet="${getIconSet(rule.icons)}">
74111
+ <iconSet ${formatAttributes(iconSetAttrs)}>
74102
74112
  ${joinXmlNodes(cfValueObjectNodes)}
74103
74113
  </iconSet>
74104
74114
  </cfRule>
@@ -74116,9 +74126,21 @@ function commonCfAttributes(cf) {
74116
74126
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
74117
74127
  ];
74118
74128
  }
74129
+ function isIconSetReversed(iconSet) {
74130
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
74131
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
74132
+ }
74119
74133
  function getIconSet(iconSet) {
74120
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
74121
- "dots"];
74134
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
74135
+ }
74136
+ /**
74137
+ * Partial detection based on "upper" point only.
74138
+ * We support any arbitrary icon in the set, while excel doesn't allow
74139
+ * mixing icons from different types.
74140
+ */
74141
+ function detectIconsType(iconSet) {
74142
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
74143
+ return type;
74122
74144
  }
74123
74145
  function thresholdAttributes(threshold, position) {
74124
74146
  const type = getExcelThresholdType(threshold.type, position);
@@ -76068,6 +76090,6 @@ exports.tokenColors = tokenColors;
76068
76090
  exports.tokenize = tokenize;
76069
76091
 
76070
76092
 
76071
- __info__.version = "18.2.4";
76072
- __info__.date = "2025-03-19T08:20:57.717Z";
76073
- __info__.hash = "958936a";
76093
+ __info__.version = "18.2.5";
76094
+ __info__.date = "2025-03-26T12:47:44.113Z";
76095
+ __info__.hash = "4675edd";
@@ -7756,11 +7756,11 @@ declare class SeriesDesignEditor extends Component<Props$Y, SpreadsheetChildEnv>
7756
7756
  index: number;
7757
7757
  };
7758
7758
  getDataSeries(): (string | undefined)[];
7759
- updateSerieEditor(ev: any): void;
7759
+ updateEditedSeries(ev: Event): void;
7760
7760
  updateDataSeriesColor(color: string): void;
7761
- getDataSerieColor(): "" | Color;
7762
- updateDataSeriesLabel(ev: any): void;
7763
- getDataSerieLabel(): string | undefined;
7761
+ getDataSeriesColor(): "" | Color;
7762
+ updateDataSeriesLabel(ev: Event): void;
7763
+ getDataSeriesLabel(): string | undefined;
7764
7764
  }
7765
7765
 
7766
7766
  interface Props$X {
@@ -7804,7 +7804,7 @@ declare class SeriesWithAxisDesignEditor extends Component<Props$X, SpreadsheetC
7804
7804
  getMaxPolynomialDegree(index: any): number;
7805
7805
  get defaultWindowSize(): number;
7806
7806
  onChangeMovingAverageWindow(index: number, ev: InputEvent): void;
7807
- getDataSerieColor(index: number): "" | Color;
7807
+ getDataSeriesColor(index: number): "" | Color;
7808
7808
  getTrendLineColor(index: number): string;
7809
7809
  updateTrendLineColor(index: number, color: Color): void;
7810
7810
  updateTrendLineValue(index: number, config: any): void;
@@ -13317,7 +13317,9 @@ declare const chartHelpers: {
13317
13317
  formatTickValue(localeFormat: LocaleFormat): (value: any) => any;
13318
13318
  getPieColors(colors: ColorGenerator, dataSetsValues: DatasetValues[]): Color[];
13319
13319
  truncateLabel(label: string | undefined): string;
13320
+ isTrendLineAxis(axisID: string): boolean;
13320
13321
  TREND_LINE_XAXIS_ID: "x1";
13322
+ MOVING_AVERAGE_TREND_LINE_XAXIS_ID: "xMovingAverage";
13321
13323
  CHART_AXIS_CHOICES: {
13322
13324
  value: string;
13323
13325
  label: string;