@odoo/o-spreadsheet 18.2.3 → 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.3
6
- * @date 2025-03-12T15:32:36.274Z
7
- * @hash 81b0e08
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) {
@@ -6287,11 +6290,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
6287
6290
  if (!cell || (!cell.isFormula && !cell.content)) {
6288
6291
  return DEFAULT_CELL_HEIGHT;
6289
6292
  }
6290
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6291
- const numberOfLines = cell.isFormula
6292
- ? 1
6293
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
6294
- const fontSize = computeTextFontSizeInPixels(cell.style);
6293
+ const content = cell.isFormula ? "" : cell.content;
6294
+ return getCellContentHeight(ctx, content, cell.style, colSize);
6295
+ }
6296
+ function getCellContentHeight(ctx, content, style, colSize) {
6297
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6298
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
6299
+ const fontSize = computeTextFontSizeInPixels(style);
6295
6300
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
6296
6301
  }
6297
6302
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
@@ -6999,7 +7004,7 @@ function isValidLocale(locale) {
6999
7004
  */
7000
7005
  function canonicalizeNumberContent(content, locale) {
7001
7006
  return content.startsWith("=")
7002
- ? canonicalizeFormula$1(content, locale)
7007
+ ? canonicalizeFormula(content, locale)
7003
7008
  : canonicalizeNumberLiteral(content, locale);
7004
7009
  }
7005
7010
  /**
@@ -7014,7 +7019,7 @@ function canonicalizeNumberContent(content, locale) {
7014
7019
  */
7015
7020
  function canonicalizeContent(content, locale) {
7016
7021
  return content.startsWith("=")
7017
- ? canonicalizeFormula$1(content, locale)
7022
+ ? canonicalizeFormula(content, locale)
7018
7023
  : canonicalizeLiteral(content, locale);
7019
7024
  }
7020
7025
  /**
@@ -7030,15 +7035,21 @@ function localizeContent(content, locale) {
7030
7035
  ? localizeFormula(content, locale)
7031
7036
  : localizeLiteral(content, locale);
7032
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
+ }
7033
7044
  /** Change a formula to its canonical form (en_US locale) */
7034
- function canonicalizeFormula$1(formula, locale) {
7035
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
7045
+ function canonicalizeFormula(formula, locale) {
7046
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7036
7047
  }
7037
7048
  /** Change a formula from the canonical form to the given locale */
7038
7049
  function localizeFormula(formula, locale) {
7039
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
7050
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
7040
7051
  }
7041
- function _localizeFormula$1(formula, fromLocale, toLocale) {
7052
+ function _localizeFormula(formula, fromLocale, toLocale) {
7042
7053
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7043
7054
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7044
7055
  return formula;
@@ -7193,37 +7204,6 @@ function getDateTimeFormat(locale) {
7193
7204
  return locale.dateFormat + " " + locale.timeFormat;
7194
7205
  }
7195
7206
 
7196
- /** Change a number string to its canonical form (en_US locale) */
7197
- function canonicalizeNumberValue(content, locale) {
7198
- return content.startsWith("=")
7199
- ? canonicalizeFormula(content, locale)
7200
- : canonicalizeNumberLiteral(content, locale);
7201
- }
7202
- /** Change a formula to its canonical form (en_US locale) */
7203
- function canonicalizeFormula(formula, locale) {
7204
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7205
- }
7206
- function _localizeFormula(formula, fromLocale, toLocale) {
7207
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7208
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7209
- return formula;
7210
- }
7211
- const tokens = tokenize(formula, fromLocale);
7212
- let localizedFormula = "";
7213
- for (const token of tokens) {
7214
- if (token.type === "NUMBER") {
7215
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7216
- }
7217
- else if (token.type === "ARG_SEPARATOR") {
7218
- localizedFormula += toLocale.formulaArgSeparator;
7219
- }
7220
- else {
7221
- localizedFormula += token.value;
7222
- }
7223
- }
7224
- return localizedFormula;
7225
- }
7226
-
7227
7207
  function boolAnd(args) {
7228
7208
  let foundBoolean = false;
7229
7209
  let acc = true;
@@ -8488,13 +8468,6 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8488
8468
  this.clearClippedZones(content);
8489
8469
  const selection = target[0];
8490
8470
  this.pasteZone(sheetId, selection.left, selection.top, content.cells, options);
8491
- this.dispatch("MOVE_RANGES", {
8492
- target: content.zones,
8493
- sheetId: content.sheetId,
8494
- targetSheetId: sheetId,
8495
- col: selection.left,
8496
- row: selection.top,
8497
- });
8498
8471
  }
8499
8472
  /**
8500
8473
  * Clear the clipped zones: remove the cells and clear the formatting
@@ -9003,14 +8976,15 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
9003
8976
  }
9004
8977
  merges.push(mergesInRow);
9005
8978
  }
9006
- return { merges };
8979
+ return { merges, sheetId };
9007
8980
  }
9008
8981
  /**
9009
8982
  * Paste the clipboard content in the given target
9010
8983
  */
9011
8984
  paste(target, content, options) {
9012
8985
  if (options.isCutOperation) {
9013
- return;
8986
+ const copiedMerges = content.merges.flat().filter(isDefined);
8987
+ this.dispatch("REMOVE_MERGE", { sheetId: content.sheetId, target: copiedMerges });
9014
8988
  }
9015
8989
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
9016
8990
  }
@@ -9045,6 +9019,27 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
9045
9019
  }
9046
9020
  }
9047
9021
 
9022
+ class ReferenceClipboardHandler extends AbstractCellClipboardHandler {
9023
+ copy(data) {
9024
+ return {
9025
+ zones: data.clippedZones,
9026
+ sheetId: data.sheetId,
9027
+ };
9028
+ }
9029
+ paste(target, content, options) {
9030
+ if (options.isCutOperation) {
9031
+ const selection = target.zones[0];
9032
+ this.dispatch("MOVE_RANGES", {
9033
+ target: content.zones,
9034
+ sheetId: content.sheetId,
9035
+ targetSheetId: target.sheetId,
9036
+ col: selection.left,
9037
+ row: selection.top,
9038
+ });
9039
+ }
9040
+ }
9041
+ }
9042
+
9048
9043
  class SheetClipboardHandler extends AbstractCellClipboardHandler {
9049
9044
  isPasteAllowed(sheetId, target, content, options) {
9050
9045
  if (!("cells" in content)) {
@@ -9208,7 +9203,8 @@ clipboardHandlersRegistries.cellHandlers
9208
9203
  .add("merge", MergeClipboardHandler)
9209
9204
  .add("border", BorderClipboardHandler)
9210
9205
  .add("table", TableClipboardHandler)
9211
- .add("conditionalFormat", ConditionalFormatClipboardHandler);
9206
+ .add("conditionalFormat", ConditionalFormatClipboardHandler)
9207
+ .add("references", ReferenceClipboardHandler);
9212
9208
 
9213
9209
  function transformZone(zone, executed) {
9214
9210
  if (executed.type === "REMOVE_COLUMNS_ROWS") {
@@ -9583,6 +9579,7 @@ class ComposerFocusStore extends SpreadsheetStore {
9583
9579
  }
9584
9580
 
9585
9581
  const TREND_LINE_XAXIS_ID = "x1";
9582
+ const MOVING_AVERAGE_TREND_LINE_XAXIS_ID = "xMovingAverage";
9586
9583
  /**
9587
9584
  * This file contains helpers that are common to different charts (mainly
9588
9585
  * line, bar and pie charts)
@@ -9933,6 +9930,9 @@ function truncateLabel(label) {
9933
9930
  }
9934
9931
  return label;
9935
9932
  }
9933
+ function isTrendLineAxis(axisID) {
9934
+ return axisID === TREND_LINE_XAXIS_ID || axisID === MOVING_AVERAGE_TREND_LINE_XAXIS_ID;
9935
+ }
9936
9936
 
9937
9937
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9938
9938
  const chartShowValuesPlugin = {
@@ -9977,7 +9977,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
9977
9977
  const yMin = chart.chartArea.top;
9978
9978
  const textsPositions = {};
9979
9979
  for (const dataset of chart._metasets) {
9980
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID || dataset.hidden) {
9980
+ if (isTrendLineAxis(dataset.axisID) || dataset.hidden) {
9981
9981
  continue;
9982
9982
  }
9983
9983
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -10020,7 +10020,7 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
10020
10020
  const xMin = chart.chartArea.left;
10021
10021
  const textsPositions = {};
10022
10022
  for (const dataset of chart._metasets) {
10023
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
10023
+ if (isTrendLineAxis(dataset.axisID)) {
10024
10024
  return; // ignore trend lines
10025
10025
  }
10026
10026
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -20528,11 +20528,26 @@ const SEARCH = {
20528
20528
  const _searchFor = toString(searchFor).toLowerCase();
20529
20529
  const _textToSearch = toString(textToSearch).toLowerCase();
20530
20530
  const _startingAt = toNumber(startingAt, this.locale);
20531
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
20532
- 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
+ }
20533
20543
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
20534
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
20535
- 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 };
20536
20551
  },
20537
20552
  isExported: true,
20538
20553
  };
@@ -21159,8 +21174,8 @@ class AbstractComposerStore extends SpreadsheetStore {
21159
21174
  this.computeParenthesisRelatedToCursor();
21160
21175
  }
21161
21176
  cancelEdition() {
21162
- this.cancelEditionAndActivateSheet();
21163
21177
  this.resetContent();
21178
+ this.cancelEditionAndActivateSheet();
21164
21179
  }
21165
21180
  setCurrentContent(content, selection) {
21166
21181
  if (selection && !this.isSelectionValid(content.length, selection.start, selection.end)) {
@@ -21178,8 +21193,8 @@ class AbstractComposerStore extends SpreadsheetStore {
21178
21193
  switch (cmd.type) {
21179
21194
  case "SELECT_FIGURE":
21180
21195
  if (cmd.id) {
21181
- this.cancelEditionAndActivateSheet();
21182
21196
  this.resetContent();
21197
+ this.cancelEditionAndActivateSheet();
21183
21198
  }
21184
21199
  break;
21185
21200
  case "START_CHANGE_HIGHLIGHT":
@@ -21867,11 +21882,14 @@ function compileTokens(tokens) {
21867
21882
  }
21868
21883
  }
21869
21884
  function compileTokensOrThrow(tokens) {
21870
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
21871
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
21885
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
21886
+ const cacheKey = compilationCacheKey(tokens);
21872
21887
  if (!functionCache[cacheKey]) {
21873
21888
  const ast = parseTokens([...tokens]);
21874
21889
  const scope = new Scope();
21890
+ let stringCount = 0;
21891
+ let numberCount = 0;
21892
+ let dependencyCount = 0;
21875
21893
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
21876
21894
  throw new BadExpressionError(_t("Invalid formula"));
21877
21895
  }
@@ -21945,16 +21963,15 @@ function compileTokensOrThrow(tokens) {
21945
21963
  case "BOOLEAN":
21946
21964
  return code.return(`{ value: ${ast.value} }`);
21947
21965
  case "NUMBER":
21948
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
21966
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
21949
21967
  case "STRING":
21950
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
21968
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
21951
21969
  case "REFERENCE":
21952
- const referenceIndex = dependencies.indexOf(ast.value);
21953
21970
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
21954
- return code.return(`range(deps[${referenceIndex}])`);
21971
+ return code.return(`range(deps[${dependencyCount++}])`);
21955
21972
  }
21956
21973
  else {
21957
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
21974
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
21958
21975
  }
21959
21976
  case "FUNCALL":
21960
21977
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -21986,7 +22003,7 @@ function compileTokensOrThrow(tokens) {
21986
22003
  const compiledFormula = {
21987
22004
  execute: functionCache[cacheKey],
21988
22005
  dependencies,
21989
- constantValues,
22006
+ literalValues,
21990
22007
  symbols,
21991
22008
  tokens,
21992
22009
  isBadExpression: false,
@@ -21999,33 +22016,31 @@ function compileTokensOrThrow(tokens) {
21999
22016
  * References, numbers and strings are replaced with placeholders because
22000
22017
  * the compiled formula does not depend on their actual value.
22001
22018
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
22002
- *
22003
22019
  * Spaces are also ignored to compute the cache key.
22004
22020
  *
22005
- * 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|)`
22006
22022
  */
22007
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
22023
+ function compilationCacheKey(tokens) {
22008
22024
  let cacheKey = "";
22009
22025
  for (const token of tokens) {
22010
22026
  switch (token.type) {
22011
22027
  case "STRING":
22012
- const value = removeStringQuotes(token.value);
22013
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
22028
+ cacheKey += "|S|";
22014
22029
  break;
22015
22030
  case "NUMBER":
22016
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
22031
+ cacheKey += "|N|";
22017
22032
  break;
22018
22033
  case "REFERENCE":
22019
22034
  case "INVALID_REFERENCE":
22020
22035
  if (token.value.includes(":")) {
22021
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
22036
+ cacheKey += "|R|";
22022
22037
  }
22023
22038
  else {
22024
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
22039
+ cacheKey += "|C|";
22025
22040
  }
22026
22041
  break;
22027
22042
  case "SPACE":
22028
- cacheKey += "";
22043
+ // ignore spaces
22029
22044
  break;
22030
22045
  default:
22031
22046
  cacheKey += token.value;
@@ -22038,7 +22053,7 @@ function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
22038
22053
  * Return formula arguments which are references, strings and numbers.
22039
22054
  */
22040
22055
  function formulaArguments(tokens) {
22041
- const constantValues = {
22056
+ const literalValues = {
22042
22057
  numbers: [],
22043
22058
  strings: [],
22044
22059
  };
@@ -22052,15 +22067,11 @@ function formulaArguments(tokens) {
22052
22067
  break;
22053
22068
  case "STRING":
22054
22069
  const value = removeStringQuotes(token.value);
22055
- if (!constantValues.strings.includes(value)) {
22056
- constantValues.strings.push(value);
22057
- }
22070
+ literalValues.strings.push({ value });
22058
22071
  break;
22059
22072
  case "NUMBER": {
22060
22073
  const value = parseNumber(token.value, DEFAULT_LOCALE);
22061
- if (!constantValues.numbers.includes(value)) {
22062
- constantValues.numbers.push(value);
22063
- }
22074
+ literalValues.numbers.push({ value });
22064
22075
  break;
22065
22076
  }
22066
22077
  case "SYMBOL": {
@@ -22071,7 +22082,7 @@ function formulaArguments(tokens) {
22071
22082
  }
22072
22083
  return {
22073
22084
  dependencies,
22074
- constantValues,
22085
+ literalValues,
22075
22086
  symbols,
22076
22087
  };
22077
22088
  }
@@ -22495,7 +22506,7 @@ autoCompleteProviders.add("pivot_group_values", {
22495
22506
  text,
22496
22507
  description: usedLabel,
22497
22508
  htmlContent: [{ value: text, color }],
22498
- fuzzySearchKey: value + usedLabel,
22509
+ fuzzySearchKey: text + usedLabel,
22499
22510
  };
22500
22511
  });
22501
22512
  },
@@ -22992,9 +23003,9 @@ const XLSX_CHART_TYPES = [
22992
23003
  /** In XLSX color format (no #) */
22993
23004
  const AUTO_COLOR = "000000";
22994
23005
  const XLSX_ICONSET_MAP = {
22995
- arrow: "3Arrows",
23006
+ arrows: "3Arrows",
22996
23007
  smiley: "3Symbols",
22997
- dot: "3TrafficLights1",
23008
+ dots: "3TrafficLights1",
22998
23009
  };
22999
23010
  const NAMESPACE = {
23000
23011
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -23525,6 +23536,7 @@ const ICON_SET_CONVERSION_MAP = {
23525
23536
  };
23526
23537
  /** Map between legend position in XLSX file and human readable position */
23527
23538
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
23539
+ none: "none",
23528
23540
  b: "bottom",
23529
23541
  t: "top",
23530
23542
  l: "left",
@@ -26287,7 +26299,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26287
26299
  default: "ffffff",
26288
26300
  }).asString(),
26289
26301
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
26290
- default: "b",
26302
+ default: "none",
26291
26303
  }).asString()],
26292
26304
  stacked: barChartGrouping === "stacked",
26293
26305
  fontColor: "000000",
@@ -26321,7 +26333,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26321
26333
  default: "ffffff",
26322
26334
  }).asString(),
26323
26335
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
26324
- default: "b",
26336
+ default: "none",
26325
26337
  }).asString()],
26326
26338
  stacked: barChartGrouping === "stacked",
26327
26339
  fontColor: "000000",
@@ -28933,7 +28945,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
28933
28945
  }
28934
28946
  }
