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