@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
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -1131,7 +1131,10 @@ function rgbaStringToHex(color) {
1131
1131
  }
1132
1132
  else if (stringVals.length === 4) {
1133
1133
  const alpha = parseFloat(stringVals.pop() || "1");
1134
- alphaHex = Math.round((alpha || 1) * 255);
1134
+ if (isNaN(alpha)) {
1135
+ throw new Error("invalid alpha value");
1136
+ }
1137
+ alphaHex = Math.round(alpha * 255);
1135
1138
  }
1136
1139
  const vals = stringVals.map((val) => parseInt(val, 10));
1137
1140
  if (alphaHex !== 255) {
@@ -6285,11 +6288,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
6285
6288
  if (!cell || (!cell.isFormula && !cell.content)) {
6286
6289
  return DEFAULT_CELL_HEIGHT;
6287
6290
  }
6288
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6289
- const numberOfLines = cell.isFormula
6290
- ? 1
6291
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
6292
- const fontSize = computeTextFontSizeInPixels(cell.style);
6291
+ const content = cell.isFormula ? "" : cell.content;
6292
+ return getCellContentHeight(ctx, content, cell.style, colSize);
6293
+ }
6294
+ function getCellContentHeight(ctx, content, style, colSize) {
6295
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6296
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
6297
+ const fontSize = computeTextFontSizeInPixels(style);
6293
6298
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
6294
6299
  }
