@odoo/o-spreadsheet 18.1.11 → 18.1.13

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.1.11
6
- * @date 2025-03-12T15:31:44.276Z
7
- * @hash 7de2363
5
+ * @version 18.1.13
6
+ * @date 2025-03-26T12:48:31.680Z
7
+ * @hash 45ec54c
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -1120,7 +1120,10 @@ function rgbaStringToHex(color) {
1120
1120
  }
1121
1121
  else if (stringVals.length === 4) {
1122
1122
  const alpha = parseFloat(stringVals.pop() || "1");
1123
- alphaHex = Math.round((alpha || 1) * 255);
1123
+ if (isNaN(alpha)) {
1124
+ throw new Error("invalid alpha value");
1125
+ }
1126
+ alphaHex = Math.round(alpha * 255);
1124
1127
  }
1125
1128
  const vals = stringVals.map((val) => parseInt(val, 10));
1126
1129
  if (alphaHex !== 255) {
@@ -6276,11 +6279,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
6276
6279
  if (!cell || (!cell.isFormula && !cell.content)) {
6277
6280
  return DEFAULT_CELL_HEIGHT;
6278
6281
  }
6279
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6280
- const numberOfLines = cell.isFormula
6281
- ? 1
6282
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
6283
- const fontSize = computeTextFontSizeInPixels(cell.style);
6282
+ const content = cell.isFormula ? "" : cell.content;
6283
+ return getCellContentHeight(ctx, content, cell.style, colSize);
6284
+ }
6285
+ function getCellContentHeight(ctx, content, style, colSize) {
6286
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6287
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
6288
+ const fontSize = computeTextFontSizeInPixels(style);
6284
6289
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
6285
6290
  }