28935
28947
  else if (dataSets.length === 1) {
28936
- 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++) {
28937
28950
  labels.formattedValues.push("");
28938
28951
  labels.values.push("");
28939
28952
  }
@@ -29116,7 +29129,7 @@ function getLineChartDatasets(definition, args) {
29116
29129
  function getScatterChartDatasets(definition, args) {
29117
29130
  const dataSets = getLineChartDatasets(definition, args);
29118
29131
  for (const dataSet of dataSets) {
29119
- if (dataSet.xAxisID !== TREND_LINE_XAXIS_ID) {
29132
+ if (!isTrendLineAxis(dataSet.xAxisID)) {
29120
29133
  dataSet.showLine = false;
29121
29134
  }
29122
29135
  }
@@ -29243,7 +29256,9 @@ function getTrendingLineDataSet(dataset, config, data) {
29243
29256
  const borderColor = config.color || lightenColor(rgbaToHex(defaultBorderColor), 0.5);
29244
29257
  return {
29245
29258
  type: "line",
29246
- xAxisID: TREND_LINE_XAXIS_ID,
29259
+ xAxisID: config.type === "trailingMovingAverage"
29260
+ ? MOVING_AVERAGE_TREND_LINE_XAXIS_ID
29261
+ : TREND_LINE_XAXIS_ID,
29247
29262
  yAxisID: dataset.yAxisID,
29248
29263
  label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
29249
29264
  data,
@@ -29318,22 +29333,19 @@ function getPieChartLegend(definition, args) {
29318
29333
  const { dataSetsValues } = args;
29319
29334
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
29320
29335
  const colors = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
29336
+ const fontColor = chartFontColor(definition.background);
29321
29337
  return {
29322
29338
  ...getLegendDisplayOptions(definition),
29323
29339
  labels: {
29324
- color: chartFontColor(definition.background),
29325
29340
  usePointStyle: true,
29326
- //@ts-ignore
29327
- generateLabels: (c) =>
29328
- //@ts-ignore
29329
- c.data.labels.map((label, index) => ({
29341
+ generateLabels: (c) => c.data.labels?.map((label, index) => ({
29330
29342
  text: truncateLabel(String(label)),
29331
29343
  strokeStyle: colors[index],
29332
29344
  fillStyle: colors[index],
29333
29345
  pointStyle: "rect",
29334
- hidden: false,
29335
29346
  lineWidth: 2,
29336
- })),
29347
+ fontColor,
29348
+ })) || [],
29337
29349
  filter: (legendItem, data) => {
29338
29350
  return "datasetIndex" in legendItem
29339
29351
  ? !data.datasets[legendItem.datasetIndex].hidden
@@ -29466,7 +29478,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29466
29478
  color: fontColor,
29467
29479
  usePointStyle: true,
29468
29480
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29469
- if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29481
+ if (isTrendLineAxis(dataset["xAxisID"])) {
29470
29482
  return {
29471
29483
  text: truncateLabel(dataset.label),
29472
29484
  fontColor,
@@ -29524,6 +29536,11 @@ function getBarChartScales(definition, args) {
29524
29536
  offset: false,
29525
29537
  display: false,
29526
29538
  };
29539
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29540
+ ...scales.x,
29541
+ offset: false,
29542
+ display: false,
29543
+ };
29527
29544
  }
29528
29545
  return scales;
29529
29546
  }
@@ -29557,6 +29574,10 @@ function getLineChartScales(definition, args) {
29557
29574
  ...scales.x,
29558
29575
  display: false,
29559
29576
  };
29577
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29578
+ ...scales.x,
29579
+ display: false,
29580
+ };
29560
29581
  if (axisType === "category" || axisType === "time") {
29561
29582
  /* We add a second x axis here to draw the trend lines, with the labels length being
29562
29583
  * set so that the second axis points match the classical x axis
@@ -29565,6 +29586,8 @@ function getLineChartScales(definition, args) {
29565
29586
  scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29566
29587
  scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29567
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;
29568
29591
  }
29569
29592
  }
29570
29593
  return scales;
@@ -29883,9 +29906,7 @@ function getBarChartTooltip(definition, args) {
29883
29906
  external: customTooltipHandler,
29884
29907
  callbacks: {
29885
29908
  title: function (tooltipItems) {
29886
- return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
29887
- ? undefined
29888
- : "";
29909
+ return tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID)) ? undefined : "";
29889
29910
  },
29890
29911
  beforeLabel: (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label,
29891
29912
  label: function (tooltipItem) {
@@ -29912,7 +29933,7 @@ function getLineChartTooltip(definition, args) {
29912
29933
  if (axisType === "linear") {
29913
29934
  tooltip.callbacks.label = (tooltipItem) => {
29914
29935
  const dataSetPoint = tooltipItem.parsed.y;
29915
- let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29936
+ let label = isTrendLineAxis(tooltipItem.dataset.xAxisID)
29916
29937
  ? ""
29917
29938
  : tooltipItem.parsed.x;
29918
29939
  if (typeof label === "string" && isNumber(label, locale)) {
@@ -29934,8 +29955,7 @@ function getLineChartTooltip(definition, args) {
29934
29955
  }
29935
29956
  tooltip.callbacks.beforeLabel = (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label;
29936
29957
  tooltip.callbacks.title = function (tooltipItems) {
29937
- const displayTooltipTitle = axisType !== "linear" &&
29938
- tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
29958
+ const displayTooltipTitle = axisType !== "linear" && tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID));
29939
29959
  return displayTooltipTitle ? undefined : "";
29940
29960
  };
29941
29961
  return tooltip;
@@ -34189,6 +34209,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34189
34209
  CHART_COMMON_OPTIONS: CHART_COMMON_OPTIONS,
34190
34210
  GaugeChart: GaugeChart,
34191
34211
  LineChart: LineChart,
34212
+ MOVING_AVERAGE_TREND_LINE_XAXIS_ID: MOVING_AVERAGE_TREND_LINE_XAXIS_ID,
34192
34213
  PieChart: PieChart,
34193
34214
  ScorecardChart: ScorecardChart$1,
34194
34215
  TREND_LINE_XAXIS_ID: TREND_LINE_XAXIS_ID,
@@ -34218,6 +34239,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34218
34239
  getDefinedAxis: getDefinedAxis,
34219
34240
  getPieColors: getPieColors,
34220
34241
  getSmartChartDefinition: getSmartChartDefinition,
34242
+ isTrendLineAxis: isTrendLineAxis,
34221
34243
  shouldRemoveFirstLabel: shouldRemoveFirstLabel,
34222
34244
  toExcelDataset: toExcelDataset,
34223
34245
  toExcelLabelRange: toExcelLabelRange,
@@ -36265,9 +36287,7 @@ class FormulaFingerprintStore extends SpreadsheetStore {
36265
36287
  }
36266
36288
  }
36267
36289
  }
36268
- // removes the index placeholders from the normalized formula
36269
- // =|N0|+|N1|+|N0| -> =|N|+|N|+|N|
36270
- const normalizedFormula = cell.compiledFormula.normalizedFormula.replace(/(|\w)(\d)(|)/g, "$1$3");
36290
+ const normalizedFormula = cell.compiledFormula.normalizedFormula;
36271
36291
  return hash(fingerprintVector) + normalizedFormula;
36272
36292
  }
36273
36293
  getLiteralFingerprint(position) {
@@ -39612,9 +39632,11 @@ class SeriesDesignEditor extends owl.Component {
39612
39632
  if (!runtime || !("chartJsConfig" in runtime)) {
39613
39633
  return [];
39614
39634
  }
39615
- 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);
39616
39638
  }
39617
- updateSerieEditor(ev) {
39639
+ updateEditedSeries(ev) {
39618
39640
  this.state.index = ev.target.selectedIndex;
39619
39641
  }
39620
39642
  updateDataSeriesColor(color) {
@@ -39627,7 +39649,7 @@ class SeriesDesignEditor extends owl.Component {
39627
39649
  };
39628
39650
  this.props.updateChart(this.props.figureId, { dataSets });
39629
39651
  }
39630
- getDataSerieColor() {
39652
+ getDataSeriesColor() {
39631
39653
  const dataSets = this.props.definition.dataSets;
39632
39654
  if (!dataSets?.[this.state.index])
39633
39655
  return "";
@@ -39647,7 +39669,7 @@ class SeriesDesignEditor extends owl.Component {
39647
39669
  };
39648
39670
  this.props.updateChart(this.props.figureId, { dataSets });
39649
39671
  }
39650
- getDataSerieLabel() {
39672
+ getDataSeriesLabel() {
39651
39673
  const dataSets = this.props.definition.dataSets;
39652
39674
  return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
39653
39675
  }
@@ -39760,7 +39782,7 @@ class SeriesWithAxisDesignEditor extends owl.Component {
39760
39782
  }
39761
39783
  this.updateTrendLineValue(index, { window });
39762
39784
  }
39763
- getDataSerieColor(index) {
39785
+ getDataSeriesColor(index) {
39764
39786
  const dataSets = this.props.definition.dataSets;
39765
39787
  if (!dataSets?.[index])
39766
39788
  return "";
@@ -39771,7 +39793,7 @@ class SeriesWithAxisDesignEditor extends owl.Component {
39771
39793
  }
39772
39794
  getTrendLineColor(index) {
39773
39795
  return (this.getTrendLineConfiguration(index)?.color ??
39774
- setColorAlpha(this.getDataSerieColor(index), 0.5));
39796
+ setColorAlpha(this.getDataSeriesColor(index), 0.5));
39775
39797
  }
39776
39798
  updateTrendLineColor(index, color) {
39777
39799
  this.updateTrendLineValue(index, { color });
@@ -47172,6 +47194,7 @@ class RemoveDuplicatesPanel extends owl.Component {
47172
47194
  columns: {},
47173
47195
  });
47174
47196
  setup() {
47197
+ this.updateColumns();
47175
47198
  owl.onWillUpdateProps(() => this.updateColumns());
47176
47199
  }
47177
47200
  toggleHasHeader() {
@@ -48955,8 +48978,8 @@ class CellComposerStore extends AbstractComposerStore {
48955
48978
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
48956
48979
  if (!sheetIdExists && this.editionMode !== "inactive") {
48957
48980
  this.sheetId = this.getters.getActiveSheetId();
48958
- this.cancelEditionAndActivateSheet();
48959
48981
  this.resetContent();
48982
+ this.cancelEditionAndActivateSheet();
48960
48983
  this.notificationStore.raiseError(CELL_DELETED_MESSAGE);
48961
48984
  }
48962
48985
  break;
@@ -65384,12 +65407,7 @@ class SheetUIPlugin extends UIPlugin {
65384
65407
  }
65385
65408
  break;
65386
65409
  case "AUTORESIZE_ROWS":
65387
- this.dispatch("RESIZE_COLUMNS_ROWS", {
65388
- elements: cmd.rows,
65389
- dimension: "ROW",
65390
- size: null,
65391
- sheetId: cmd.sheetId,
65392
- });
65410
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
65393
65411
  break;
65394
65412
  }
65395
65413
  }
@@ -65553,6 +65571,48 @@ class SheetUIPlugin extends UIPlugin {
65553
65571
  }
65554
65572
  return "Success" /* CommandResult.Success */;
65555
65573
  }
65574
+ autoResizeRows(sheetId, rows) {
65575
+ const rowSizes = [];
65576
+ for (const row of rows) {
65577
+ let evaluatedRowSize = 0;
65578
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
65579
+ const cell = this.getters.getCellById(cellId);
65580
+ if (!cell) {
65581
+ continue;
65582
+ }
65583
+ const position = this.getters.getCellPosition(cell.id);
65584
+ const colSize = this.getters.getColSize(sheetId, position.col);
65585
+ if (cell.isFormula) {
65586
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
65587
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65588
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
65589
+ evaluatedRowSize = evaluatedSize;
65590
+ }
65591
+ }
65592
+ else {
65593
+ const content = cell.content;
65594
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65595
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
65596
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
65597
+ evaluatedRowSize = 0;
65598
+ }
65599
+ }
65600
+ }
65601
+ rowSizes.push(evaluatedRowSize || null);
65602
+ }
65603
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
65604
+ for (let i = 0; i < rowSizes.length; i++) {
65605
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
65606
+ }
65607
+ for (const [size, rows] of groupedSizes) {
65608
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
65609
+ elements: rows,
65610
+ dimension: "ROW",
65611
+ size,
65612
+ sheetId,
65613
+ });
65614
+ }
65615
+ }
65556
65616
  }
65557
65617
 
65558
65618
  class TableComputedStylePlugin extends UIPlugin {
@@ -70713,7 +70773,9 @@ css /* scss */ `
70713
70773
  border: 1px solid;
70714
70774
  font-family: ${DEFAULT_FONT};
70715
70775
 
70716
- .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 {
70717
70779
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
70718
70780
  position: relative;
70719
70781
  top: 20%;
@@ -74039,10 +74101,14 @@ function addIconSetRule(cf, rule) {
74039
74101
  continue;
74040
74102
  }
74041
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
+ }
74042
74108
  conditionalFormats.push(escapeXml /*xml*/ `
74043
74109
  <conditionalFormatting sqref="${range}">
74044
74110
  <cfRule ${formatAttributes(ruleAttributes)}>
74045
- <iconSet iconSet="${getIconSet(rule.icons)}">
74111
+ <iconSet ${formatAttributes(iconSetAttrs)}>
74046
74112
  ${joinXmlNodes(cfValueObjectNodes)}
74047
74113
  </iconSet>
74048
74114
  </cfRule>
@@ -74060,9 +74126,21 @@ function commonCfAttributes(cf) {
74060
74126
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
74061
74127
  ];
74062
74128
  }
74129
+ function isIconSetReversed(iconSet) {
74130
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
74131
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
74132
+ }
74063
74133
  function getIconSet(iconSet) {
74064
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
74065
- "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;
74066
74144
  }
74067
74145
  function thresholdAttributes(threshold, position) {
74068
74146
  const type = getExcelThresholdType(threshold.type, position);
@@ -76012,6 +76090,6 @@ exports.tokenColors = tokenColors;
76012
76090
  exports.tokenize = tokenize;
76013
76091
 
76014
76092
 
76015
- __info__.version = "18.2.3";
76016
- __info__.date = "2025-03-12T15:32:36.274Z";
76017
- __info__.hash = "81b0e08";
76093
+ __info__.version = "18.2.5";
76094
+ __info__.date = "2025-03-26T12:47:44.113Z";
76095
+ __info__.hash = "4675edd";