6295
6300
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
@@ -6997,7 +7002,7 @@ function isValidLocale(locale) {
6997
7002
  */
6998
7003
  function canonicalizeNumberContent(content, locale) {
6999
7004
  return content.startsWith("=")
7000
- ? canonicalizeFormula$1(content, locale)
7005
+ ? canonicalizeFormula(content, locale)
7001
7006
  : canonicalizeNumberLiteral(content, locale);
7002
7007
  }
7003
7008
  /**
@@ -7012,7 +7017,7 @@ function canonicalizeNumberContent(content, locale) {
7012
7017
  */
7013
7018
  function canonicalizeContent(content, locale) {
7014
7019
  return content.startsWith("=")
7015
- ? canonicalizeFormula$1(content, locale)
7020
+ ? canonicalizeFormula(content, locale)
7016
7021
  : canonicalizeLiteral(content, locale);
7017
7022
  }
7018
7023
  /**
@@ -7028,15 +7033,21 @@ function localizeContent(content, locale) {
7028
7033
  ? localizeFormula(content, locale)
7029
7034
  : localizeLiteral(content, locale);
7030
7035
  }
7036
+ /** Change a number string to its canonical form (en_US locale) */
7037
+ function canonicalizeNumberValue(content, locale) {
7038
+ return content.startsWith("=")
7039
+ ? canonicalizeFormula(content, locale)
7040
+ : canonicalizeNumberLiteral(content, locale);
7041
+ }
7031
7042
  /** Change a formula to its canonical form (en_US locale) */
7032
- function canonicalizeFormula$1(formula, locale) {
7033
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
7043
+ function canonicalizeFormula(formula, locale) {
7044
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7034
7045
  }
7035
7046
  /** Change a formula from the canonical form to the given locale */
7036
7047
  function localizeFormula(formula, locale) {
7037
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
7048
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
7038
7049
  }
7039
- function _localizeFormula$1(formula, fromLocale, toLocale) {
7050
+ function _localizeFormula(formula, fromLocale, toLocale) {
7040
7051
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7041
7052
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7042
7053
  return formula;
@@ -7191,37 +7202,6 @@ function getDateTimeFormat(locale) {
7191
7202
  return locale.dateFormat + " " + locale.timeFormat;
7192
7203
  }
7193
7204
 
7194
- /** Change a number string to its canonical form (en_US locale) */
7195
- function canonicalizeNumberValue(content, locale) {
7196
- return content.startsWith("=")
7197
- ? canonicalizeFormula(content, locale)
7198
- : canonicalizeNumberLiteral(content, locale);
7199
- }
7200
- /** Change a formula to its canonical form (en_US locale) */
7201
- function canonicalizeFormula(formula, locale) {
7202
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7203
- }
7204
- function _localizeFormula(formula, fromLocale, toLocale) {
7205
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7206
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7207
- return formula;
7208
- }
7209
- const tokens = tokenize(formula, fromLocale);
7210
- let localizedFormula = "";
7211
- for (const token of tokens) {
7212
- if (token.type === "NUMBER") {
7213
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7214
- }
7215
- else if (token.type === "ARG_SEPARATOR") {
7216
- localizedFormula += toLocale.formulaArgSeparator;
7217
- }
7218
- else {
7219
- localizedFormula += token.value;
7220
- }
7221
- }
7222
- return localizedFormula;
7223
- }
7224
-
7225
7205
  function boolAnd(args) {
7226
7206
  let foundBoolean = false;
7227
7207
  let acc = true;
@@ -8486,13 +8466,6 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8486
8466
  this.clearClippedZones(content);
8487
8467
  const selection = target[0];
8488
8468
  this.pasteZone(sheetId, selection.left, selection.top, content.cells, options);
8489
- this.dispatch("MOVE_RANGES", {
8490
- target: content.zones,
8491
- sheetId: content.sheetId,
8492
- targetSheetId: sheetId,
8493
- col: selection.left,
8494
- row: selection.top,
8495
- });
8496
8469
  }
8497
8470
  /**
8498
8471
  * Clear the clipped zones: remove the cells and clear the formatting
@@ -9001,14 +8974,15 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
9001
8974
  }
9002
8975
  merges.push(mergesInRow);
9003
8976
  }
9004
- return { merges };
8977
+ return { merges, sheetId };
9005
8978
  }
9006
8979
  /**
9007
8980
  * Paste the clipboard content in the given target
9008
8981
  */
9009
8982
  paste(target, content, options) {
9010
8983
  if (options.isCutOperation) {
9011
- return;
8984
+ const copiedMerges = content.merges.flat().filter(isDefined);
8985
+ this.dispatch("REMOVE_MERGE", { sheetId: content.sheetId, target: copiedMerges });
9012
8986
  }
9013
8987
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
9014
8988
  }
@@ -9043,6 +9017,27 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
9043
9017
  }
9044
9018
  }
9045
9019
 
9020
+ class ReferenceClipboardHandler extends AbstractCellClipboardHandler {
9021
+ copy(data) {
9022
+ return {
9023
+ zones: data.clippedZones,
9024
+ sheetId: data.sheetId,
9025
+ };
9026
+ }
9027
+ paste(target, content, options) {
9028
+ if (options.isCutOperation) {
9029
+ const selection = target.zones[0];
9030
+ this.dispatch("MOVE_RANGES", {
9031
+ target: content.zones,
9032
+ sheetId: content.sheetId,
9033
+ targetSheetId: target.sheetId,
9034
+ col: selection.left,
9035
+ row: selection.top,
9036
+ });
9037
+ }
9038
+ }
9039
+ }
9040
+
9046
9041
  class SheetClipboardHandler extends AbstractCellClipboardHandler {
9047
9042
  isPasteAllowed(sheetId, target, content, options) {
9048
9043
  if (!("cells" in content)) {
@@ -9206,7 +9201,8 @@ clipboardHandlersRegistries.cellHandlers
9206
9201
  .add("merge", MergeClipboardHandler)
9207
9202
  .add("border", BorderClipboardHandler)
9208
9203
  .add("table", TableClipboardHandler)
9209
- .add("conditionalFormat", ConditionalFormatClipboardHandler);
9204
+ .add("conditionalFormat", ConditionalFormatClipboardHandler)
9205
+ .add("references", ReferenceClipboardHandler);
9210
9206
 
9211
9207
  function transformZone(zone, executed) {
9212
9208
  if (executed.type === "REMOVE_COLUMNS_ROWS") {
@@ -9581,6 +9577,7 @@ class ComposerFocusStore extends SpreadsheetStore {
9581
9577
  }
9582
9578
 
9583
9579
  const TREND_LINE_XAXIS_ID = "x1";
9580
+ const MOVING_AVERAGE_TREND_LINE_XAXIS_ID = "xMovingAverage";
9584
9581
  /**
9585
9582
  * This file contains helpers that are common to different charts (mainly
9586
9583
  * line, bar and pie charts)
@@ -9931,6 +9928,9 @@ function truncateLabel(label) {
9931
9928
  }
9932
9929
  return label;
9933
9930
  }
9931
+ function isTrendLineAxis(axisID) {
9932
+ return axisID === TREND_LINE_XAXIS_ID || axisID === MOVING_AVERAGE_TREND_LINE_XAXIS_ID;
9933
+ }
9934
9934
 
9935
9935
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9936
9936
  const chartShowValuesPlugin = {
@@ -9975,7 +9975,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
9975
9975
  const yMin = chart.chartArea.top;
9976
9976
  const textsPositions = {};
9977
9977
  for (const dataset of chart._metasets) {
9978
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID || dataset.hidden) {
9978
+ if (isTrendLineAxis(dataset.axisID) || dataset.hidden) {
9979
9979
  continue;
9980
9980
  }
9981
9981
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -10018,7 +10018,7 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
10018
10018
  const xMin = chart.chartArea.left;
10019
10019
  const textsPositions = {};
10020
10020
  for (const dataset of chart._metasets) {
10021
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
10021
+ if (isTrendLineAxis(dataset.axisID)) {
10022
10022
  return; // ignore trend lines
10023
10023
  }
10024
10024
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -20526,11 +20526,26 @@ const SEARCH = {
20526
20526
  const _searchFor = toString(searchFor).toLowerCase();
20527
20527
  const _textToSearch = toString(textToSearch).toLowerCase();
20528
20528
  const _startingAt = toNumber(startingAt, this.locale);
20529
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
20530
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
20529
+ if (_textToSearch === "") {
20530
+ return {
20531
+ value: CellErrorType.GenericError,
20532
+ message: _t("The text_to_search must be non-empty."),
20533
+ };
20534
+ }
20535
+ if (_startingAt < 1) {
20536
+ return {
20537
+ value: CellErrorType.GenericError,
20538
+ message: _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt),
20539
+ };
20540
+ }
20531
20541
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
20532
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
20533
- return result + 1;
20542
+ if (result === -1) {
20543
+ return {
20544
+ value: CellErrorType.GenericError,
20545
+ message: _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch),
20546
+ };
20547
+ }
20548
+ return { value: result + 1 };
20534
20549
  },
20535
20550
  isExported: true,
20536
20551
  };
@@ -21157,8 +21172,8 @@ class AbstractComposerStore extends SpreadsheetStore {
21157
21172
  this.computeParenthesisRelatedToCursor();
21158
21173
  }
21159
21174
  cancelEdition() {
21160
- this.cancelEditionAndActivateSheet();
21161
21175
  this.resetContent();
21176
+ this.cancelEditionAndActivateSheet();
21162
21177
  }
21163
21178
  setCurrentContent(content, selection) {
21164
21179
  if (selection && !this.isSelectionValid(content.length, selection.start, selection.end)) {
@@ -21176,8 +21191,8 @@ class AbstractComposerStore extends SpreadsheetStore {
21176
21191
  switch (cmd.type) {
21177
21192
  case "SELECT_FIGURE":
21178
21193
  if (cmd.id) {
21179
- this.cancelEditionAndActivateSheet();
21180
21194
  this.resetContent();
21195
+ this.cancelEditionAndActivateSheet();
21181
21196
  }
21182
21197
  break;
21183
21198
  case "START_CHANGE_HIGHLIGHT":
@@ -21865,11 +21880,14 @@ function compileTokens(tokens) {
21865
21880
  }
21866
21881
  }
21867
21882
  function compileTokensOrThrow(tokens) {
21868
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
21869
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
21883
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
21884
+ const cacheKey = compilationCacheKey(tokens);
21870
21885
  if (!functionCache[cacheKey]) {
21871
21886
  const ast = parseTokens([...tokens]);
21872
21887
  const scope = new Scope();
21888
+ let stringCount = 0;
21889
+ let numberCount = 0;
21890
+ let dependencyCount = 0;
21873
21891
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
21874
21892
  throw new BadExpressionError(_t("Invalid formula"));
21875
21893
  }
@@ -21943,16 +21961,15 @@ function compileTokensOrThrow(tokens) {
21943
21961
  case "BOOLEAN":
21944
21962
  return code.return(`{ value: ${ast.value} }`);
21945
21963
  case "NUMBER":
21946
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
21964
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
21947
21965
  case "STRING":
21948
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
21966
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
21949
21967
  case "REFERENCE":
21950
- const referenceIndex = dependencies.indexOf(ast.value);
21951
21968
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
21952
- return code.return(`range(deps[${referenceIndex}])`);
21969
+ return code.return(`range(deps[${dependencyCount++}])`);
21953
21970
  }
21954
21971
  else {
21955
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
21972
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
21956
21973
  }
21957
21974
  case "FUNCALL":
21958
21975
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -21984,7 +22001,7 @@ function compileTokensOrThrow(tokens) {
21984
22001
  const compiledFormula = {
21985
22002
  execute: functionCache[cacheKey],
21986
22003
  dependencies,
21987
- constantValues,
22004
+ literalValues,
21988
22005
  symbols,
21989
22006
  tokens,
21990
22007
  isBadExpression: false,
@@ -21997,33 +22014,31 @@ function compileTokensOrThrow(tokens) {
21997
22014
  * References, numbers and strings are replaced with placeholders because
21998
22015
  * the compiled formula does not depend on their actual value.
21999
22016
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
22000
- *
22001
22017
  * Spaces are also ignored to compute the cache key.
22002
22018
  *
22003
- * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|0|+|1|+SUM(|N0|,|N0|,|S0|)`
22019
+ * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|C|+|C|+SUM(|N|,|N|,|S|)`
22004
22020
  */
22005
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
22021
+ function compilationCacheKey(tokens) {
22006
22022
  let cacheKey = "";
22007
22023
  for (const token of tokens) {
22008
22024
  switch (token.type) {
22009
22025
  case "STRING":
22010
- const value = removeStringQuotes(token.value);
22011
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
22026
+ cacheKey += "|S|";
22012
22027
  break;
22013
22028
  case "NUMBER":
22014
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
22029
+ cacheKey += "|N|";
22015
22030
  break;
22016
22031
  case "REFERENCE":
22017
22032
  case "INVALID_REFERENCE":
22018
22033
  if (token.value.includes(":")) {
22019
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
22034
+ cacheKey += "|R|";
22020
22035
  }
22021
22036
  else {
22022
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
22037
+ cacheKey += "|C|";
22023
22038
  }
22024
22039
  break;
22025
22040
  case "SPACE":
22026
- cacheKey += "";
22041
+ // ignore spaces
22027
22042
  break;
22028
22043
  default:
22029
22044
  cacheKey += token.value;
@@ -22036,7 +22051,7 @@ function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
22036
22051
  * Return formula arguments which are references, strings and numbers.
22037
22052
  */
22038
22053
  function formulaArguments(tokens) {
22039
- const constantValues = {
22054
+ const literalValues = {
22040
22055
  numbers: [],
22041
22056
  strings: [],
22042
22057
  };
@@ -22050,15 +22065,11 @@ function formulaArguments(tokens) {
22050
22065
  break;
22051
22066
  case "STRING":
22052
22067
  const value = removeStringQuotes(token.value);
22053
- if (!constantValues.strings.includes(value)) {
22054
- constantValues.strings.push(value);
22055
- }
22068
+ literalValues.strings.push({ value });
22056
22069
  break;
22057
22070
  case "NUMBER": {
22058
22071
  const value = parseNumber(token.value, DEFAULT_LOCALE);
22059
- if (!constantValues.numbers.includes(value)) {
22060
- constantValues.numbers.push(value);
22061
- }
22072
+ literalValues.numbers.push({ value });
22062
22073
  break;
22063
22074
  }
22064
22075
  case "SYMBOL": {
@@ -22069,7 +22080,7 @@ function formulaArguments(tokens) {
22069
22080
  }
22070
22081
  return {
22071
22082
  dependencies,
22072
- constantValues,
22083
+ literalValues,
22073
22084
  symbols,
22074
22085
  };
22075
22086
  }
@@ -22493,7 +22504,7 @@ autoCompleteProviders.add("pivot_group_values", {
22493
22504
  text,
22494
22505
  description: usedLabel,
22495
22506
  htmlContent: [{ value: text, color }],
22496
- fuzzySearchKey: value + usedLabel,
22507
+ fuzzySearchKey: text + usedLabel,
22497
22508
  };
22498
22509
  });
22499
22510
  },
@@ -22990,9 +23001,9 @@ const XLSX_CHART_TYPES = [
22990
23001
  /** In XLSX color format (no #) */
22991
23002
  const AUTO_COLOR = "000000";
22992
23003
  const XLSX_ICONSET_MAP = {
22993
- arrow: "3Arrows",
23004
+ arrows: "3Arrows",
22994
23005
  smiley: "3Symbols",
22995
- dot: "3TrafficLights1",
23006
+ dots: "3TrafficLights1",
22996
23007
  };
22997
23008
  const NAMESPACE = {
22998
23009
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -23523,6 +23534,7 @@ const ICON_SET_CONVERSION_MAP = {
23523
23534
  };
23524
23535
  /** Map between legend position in XLSX file and human readable position */
23525
23536
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
23537
+ none: "none",
23526
23538
  b: "bottom",
23527
23539
  t: "top",
23528
23540
  l: "left",
@@ -26285,7 +26297,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26285
26297
  default: "ffffff",
26286
26298
  }).asString(),
26287
26299
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
26288
- default: "b",
26300
+ default: "none",
26289
26301
  }).asString()],
26290
26302
  stacked: barChartGrouping === "stacked",
26291
26303
  fontColor: "000000",
@@ -26319,7 +26331,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26319
26331
  default: "ffffff",
26320
26332
  }).asString(),
26321
26333
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
26322
- default: "b",
26334
+ default: "none",
26323
26335
  }).asString()],
26324
26336
  stacked: barChartGrouping === "stacked",
26325
26337
  fontColor: "000000",
@@ -28931,7 +28943,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
28931
28943
  }
28932
28944
  }