6286
6291
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
@@ -6988,7 +6993,7 @@ function isValidLocale(locale) {
6988
6993
  */
6989
6994
  function canonicalizeNumberContent(content, locale) {
6990
6995
  return content.startsWith("=")
6991
- ? canonicalizeFormula$1(content, locale)
6996
+ ? canonicalizeFormula(content, locale)
6992
6997
  : canonicalizeNumberLiteral(content, locale);
6993
6998
  }
6994
6999
  /**
@@ -7003,7 +7008,7 @@ function canonicalizeNumberContent(content, locale) {
7003
7008
  */
7004
7009
  function canonicalizeContent(content, locale) {
7005
7010
  return content.startsWith("=")
7006
- ? canonicalizeFormula$1(content, locale)
7011
+ ? canonicalizeFormula(content, locale)
7007
7012
  : canonicalizeLiteral(content, locale);
7008
7013
  }
7009
7014
  /**
@@ -7019,15 +7024,21 @@ function localizeContent(content, locale) {
7019
7024
  ? localizeFormula(content, locale)
7020
7025
  : localizeLiteral(content, locale);
7021
7026
  }
7027
+ /** Change a number string to its canonical form (en_US locale) */
7028
+ function canonicalizeNumberValue(content, locale) {
7029
+ return content.startsWith("=")
7030
+ ? canonicalizeFormula(content, locale)
7031
+ : canonicalizeNumberLiteral(content, locale);
7032
+ }
7022
7033
  /** Change a formula to its canonical form (en_US locale) */
7023
- function canonicalizeFormula$1(formula, locale) {
7024
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
7034
+ function canonicalizeFormula(formula, locale) {
7035
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7025
7036
  }
7026
7037
  /** Change a formula from the canonical form to the given locale */
7027
7038
  function localizeFormula(formula, locale) {
7028
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
7039
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
7029
7040
  }
7030
- function _localizeFormula$1(formula, fromLocale, toLocale) {
7041
+ function _localizeFormula(formula, fromLocale, toLocale) {
7031
7042
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7032
7043
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7033
7044
  return formula;
@@ -7182,37 +7193,6 @@ function getDateTimeFormat(locale) {
7182
7193
  return locale.dateFormat + " " + locale.timeFormat;
7183
7194
  }
7184
7195
 
7185
- /** Change a number string to its canonical form (en_US locale) */
7186
- function canonicalizeNumberValue(content, locale) {
7187
- return content.startsWith("=")
7188
- ? canonicalizeFormula(content, locale)
7189
- : canonicalizeNumberLiteral(content, locale);
7190
- }
7191
- /** Change a formula to its canonical form (en_US locale) */
7192
- function canonicalizeFormula(formula, locale) {
7193
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7194
- }
7195
- function _localizeFormula(formula, fromLocale, toLocale) {
7196
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7197
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7198
- return formula;
7199
- }
7200
- const tokens = tokenize(formula, fromLocale);
7201
- let localizedFormula = "";
7202
- for (const token of tokens) {
7203
- if (token.type === "NUMBER") {
7204
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7205
- }
7206
- else if (token.type === "ARG_SEPARATOR") {
7207
- localizedFormula += toLocale.formulaArgSeparator;
7208
- }
7209
- else {
7210
- localizedFormula += token.value;
7211
- }
7212
- }
7213
- return localizedFormula;
7214
- }
7215
-
7216
7196
  function boolAnd(args) {
7217
7197
  let foundBoolean = false;
7218
7198
  let acc = true;
@@ -8476,13 +8456,6 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8476
8456
  this.clearClippedZones(content);
8477
8457
  const selection = target[0];
8478
8458
  this.pasteZone(sheetId, selection.left, selection.top, content.cells, options);
8479
- this.dispatch("MOVE_RANGES", {
8480
- target: content.zones,
8481
- sheetId: content.sheetId,
8482
- targetSheetId: sheetId,
8483
- col: selection.left,
8484
- row: selection.top,
8485
- });
8486
8459
  }
8487
8460
  /**
8488
8461
  * Clear the clipped zones: remove the cells and clear the formatting
@@ -8991,14 +8964,15 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
8991
8964
  }
8992
8965
  merges.push(mergesInRow);
8993
8966
  }
8994
- return { merges };
8967
+ return { merges, sheetId };
8995
8968
  }
8996
8969
  /**
8997
8970
  * Paste the clipboard content in the given target
8998
8971
  */
8999
8972
  paste(target, content, options) {
9000
8973
  if (options.isCutOperation) {
9001
- return;
8974
+ const copiedMerges = content.merges.flat().filter(isDefined);
8975
+ this.dispatch("REMOVE_MERGE", { sheetId: content.sheetId, target: copiedMerges });
9002
8976
  }
9003
8977
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
9004
8978
  }
@@ -9033,6 +9007,27 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
9033
9007
  }
9034
9008
  }
9035
9009
 
9010
+ class ReferenceClipboardHandler extends AbstractCellClipboardHandler {
9011
+ copy(data) {
9012
+ return {
9013
+ zones: data.clippedZones,
9014
+ sheetId: data.sheetId,
9015
+ };
9016
+ }
9017
+ paste(target, content, options) {
9018
+ if (options.isCutOperation) {
9019
+ const selection = target.zones[0];
9020
+ this.dispatch("MOVE_RANGES", {
9021
+ target: content.zones,
9022
+ sheetId: content.sheetId,
9023
+ targetSheetId: target.sheetId,
9024
+ col: selection.left,
9025
+ row: selection.top,
9026
+ });
9027
+ }
9028
+ }
9029
+ }
9030
+
9036
9031
  class SheetClipboardHandler extends AbstractCellClipboardHandler {
9037
9032
  isPasteAllowed(sheetId, target, content, options) {
9038
9033
  if (!("cells" in content)) {
@@ -9196,7 +9191,8 @@ clipboardHandlersRegistries.cellHandlers
9196
9191
  .add("merge", MergeClipboardHandler)
9197
9192
  .add("border", BorderClipboardHandler)
9198
9193
  .add("table", TableClipboardHandler)
9199
- .add("conditionalFormat", ConditionalFormatClipboardHandler);
9194
+ .add("conditionalFormat", ConditionalFormatClipboardHandler)
9195
+ .add("references", ReferenceClipboardHandler);
9200
9196
 
9201
9197
  function transformZone(zone, executed) {
9202
9198
  if (executed.type === "REMOVE_COLUMNS_ROWS") {
@@ -9571,6 +9567,7 @@ class ComposerFocusStore extends SpreadsheetStore {
9571
9567
  }
9572
9568
 
9573
9569
  const TREND_LINE_XAXIS_ID = "x1";
9570
+ const MOVING_AVERAGE_TREND_LINE_XAXIS_ID = "xMovingAverage";
9574
9571
  /**
9575
9572
  * This file contains helpers that are common to different charts (mainly
9576
9573
  * line, bar and pie charts)
@@ -9911,6 +9908,9 @@ function getPieColors(colors, dataSetsValues) {
9911
9908
  }
9912
9909
  return pieColors;
9913
9910
  }
9911
+ function isTrendLineAxis(axisID) {
9912
+ return axisID === TREND_LINE_XAXIS_ID || axisID === MOVING_AVERAGE_TREND_LINE_XAXIS_ID;
9913
+ }
9914
9914
 
9915
9915
  /** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
9916
9916
  const chartShowValuesPlugin = {
@@ -9955,7 +9955,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
9955
9955
  const yMin = chart.chartArea.top;
9956
9956
  const textsPositions = {};
9957
9957
  for (const dataset of chart._metasets) {
9958
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID || dataset.hidden) {
9958
+ if (isTrendLineAxis(dataset.axisID) || dataset.hidden) {
9959
9959
  continue;
9960
9960
  }
9961
9961
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -9998,7 +9998,7 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
9998
9998
  const xMin = chart.chartArea.left;
9999
9999
  const textsPositions = {};
10000
10000
  for (const dataset of chart._metasets) {
10001
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
10001
+ if (isTrendLineAxis(dataset.axisID)) {
10002
10002
  return; // ignore trend lines
10003
10003
  }
10004
10004
  for (let i = 0; i < dataset._parsed.length; i++) {
@@ -20362,11 +20362,26 @@ const SEARCH = {
20362
20362
  const _searchFor = toString(searchFor).toLowerCase();
20363
20363
  const _textToSearch = toString(textToSearch).toLowerCase();
20364
20364
  const _startingAt = toNumber(startingAt, this.locale);
20365
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
20366
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
20365
+ if (_textToSearch === "") {
20366
+ return {
20367
+ value: CellErrorType.GenericError,
20368
+ message: _t("The text_to_search must be non-empty."),
20369
+ };
20370
+ }
20371
+ if (_startingAt < 1) {
20372
+ return {
20373
+ value: CellErrorType.GenericError,
20374
+ message: _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt),
20375
+ };
20376
+ }
20367
20377
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
20368
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
20369
- return result + 1;
20378
+ if (result === -1) {
20379
+ return {
20380
+ value: CellErrorType.GenericError,
20381
+ message: _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch),
20382
+ };
20383
+ }
20384
+ return { value: result + 1 };
20370
20385
  },
20371
20386
  isExported: true,
20372
20387
  };
@@ -20993,8 +21008,8 @@ class AbstractComposerStore extends SpreadsheetStore {
20993
21008
  this.computeParenthesisRelatedToCursor();
20994
21009
  }
20995
21010
  cancelEdition() {
20996
- this.cancelEditionAndActivateSheet();
20997
21011
  this.resetContent();
21012
+ this.cancelEditionAndActivateSheet();
20998
21013
  }
20999
21014
  setCurrentContent(content, selection) {
21000
21015
  if (selection && !this.isSelectionValid(content.length, selection.start, selection.end)) {
@@ -21012,8 +21027,8 @@ class AbstractComposerStore extends SpreadsheetStore {
21012
21027
  switch (cmd.type) {
21013
21028
  case "SELECT_FIGURE":
21014
21029
  if (cmd.id) {
21015
- this.cancelEditionAndActivateSheet();
21016
21030
  this.resetContent();
21031
+ this.cancelEditionAndActivateSheet();
21017
21032
  }
21018
21033
  break;
21019
21034
  case "START_CHANGE_HIGHLIGHT":
@@ -21701,11 +21716,14 @@ function compileTokens(tokens) {
21701
21716
  }
21702
21717
  }
21703
21718
  function compileTokensOrThrow(tokens) {
21704
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
21705
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
21719
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
21720
+ const cacheKey = compilationCacheKey(tokens);
21706
21721
  if (!functionCache[cacheKey]) {
21707
21722
  const ast = parseTokens([...tokens]);
21708
21723
  const scope = new Scope();
21724
+ let stringCount = 0;
21725
+ let numberCount = 0;
21726
+ let dependencyCount = 0;
21709
21727
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
21710
21728
  throw new BadExpressionError(_t("Invalid formula"));
21711
21729
  }
@@ -21779,16 +21797,15 @@ function compileTokensOrThrow(tokens) {
21779
21797
  case "BOOLEAN":
21780
21798
  return code.return(`{ value: ${ast.value} }`);
21781
21799
  case "NUMBER":
21782
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
21800
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
21783
21801
  case "STRING":
21784
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
21802
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
21785
21803
  case "REFERENCE":
21786
- const referenceIndex = dependencies.indexOf(ast.value);
21787
21804
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
21788
- return code.return(`range(deps[${referenceIndex}])`);
21805
+ return code.return(`range(deps[${dependencyCount++}])`);
21789
21806
  }
21790
21807
  else {
21791
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
21808
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
21792
21809
  }
21793
21810
  case "FUNCALL":
21794
21811
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -21820,7 +21837,7 @@ function compileTokensOrThrow(tokens) {
21820
21837
  const compiledFormula = {
21821
21838
  execute: functionCache[cacheKey],
21822
21839
  dependencies,
21823
- constantValues,
21840
+ literalValues,
21824
21841
  symbols,
21825
21842
  tokens,
21826
21843
  isBadExpression: false,
@@ -21833,33 +21850,31 @@ function compileTokensOrThrow(tokens) {
21833
21850
  * References, numbers and strings are replaced with placeholders because
21834
21851
  * the compiled formula does not depend on their actual value.
21835
21852
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
21836
- *
21837
21853
  * Spaces are also ignored to compute the cache key.
21838
21854
  *
21839
- * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|0|+|1|+SUM(|N0|,|N0|,|S0|)`
21855
+ * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|C|+|C|+SUM(|N|,|N|,|S|)`
21840
21856
  */
21841
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
21857
+ function compilationCacheKey(tokens) {
21842
21858
  let cacheKey = "";
21843
21859
  for (const token of tokens) {
21844
21860
  switch (token.type) {
21845
21861
  case "STRING":
21846
- const value = removeStringQuotes(token.value);
21847
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
21862
+ cacheKey += "|S|";
21848
21863
  break;
21849
21864
  case "NUMBER":
21850
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
21865
+ cacheKey += "|N|";
21851
21866
  break;
21852
21867
  case "REFERENCE":
21853
21868
  case "INVALID_REFERENCE":
21854
21869
  if (token.value.includes(":")) {
21855
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
21870
+ cacheKey += "|R|";
21856
21871
  }
21857
21872
  else {
21858
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
21873
+ cacheKey += "|C|";
21859
21874
  }
21860
21875
  break;
21861
21876
  case "SPACE":
21862
- cacheKey += "";
21877
+ // ignore spaces
21863
21878
  break;
21864
21879
  default:
21865
21880
  cacheKey += token.value;
@@ -21872,7 +21887,7 @@ function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
21872
21887
  * Return formula arguments which are references, strings and numbers.
21873
21888
  */
21874
21889
  function formulaArguments(tokens) {
21875
- const constantValues = {
21890
+ const literalValues = {
21876
21891
  numbers: [],
21877
21892
  strings: [],
21878
21893
  };
@@ -21886,15 +21901,11 @@ function formulaArguments(tokens) {
21886
21901
  break;
21887
21902
  case "STRING":
21888
21903
  const value = removeStringQuotes(token.value);
21889
- if (!constantValues.strings.includes(value)) {
21890
- constantValues.strings.push(value);
21891
- }
21904
+ literalValues.strings.push({ value });
21892
21905
  break;
21893
21906
  case "NUMBER": {
21894
21907
  const value = parseNumber(token.value, DEFAULT_LOCALE);
21895
- if (!constantValues.numbers.includes(value)) {
21896
- constantValues.numbers.push(value);
21897
- }
21908
+ literalValues.numbers.push({ value });
21898
21909
  break;
21899
21910
  }
21900
21911
  case "SYMBOL": {
@@ -21905,7 +21916,7 @@ function formulaArguments(tokens) {
21905
21916
  }
21906
21917
  return {
21907
21918
  dependencies,
21908
- constantValues,
21919
+ literalValues,
21909
21920
  symbols,
21910
21921
  };
21911
21922
  }
@@ -22329,7 +22340,7 @@ autoCompleteProviders.add("pivot_group_values", {
22329
22340
  text,
22330
22341
  description: usedLabel,
22331
22342
  htmlContent: [{ value: text, color }],
22332
- fuzzySearchKey: value + usedLabel,
22343
+ fuzzySearchKey: text + usedLabel,
22333
22344
  };
22334
22345
  });
22335
22346
  },
@@ -22826,9 +22837,9 @@ const XLSX_CHART_TYPES = [
22826
22837
  /** In XLSX color format (no #) */
22827
22838
  const AUTO_COLOR = "000000";
22828
22839
  const XLSX_ICONSET_MAP = {
22829
- arrow: "3Arrows",
22840
+ arrows: "3Arrows",
22830
22841
  smiley: "3Symbols",
22831
- dot: "3TrafficLights1",
22842
+ dots: "3TrafficLights1",
22832
22843
  };
22833
22844
  const NAMESPACE = {
22834
22845
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -23499,6 +23510,7 @@ const ICON_SET_CONVERSION_MAP = {
23499
23510
  };
23500
23511
  /** Map between legend position in XLSX file and human readable position */
23501
23512
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
23513
+ none: "none",
23502
23514
  b: "bottom",
23503
23515
  t: "top",
23504
23516
  l: "left",
@@ -26261,7 +26273,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26261
26273
  default: "ffffff",
26262
26274
  }).asString(),
26263
26275
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
26264
- default: "b",
26276
+ default: "none",
26265
26277
  }).asString()],
26266
26278
  stacked: barChartGrouping === "stacked",
26267
26279
  fontColor: "000000",
@@ -26295,7 +26307,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
26295
26307
  default: "ffffff",
26296
26308
  }).asString(),
26297
26309
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
26298
- default: "b",
26310
+ default: "none",
26299
26311
  }).asString()],
26300
26312
  stacked: barChartGrouping === "stacked",
26301
26313
  fontColor: "000000",
@@ -28907,7 +28919,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
28907
28919
  }
28908
28920
  }
