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