28933
28945
  else if (dataSets.length === 1) {
28934
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
28946
+ const dataLength = getData(getters, dataSets[0]).length;
28947
+ for (let i = 0; i < dataLength; i++) {
28935
28948
  labels.formattedValues.push("");
28936
28949
  labels.values.push("");
28937
28950
  }
@@ -29114,7 +29127,7 @@ function getLineChartDatasets(definition, args) {
29114
29127
  function getScatterChartDatasets(definition, args) {
29115
29128
  const dataSets = getLineChartDatasets(definition, args);
29116
29129
  for (const dataSet of dataSets) {
29117
- if (dataSet.xAxisID !== TREND_LINE_XAXIS_ID) {
29130
+ if (!isTrendLineAxis(dataSet.xAxisID)) {
29118
29131
  dataSet.showLine = false;
29119
29132
  }
29120
29133
  }
@@ -29241,7 +29254,9 @@ function getTrendingLineDataSet(dataset, config, data) {
29241
29254
  const borderColor = config.color || lightenColor(rgbaToHex(defaultBorderColor), 0.5);
29242
29255
  return {
29243
29256
  type: "line",
29244
- xAxisID: TREND_LINE_XAXIS_ID,
29257
+ xAxisID: config.type === "trailingMovingAverage"
29258
+ ? MOVING_AVERAGE_TREND_LINE_XAXIS_ID
29259
+ : TREND_LINE_XAXIS_ID,
29245
29260
  yAxisID: dataset.yAxisID,
29246
29261
  label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
29247
29262
  data,
@@ -29316,22 +29331,19 @@ function getPieChartLegend(definition, args) {
29316
29331
  const { dataSetsValues } = args;
29317
29332
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
29318
29333
  const colors = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
29334
+ const fontColor = chartFontColor(definition.background);
29319
29335
  return {
29320
29336
  ...getLegendDisplayOptions(definition),
29321
29337
  labels: {
29322
- color: chartFontColor(definition.background),
29323
29338
  usePointStyle: true,
29324
- //@ts-ignore
29325
- generateLabels: (c) =>
29326
- //@ts-ignore
29327
- c.data.labels.map((label, index) => ({
29339
+ generateLabels: (c) => c.data.labels?.map((label, index) => ({
29328
29340
  text: truncateLabel(String(label)),
29329
29341
  strokeStyle: colors[index],
29330
29342
  fillStyle: colors[index],
29331
29343
  pointStyle: "rect",
29332
- hidden: false,
29333
29344
  lineWidth: 2,
29334
- })),
29345
+ fontColor,
29346
+ })) || [],
29335
29347
  filter: (legendItem, data) => {
29336
29348
  return "datasetIndex" in legendItem
29337
29349
  ? !data.datasets[legendItem.datasetIndex].hidden
@@ -29464,7 +29476,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29464
29476
  color: fontColor,
29465
29477
  usePointStyle: true,
29466
29478
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29467
- if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29479
+ if (isTrendLineAxis(dataset["xAxisID"])) {
29468
29480
  return {
29469
29481
  text: truncateLabel(dataset.label),
29470
29482
  fontColor,
@@ -29522,6 +29534,11 @@ function getBarChartScales(definition, args) {
29522
29534
  offset: false,
29523
29535
  display: false,
29524
29536
  };
29537
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29538
+ ...scales.x,
29539
+ offset: false,
29540
+ display: false,
29541
+ };
29525
29542
  }
29526
29543
  return scales;
29527
29544
  }
@@ -29555,6 +29572,10 @@ function getLineChartScales(definition, args) {
29555
29572
  ...scales.x,
29556
29573
  display: false,
29557
29574
  };
29575
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29576
+ ...scales.x,
29577
+ display: false,
29578
+ };
29558
29579
  if (axisType === "category" || axisType === "time") {
29559
29580
  /* We add a second x axis here to draw the trend lines, with the labels length being
29560
29581
  * set so that the second axis points match the classical x axis
@@ -29563,6 +29584,8 @@ function getLineChartScales(definition, args) {
29563
29584
  scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29564
29585
  scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29565
29586
  scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29587
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID]["type"] = "category";
29588
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID]["offset"] = false;
29566
29589
  }
29567
29590
  }
29568
29591
  return scales;
@@ -29881,9 +29904,7 @@ function getBarChartTooltip(definition, args) {
29881
29904
  external: customTooltipHandler,
29882
29905
  callbacks: {
29883
29906
  title: function (tooltipItems) {
29884
- return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
29885
- ? undefined
29886
- : "";
29907
+ return tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID)) ? undefined : "";
29887
29908
  },
29888
29909
  beforeLabel: (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label,
29889
29910
  label: function (tooltipItem) {
@@ -29910,7 +29931,7 @@ function getLineChartTooltip(definition, args) {
29910
29931
  if (axisType === "linear") {
29911
29932
  tooltip.callbacks.label = (tooltipItem) => {
29912
29933
  const dataSetPoint = tooltipItem.parsed.y;
29913
- let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29934
+ let label = isTrendLineAxis(tooltipItem.dataset.xAxisID)
29914
29935
  ? ""
29915
29936
  : tooltipItem.parsed.x;
29916
29937
  if (typeof label === "string" && isNumber(label, locale)) {
@@ -29932,8 +29953,7 @@ function getLineChartTooltip(definition, args) {
29932
29953
  }
29933
29954
  tooltip.callbacks.beforeLabel = (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label;
29934
29955
  tooltip.callbacks.title = function (tooltipItems) {
29935
- const displayTooltipTitle = axisType !== "linear" &&
29936
- tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
29956
+ const displayTooltipTitle = axisType !== "linear" && tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID));
29937
29957
  return displayTooltipTitle ? undefined : "";
29938
29958
  };
29939
29959
  return tooltip;
@@ -34187,6 +34207,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34187
34207
  CHART_COMMON_OPTIONS: CHART_COMMON_OPTIONS,
34188
34208
  GaugeChart: GaugeChart,
34189
34209
  LineChart: LineChart,
34210
+ MOVING_AVERAGE_TREND_LINE_XAXIS_ID: MOVING_AVERAGE_TREND_LINE_XAXIS_ID,
34190
34211
  PieChart: PieChart,
34191
34212
  ScorecardChart: ScorecardChart$1,
34192
34213
  TREND_LINE_XAXIS_ID: TREND_LINE_XAXIS_ID,
@@ -34216,6 +34237,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34216
34237
  getDefinedAxis: getDefinedAxis,
34217
34238
  getPieColors: getPieColors,
34218
34239
  getSmartChartDefinition: getSmartChartDefinition,
34240
+ isTrendLineAxis: isTrendLineAxis,
34219
34241
  shouldRemoveFirstLabel: shouldRemoveFirstLabel,
34220
34242
  toExcelDataset: toExcelDataset,
34221
34243
  toExcelLabelRange: toExcelLabelRange,
@@ -36263,9 +36285,7 @@ class FormulaFingerprintStore extends SpreadsheetStore {
36263
36285
  }
36264
36286
  }
36265
36287
  }
36266
- // removes the index placeholders from the normalized formula
36267
- // =|N0|+|N1|+|N0| -> =|N|+|N|+|N|
36268
- const normalizedFormula = cell.compiledFormula.normalizedFormula.replace(/(|\w)(\d)(|)/g, "$1$3");
36288
+ const normalizedFormula = cell.compiledFormula.normalizedFormula;
36269
36289
  return hash(fingerprintVector) + normalizedFormula;
36270
36290
  }
36271
36291
  getLiteralFingerprint(position) {
@@ -39610,9 +39630,11 @@ class SeriesDesignEditor extends Component {
39610
39630
  if (!runtime || !("chartJsConfig" in runtime)) {
39611
39631
  return [];
39612
39632
  }
39613
- return runtime.chartJsConfig.data.datasets.map((d) => d.label);
39633
+ return runtime.chartJsConfig.data.datasets
39634
+ .filter((d) => !isTrendLineAxis(d["xAxisID"] ?? ""))
39635
+ .map((d) => d.label);
39614
39636
  }
39615
- updateSerieEditor(ev) {
39637
+ updateEditedSeries(ev) {
39616
39638
  this.state.index = ev.target.selectedIndex;
39617
39639
  }
39618
39640
  updateDataSeriesColor(color) {
@@ -39625,7 +39647,7 @@ class SeriesDesignEditor extends Component {
39625
39647
  };
39626
39648
  this.props.updateChart(this.props.figureId, { dataSets });
39627
39649
  }
39628
- getDataSerieColor() {
39650
+ getDataSeriesColor() {
39629
39651
  const dataSets = this.props.definition.dataSets;
39630
39652
  if (!dataSets?.[this.state.index])
39631
39653
  return "";
@@ -39645,7 +39667,7 @@ class SeriesDesignEditor extends Component {
39645
39667
  };
39646
39668
  this.props.updateChart(this.props.figureId, { dataSets });
39647
39669
  }
39648
- getDataSerieLabel() {
39670
+ getDataSeriesLabel() {
39649
39671
  const dataSets = this.props.definition.dataSets;
39650
39672
  return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
39651
39673
  }
@@ -39758,7 +39780,7 @@ class SeriesWithAxisDesignEditor extends Component {
39758
39780
  }
39759
39781
  this.updateTrendLineValue(index, { window });
39760
39782
  }
39761
- getDataSerieColor(index) {
39783
+ getDataSeriesColor(index) {
39762
39784
  const dataSets = this.props.definition.dataSets;
39763
39785
  if (!dataSets?.[index])
39764
39786
  return "";
@@ -39769,7 +39791,7 @@ class SeriesWithAxisDesignEditor extends Component {
39769
39791
  }
39770
39792
  getTrendLineColor(index) {
39771
39793
  return (this.getTrendLineConfiguration(index)?.color ??
39772
- setColorAlpha(this.getDataSerieColor(index), 0.5));
39794
+ setColorAlpha(this.getDataSeriesColor(index), 0.5));
39773
39795
  }
39774
39796
  updateTrendLineColor(index, color) {
39775
39797
  this.updateTrendLineValue(index, { color });
@@ -47170,6 +47192,7 @@ class RemoveDuplicatesPanel extends Component {
47170
47192
  columns: {},
47171
47193
  });
47172
47194
  setup() {
47195
+ this.updateColumns();
47173
47196
  onWillUpdateProps(() => this.updateColumns());
47174
47197
  }
47175
47198
  toggleHasHeader() {
@@ -48953,8 +48976,8 @@ class CellComposerStore extends AbstractComposerStore {
48953
48976
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
48954
48977
  if (!sheetIdExists && this.editionMode !== "inactive") {
48955
48978
  this.sheetId = this.getters.getActiveSheetId();
48956
- this.cancelEditionAndActivateSheet();
48957
48979
  this.resetContent();
48980
+ this.cancelEditionAndActivateSheet();
48958
48981
  this.notificationStore.raiseError(CELL_DELETED_MESSAGE);
48959
48982
  }
48960
48983
  break;
@@ -65382,12 +65405,7 @@ class SheetUIPlugin extends UIPlugin {
65382
65405
  }
65383
65406
  break;
65384
65407
  case "AUTORESIZE_ROWS":
65385
- this.dispatch("RESIZE_COLUMNS_ROWS", {
65386
- elements: cmd.rows,
65387
- dimension: "ROW",
65388
- size: null,
65389
- sheetId: cmd.sheetId,
65390
- });
65408
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
65391
65409
  break;
65392
65410
  }
65393
65411
  }
@@ -65551,6 +65569,48 @@ class SheetUIPlugin extends UIPlugin {
65551
65569
  }
65552
65570
  return "Success" /* CommandResult.Success */;
65553
65571
  }
65572
+ autoResizeRows(sheetId, rows) {
65573
+ const rowSizes = [];
65574
+ for (const row of rows) {
65575
+ let evaluatedRowSize = 0;
65576
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
65577
+ const cell = this.getters.getCellById(cellId);
65578
+ if (!cell) {
65579
+ continue;
65580
+ }
65581
+ const position = this.getters.getCellPosition(cell.id);
65582
+ const colSize = this.getters.getColSize(sheetId, position.col);
65583
+ if (cell.isFormula) {
65584
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
65585
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65586
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
65587
+ evaluatedRowSize = evaluatedSize;
65588
+ }
65589
+ }
65590
+ else {
65591
+ const content = cell.content;
65592
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65593
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
65594
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
65595
+ evaluatedRowSize = 0;
65596
+ }
65597
+ }
65598
+ }
65599
+ rowSizes.push(evaluatedRowSize || null);
65600
+ }
65601
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
65602
+ for (let i = 0; i < rowSizes.length; i++) {
65603
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
65604
+ }
65605
+ for (const [size, rows] of groupedSizes) {
65606
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
65607
+ elements: rows,
65608
+ dimension: "ROW",
65609
+ size,
65610
+ sheetId,
65611
+ });
65612
+ }
65613
+ }
65554
65614
  }
65555
65615
 
65556
65616
  class TableComputedStylePlugin extends UIPlugin {
@@ -70711,7 +70771,9 @@ css /* scss */ `
70711
70771
  border: 1px solid;
70712
70772
  font-family: ${DEFAULT_FONT};
70713
70773
 
70714
- .o-composer:empty:not(:focus):not(.active)::before {
70774
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
70775
+ .o-composer:empty:not(:focus):not(.active)::before,
70776
+ &.o-topbar-composer-readonly .o-composer:empty::before {
70715
70777
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
70716
70778
  position: relative;
70717
70779
  top: 20%;
@@ -74037,10 +74099,14 @@ function addIconSetRule(cf, rule) {
74037
74099
  continue;
74038
74100
  }
74039
74101
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
74102
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
74103
+ if (isIconSetReversed(rule.icons)) {
74104
+ iconSetAttrs.push(["reverse", "1"]);
74105
+ }
74040
74106
  conditionalFormats.push(escapeXml /*xml*/ `
74041
74107
  <conditionalFormatting sqref="${range}">
74042
74108
  <cfRule ${formatAttributes(ruleAttributes)}>
74043
- <iconSet iconSet="${getIconSet(rule.icons)}">
74109
+ <iconSet ${formatAttributes(iconSetAttrs)}>
74044
74110
  ${joinXmlNodes(cfValueObjectNodes)}
74045
74111
  </iconSet>
74046
74112
  </cfRule>
@@ -74058,9 +74124,21 @@ function commonCfAttributes(cf) {
74058
74124
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
74059
74125
  ];
74060
74126
  }
74127
+ function isIconSetReversed(iconSet) {
74128
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
74129
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
74130
+ }
74061
74131
  function getIconSet(iconSet) {
74062
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
74063
- "dots"];
74132
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
74133
+ }
74134
+ /**
74135
+ * Partial detection based on "upper" point only.
74136
+ * We support any arbitrary icon in the set, while excel doesn't allow
74137
+ * mixing icons from different types.
74138
+ */
74139
+ function detectIconsType(iconSet) {
74140
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
74141
+ return type;
74064
74142
  }
74065
74143
  function thresholdAttributes(threshold, position) {
74066
74144
  const type = getExcelThresholdType(threshold.type, position);
@@ -75965,6 +76043,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75965
76043
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75966
76044
 
75967
76045
 
75968
- __info__.version = "18.2.3";
75969
- __info__.date = "2025-03-12T15:32:36.274Z";
75970
- __info__.hash = "81b0e08";
76046
+ __info__.version = "18.2.5";
76047
+ __info__.date = "2025-03-26T12:47:44.113Z";
76048
+ __info__.hash = "4675edd";