28909
28921
  else if (dataSets.length === 1) {
28910
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
28922
+ const dataLength = getData(getters, dataSets[0]).length;
28923
+ for (let i = 0; i < dataLength; i++) {
28911
28924
  labels.formattedValues.push("");
28912
28925
  labels.values.push("");
28913
28926
  }
@@ -29090,7 +29103,7 @@ function getLineChartDatasets(definition, args) {
29090
29103
  function getScatterChartDatasets(definition, args) {
29091
29104
  const dataSets = getLineChartDatasets(definition, args);
29092
29105
  for (const dataSet of dataSets) {
29093
- if (dataSet.xAxisID !== TREND_LINE_XAXIS_ID) {
29106
+ if (!isTrendLineAxis(dataSet.xAxisID)) {
29094
29107
  dataSet.showLine = false;
29095
29108
  }
29096
29109
  }
@@ -29217,7 +29230,9 @@ function getTrendingLineDataSet(dataset, config, data) {
29217
29230
  const borderColor = config.color || lightenColor(rgbaToHex(defaultBorderColor), 0.5);
29218
29231
  return {
29219
29232
  type: "line",
29220
- xAxisID: TREND_LINE_XAXIS_ID,
29233
+ xAxisID: config.type === "trailingMovingAverage"
29234
+ ? MOVING_AVERAGE_TREND_LINE_XAXIS_ID
29235
+ : TREND_LINE_XAXIS_ID,
29221
29236
  yAxisID: dataset.yAxisID,
29222
29237
  label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
29223
29238
  data,
@@ -29292,22 +29307,19 @@ function getPieChartLegend(definition, args) {
29292
29307
  const { dataSetsValues } = args;
29293
29308
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
29294
29309
  const colors = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
29310
+ const fontColor = chartFontColor(definition.background);
29295
29311
  return {
29296
29312
  ...getLegendDisplayOptions(definition),
29297
29313
  labels: {
29298
- color: chartFontColor(definition.background),
29299
29314
  usePointStyle: true,
29300
- //@ts-ignore
29301
- generateLabels: (c) =>
29302
- //@ts-ignore
29303
- c.data.labels.map((label, index) => ({
29304
- text: label,
29315
+ generateLabels: (c) => c.data.labels?.map((label, index) => ({
29316
+ text: String(label),
29305
29317
  strokeStyle: colors[index],
29306
29318
  fillStyle: colors[index],
29307
29319
  pointStyle: "rect",
29308
- hidden: false,
29309
29320
  lineWidth: 2,
29310
- })),
29321
+ fontColor,
29322
+ })) || [],
29311
29323
  filter: (legendItem, data) => {
29312
29324
  return "datasetIndex" in legendItem
29313
29325
  ? !data.datasets[legendItem.datasetIndex].hidden
@@ -29440,7 +29452,7 @@ function getCustomLegendLabels(fontColor, legendLabelConfig) {
29440
29452
  color: fontColor,
29441
29453
  usePointStyle: true,
29442
29454
  generateLabels: (chart) => chart.data.datasets.map((dataset, index) => {
29443
- if (dataset["xAxisID"] === TREND_LINE_XAXIS_ID) {
29455
+ if (isTrendLineAxis(dataset["xAxisID"])) {
29444
29456
  return {
29445
29457
  text: dataset.label ?? "",
29446
29458
  fontColor,
@@ -29498,6 +29510,11 @@ function getBarChartScales(definition, args) {
29498
29510
  offset: false,
29499
29511
  display: false,
29500
29512
  };
29513
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29514
+ ...scales.x,
29515
+ offset: false,
29516
+ display: false,
29517
+ };
29501
29518
  }
29502
29519
  return scales;
29503
29520
  }
@@ -29531,6 +29548,10 @@ function getLineChartScales(definition, args) {
29531
29548
  ...scales.x,
29532
29549
  display: false,
29533
29550
  };
29551
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID] = {
29552
+ ...scales.x,
29553
+ display: false,
29554
+ };
29534
29555
  if (axisType === "category" || axisType === "time") {
29535
29556
  /* We add a second x axis here to draw the trend lines, with the labels length being
29536
29557
  * set so that the second axis points match the classical x axis
@@ -29539,6 +29560,8 @@ function getLineChartScales(definition, args) {
29539
29560
  scales[TREND_LINE_XAXIS_ID]["type"] = "category";
29540
29561
  scales[TREND_LINE_XAXIS_ID]["labels"] = range(0, maxLength).map((x) => x.toString());
29541
29562
  scales[TREND_LINE_XAXIS_ID]["offset"] = false;
29563
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID]["type"] = "category";
29564
+ scales[MOVING_AVERAGE_TREND_LINE_XAXIS_ID]["offset"] = false;
29542
29565
  }
29543
29566
  }
29544
29567
  return scales;
@@ -29784,9 +29807,7 @@ function getBarChartTooltip(definition, args) {
29784
29807
  return {
29785
29808
  callbacks: {
29786
29809
  title: function (tooltipItems) {
29787
- return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
29788
- ? undefined
29789
- : "";
29810
+ return tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID)) ? undefined : "";
29790
29811
  },
29791
29812
  label: function (tooltipItem) {
29792
29813
  const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
@@ -29809,7 +29830,7 @@ function getLineChartTooltip(definition, args) {
29809
29830
  if (axisType === "linear") {
29810
29831
  tooltip.callbacks.label = (tooltipItem) => {
29811
29832
  const dataSetPoint = tooltipItem.parsed.y;
29812
- let label = tooltipItem.dataset.xAxisID === TREND_LINE_XAXIS_ID
29833
+ let label = isTrendLineAxis(tooltipItem.dataset.xAxisID)
29813
29834
  ? ""
29814
29835
  : tooltipItem.parsed.x;
29815
29836
  if (typeof label === "string" && isNumber(label, locale)) {
@@ -29834,8 +29855,7 @@ function getLineChartTooltip(definition, args) {
29834
29855
  };
29835
29856
  }
29836
29857
  tooltip.callbacks.title = function (tooltipItems) {
29837
- const displayTooltipTitle = axisType !== "linear" &&
29838
- tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
29858
+ const displayTooltipTitle = axisType !== "linear" && tooltipItems.some((item) => !isTrendLineAxis(item.dataset.xAxisID));
29839
29859
  return displayTooltipTitle ? undefined : "";
29840
29860
  };
29841
29861
  return tooltip;
@@ -33992,6 +34012,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
33992
34012
  CHART_COMMON_OPTIONS: CHART_COMMON_OPTIONS,
33993
34013
  GaugeChart: GaugeChart,
33994
34014
  LineChart: LineChart,
34015
+ MOVING_AVERAGE_TREND_LINE_XAXIS_ID: MOVING_AVERAGE_TREND_LINE_XAXIS_ID,
33995
34016
  PieChart: PieChart,
33996
34017
  ScorecardChart: ScorecardChart$1,
33997
34018
  TREND_LINE_XAXIS_ID: TREND_LINE_XAXIS_ID,
@@ -34021,6 +34042,7 @@ var CHART_HELPERS = /*#__PURE__*/Object.freeze({
34021
34042
  getDefinedAxis: getDefinedAxis,
34022
34043
  getPieColors: getPieColors,
34023
34044
  getSmartChartDefinition: getSmartChartDefinition,
34045
+ isTrendLineAxis: isTrendLineAxis,
34024
34046
  shouldRemoveFirstLabel: shouldRemoveFirstLabel,
34025
34047
  toExcelDataset: toExcelDataset,
34026
34048
  toExcelLabelRange: toExcelLabelRange,
@@ -36068,9 +36090,7 @@ class FormulaFingerprintStore extends SpreadsheetStore {
36068
36090
  }
36069
36091
  }
36070
36092
  }
36071
- // removes the index placeholders from the normalized formula
36072
- // =|N0|+|N1|+|N0| -> =|N|+|N|+|N|
36073
- const normalizedFormula = cell.compiledFormula.normalizedFormula.replace(/(|\w)(\d)(|)/g, "$1$3");
36093
+ const normalizedFormula = cell.compiledFormula.normalizedFormula;
36074
36094
  return hash(fingerprintVector) + normalizedFormula;
36075
36095
  }
36076
36096
  getLiteralFingerprint(position) {
@@ -39061,9 +39081,11 @@ class SeriesDesignEditor extends Component {
39061
39081
  if (!runtime || !("chartJsConfig" in runtime)) {
39062
39082
  return [];
39063
39083
  }
39064
- return runtime.chartJsConfig.data.datasets.map((d) => d.label);
39084
+ return runtime.chartJsConfig.data.datasets
39085
+ .filter((d) => !isTrendLineAxis(d["xAxisID"] ?? ""))
39086
+ .map((d) => d.label);
39065
39087
  }
39066
- updateSerieEditor(ev) {
39088
+ updateEditedSeries(ev) {
39067
39089
  this.state.index = ev.target.selectedIndex;
39068
39090
  }
39069
39091
  updateDataSeriesColor(color) {
@@ -39076,7 +39098,7 @@ class SeriesDesignEditor extends Component {
39076
39098
  };
39077
39099
  this.props.updateChart(this.props.figureId, { dataSets });
39078
39100
  }
39079
- getDataSerieColor() {
39101
+ getDataSeriesColor() {
39080
39102
  const dataSets = this.props.definition.dataSets;
39081
39103
  if (!dataSets?.[this.state.index])
39082
39104
  return "";
@@ -39096,7 +39118,7 @@ class SeriesDesignEditor extends Component {
39096
39118
  };
39097
39119
  this.props.updateChart(this.props.figureId, { dataSets });
39098
39120
  }
39099
- getDataSerieLabel() {
39121
+ getDataSeriesLabel() {
39100
39122
  const dataSets = this.props.definition.dataSets;
39101
39123
  return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
39102
39124
  }
@@ -39209,7 +39231,7 @@ class SeriesWithAxisDesignEditor extends Component {
39209
39231
  }
39210
39232
  this.updateTrendLineValue(index, { window });
39211
39233
  }
39212
- getDataSerieColor(index) {
39234
+ getDataSeriesColor(index) {
39213
39235
  const dataSets = this.props.definition.dataSets;
39214
39236
  if (!dataSets?.[index])
39215
39237
  return "";
@@ -39220,7 +39242,7 @@ class SeriesWithAxisDesignEditor extends Component {
39220
39242
  }
39221
39243
  getTrendLineColor(index) {
39222
39244
  return (this.getTrendLineConfiguration(index)?.color ??
39223
- setColorAlpha(this.getDataSerieColor(index), 0.5));
39245
+ setColorAlpha(this.getDataSeriesColor(index), 0.5));
39224
39246
  }
39225
39247
  updateTrendLineColor(index, color) {
39226
39248
  this.updateTrendLineValue(index, { color });
@@ -46836,6 +46858,7 @@ class RemoveDuplicatesPanel extends Component {
46836
46858
  columns: {},
46837
46859
  });
46838
46860
  setup() {
46861
+ this.updateColumns();
46839
46862
  onWillUpdateProps(() => this.updateColumns());
46840
46863
  }
46841
46864
  toggleHasHeader() {
@@ -48623,8 +48646,8 @@ class CellComposerStore extends AbstractComposerStore {
48623
48646
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
48624
48647
  if (!sheetIdExists && this.editionMode !== "inactive") {
48625
48648
  this.sheetId = this.getters.getActiveSheetId();
48626
- this.cancelEditionAndActivateSheet();
48627
48649
  this.resetContent();
48650
+ this.cancelEditionAndActivateSheet();
48628
48651
  this.notificationStore.raiseError(CELL_DELETED_MESSAGE);
48629
48652
  }
48630
48653
  break;
@@ -64911,12 +64934,7 @@ class SheetUIPlugin extends UIPlugin {
64911
64934
  }
64912
64935
  break;
64913
64936
  case "AUTORESIZE_ROWS":
64914
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64915
- elements: cmd.rows,
64916
- dimension: "ROW",
64917
- size: null,
64918
- sheetId: cmd.sheetId,
64919
- });
64937
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
64920
64938
  break;
64921
64939
  }
64922
64940
  }
@@ -65080,6 +65098,48 @@ class SheetUIPlugin extends UIPlugin {
65080
65098
  }
65081
65099
  return "Success" /* CommandResult.Success */;
65082
65100
  }
65101
+ autoResizeRows(sheetId, rows) {
65102
+ const rowSizes = [];
65103
+ for (const row of rows) {
65104
+ let evaluatedRowSize = 0;
65105
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
65106
+ const cell = this.getters.getCellById(cellId);
65107
+ if (!cell) {
65108
+ continue;
65109
+ }
65110
+ const position = this.getters.getCellPosition(cell.id);
65111
+ const colSize = this.getters.getColSize(sheetId, position.col);
65112
+ if (cell.isFormula) {
65113
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
65114
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65115
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
65116
+ evaluatedRowSize = evaluatedSize;
65117
+ }
65118
+ }
65119
+ else {
65120
+ const content = cell.content;
65121
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65122
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
65123
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
65124
+ evaluatedRowSize = 0;
65125
+ }
65126
+ }
65127
+ }
65128
+ rowSizes.push(evaluatedRowSize || null);
65129
+ }
65130
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
65131
+ for (let i = 0; i < rowSizes.length; i++) {
65132
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
65133
+ }
65134
+ for (const [size, rows] of groupedSizes) {
65135
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
65136
+ elements: rows,
65137
+ dimension: "ROW",
65138
+ size,
65139
+ sheetId,
65140
+ });
65141
+ }
65142
+ }
65083
65143
  }
65084
65144
 
65085
65145
  class TableComputedStylePlugin extends UIPlugin {
@@ -70265,7 +70325,9 @@ css /* scss */ `
70265
70325
  border: 1px solid;
70266
70326
  font-family: ${DEFAULT_FONT};
70267
70327
 
70268
- .o-composer:empty:not(:focus):not(.active)::before {
70328
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
70329
+ .o-composer:empty:not(:focus):not(.active)::before,
70330
+ &.o-topbar-composer-readonly .o-composer:empty::before {
70269
70331
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
70270
70332
  position: relative;
70271
70333
  top: 20%;
@@ -73606,10 +73668,14 @@ function addIconSetRule(cf, rule) {
73606
73668
  continue;
73607
73669
  }
73608
73670
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
73671
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
73672
+ if (isIconSetReversed(rule.icons)) {
73673
+ iconSetAttrs.push(["reverse", "1"]);
73674
+ }
73609
73675
  conditionalFormats.push(escapeXml /*xml*/ `
73610
73676
  <conditionalFormatting sqref="${range}">
73611
73677
  <cfRule ${formatAttributes(ruleAttributes)}>
73612
- <iconSet iconSet="${getIconSet(rule.icons)}">
73678
+ <iconSet ${formatAttributes(iconSetAttrs)}>
73613
73679
  ${joinXmlNodes(cfValueObjectNodes)}
73614
73680
  </iconSet>
73615
73681
  </cfRule>
@@ -73627,9 +73693,21 @@ function commonCfAttributes(cf) {
73627
73693
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
73628
73694
  ];
73629
73695
  }
73696
+ function isIconSetReversed(iconSet) {
73697
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
73698
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
73699
+ }
73630
73700
  function getIconSet(iconSet) {
73631
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
73632
- "dots"];
73701
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
73702
+ }
73703
+ /**
73704
+ * Partial detection based on "upper" point only.
73705
+ * We support any arbitrary icon in the set, while excel doesn't allow
73706
+ * mixing icons from different types.
73707
+ */
73708
+ function detectIconsType(iconSet) {
73709
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
73710
+ return type;
73633
73711
  }
73634
73712
  function thresholdAttributes(threshold, position) {
73635
73713
  const type = getExcelThresholdType(threshold.type, position);
@@ -75502,6 +75580,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75502
75580
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, 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 };
75503
75581
 
75504
75582
 
75505
- __info__.version = "18.1.11";
75506
- __info__.date = "2025-03-12T15:31:44.276Z";
75507
- __info__.hash = "7de2363";
75583
+ __info__.version = "18.1.13";
75584
+ __info__.date = "2025-03-26T12:48:31.680Z";
75585
+ __info__.hash = "45ec54c";