@odoo/o-spreadsheet 18.0.19 → 18.0.21

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.0.19
6
- * @date 2025-03-12T15:33:28.300Z
7
- * @hash d8dea1b
5
+ * @version 18.0.21
6
+ * @date 2025-03-26T12:49:46.872Z
7
+ * @hash c687e1c
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';
@@ -1111,7 +1111,10 @@ function rgbaStringToHex(color) {
1111
1111
  }
1112
1112
  else if (stringVals.length === 4) {
1113
1113
  const alpha = parseFloat(stringVals.pop() || "1");
1114
- alphaHex = Math.round((alpha || 1) * 255);
1114
+ if (isNaN(alpha)) {
1115
+ throw new Error("invalid alpha value");
1116
+ }
1117
+ alphaHex = Math.round(alpha * 255);
1115
1118
  }
1116
1119
  const vals = stringVals.map((val) => parseInt(val, 10));
1117
1120
  if (alphaHex !== 255) {
@@ -6107,11 +6110,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
6107
6110
  if (!cell || (!cell.isFormula && !cell.content)) {
6108
6111
  return DEFAULT_CELL_HEIGHT;
6109
6112
  }
6110
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6111
- const numberOfLines = cell.isFormula
6112
- ? 1
6113
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
6114
- const fontSize = computeTextFontSizeInPixels(cell.style);
6113
+ const content = cell.isFormula ? "" : cell.content;
6114
+ return getCellContentHeight(ctx, content, cell.style, colSize);
6115
+ }
6116
+ function getCellContentHeight(ctx, content, style, colSize) {
6117
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6118
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
6119
+ const fontSize = computeTextFontSizeInPixels(style);
6115
6120
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
6116
6121
  }
6117
6122
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
@@ -6833,7 +6838,7 @@ function isValidLocale(locale) {
6833
6838
  */
6834
6839
  function canonicalizeNumberContent(content, locale) {
6835
6840
  return content.startsWith("=")
6836
- ? canonicalizeFormula$1(content, locale)
6841
+ ? canonicalizeFormula(content, locale)
6837
6842
  : canonicalizeNumberLiteral(content, locale);
6838
6843
  }
6839
6844
  /**
@@ -6848,7 +6853,7 @@ function canonicalizeNumberContent(content, locale) {
6848
6853
  */
6849
6854
  function canonicalizeContent(content, locale) {
6850
6855
  return content.startsWith("=")
6851
- ? canonicalizeFormula$1(content, locale)
6856
+ ? canonicalizeFormula(content, locale)
6852
6857
  : canonicalizeLiteral(content, locale);
6853
6858
  }
6854
6859
  /**
@@ -6864,15 +6869,21 @@ function localizeContent(content, locale) {
6864
6869
  ? localizeFormula(content, locale)
6865
6870
  : localizeLiteral(content, locale);
6866
6871
  }
6872
+ /** Change a number string to its canonical form (en_US locale) */
6873
+ function canonicalizeNumberValue(content, locale) {
6874
+ return content.startsWith("=")
6875
+ ? canonicalizeFormula(content, locale)
6876
+ : canonicalizeNumberLiteral(content, locale);
6877
+ }
6867
6878
  /** Change a formula to its canonical form (en_US locale) */
6868
- function canonicalizeFormula$1(formula, locale) {
6869
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
6879
+ function canonicalizeFormula(formula, locale) {
6880
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
6870
6881
  }
6871
6882
  /** Change a formula from the canonical form to the given locale */
6872
6883
  function localizeFormula(formula, locale) {
6873
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
6884
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
6874
6885
  }
6875
- function _localizeFormula$1(formula, fromLocale, toLocale) {
6886
+ function _localizeFormula(formula, fromLocale, toLocale) {
6876
6887
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
6877
6888
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
6878
6889
  return formula;
@@ -7028,37 +7039,6 @@ function getDateTimeFormat(locale) {
7028
7039
  return locale.dateFormat + " " + locale.timeFormat;
7029
7040
  }
7030
7041
 
7031
- /** Change a number string to its canonical form (en_US locale) */
7032
- function canonicalizeNumberValue(content, locale) {
7033
- return content.startsWith("=")
7034
- ? canonicalizeFormula(content, locale)
7035
- : canonicalizeNumberLiteral(content, locale);
7036
- }
7037
- /** Change a formula to its canonical form (en_US locale) */
7038
- function canonicalizeFormula(formula, locale) {
7039
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7040
- }
7041
- function _localizeFormula(formula, fromLocale, toLocale) {
7042
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7043
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7044
- return formula;
7045
- }
7046
- const tokens = tokenize(formula, fromLocale);
7047
- let localizedFormula = "";
7048
- for (const token of tokens) {
7049
- if (token.type === "NUMBER") {
7050
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
7051
- }
7052
- else if (token.type === "ARG_SEPARATOR") {
7053
- localizedFormula += toLocale.formulaArgSeparator;
7054
- }
7055
- else {
7056
- localizedFormula += token.value;
7057
- }
7058
- }
7059
- return localizedFormula;
7060
- }
7061
-
7062
7042
  function boolAnd(args) {
7063
7043
  let foundBoolean = false;
7064
7044
  let acc = true;
@@ -8274,13 +8254,6 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8274
8254
  this.clearClippedZones(content);
8275
8255
  const selection = target[0];
8276
8256
  this.pasteZone(sheetId, selection.left, selection.top, content.cells, options);
8277
- this.dispatch("MOVE_RANGES", {
8278
- target: content.zones,
8279
- sheetId: content.sheetId,
8280
- targetSheetId: sheetId,
8281
- col: selection.left,
8282
- row: selection.top,
8283
- });
8284
8257
  }
8285
8258
  /**
8286
8259
  * Clear the clipped zones: remove the cells and clear the formatting
@@ -8789,14 +8762,15 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
8789
8762
  }
8790
8763
  merges.push(mergesInRow);
8791
8764
  }
8792
- return { merges };
8765
+ return { merges, sheetId };
8793
8766
  }
8794
8767
  /**
8795
8768
  * Paste the clipboard content in the given target
8796
8769
  */
8797
8770
  paste(target, content, options) {
8798
8771
  if (options.isCutOperation) {
8799
- return;
8772
+ const copiedMerges = content.merges.flat().filter(isDefined);
8773
+ this.dispatch("REMOVE_MERGE", { sheetId: content.sheetId, target: copiedMerges });
8800
8774
  }
8801
8775
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
8802
8776
  }
@@ -8831,6 +8805,27 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
8831
8805
  }
8832
8806
  }
8833
8807
 
8808
+ class ReferenceClipboardHandler extends AbstractCellClipboardHandler {
8809
+ copy(data) {
8810
+ return {
8811
+ zones: data.clippedZones,
8812
+ sheetId: data.sheetId,
8813
+ };
8814
+ }
8815
+ paste(target, content, options) {
8816
+ if (options.isCutOperation) {
8817
+ const selection = target.zones[0];
8818
+ this.dispatch("MOVE_RANGES", {
8819
+ target: content.zones,
8820
+ sheetId: content.sheetId,
8821
+ targetSheetId: target.sheetId,
8822
+ col: selection.left,
8823
+ row: selection.top,
8824
+ });
8825
+ }
8826
+ }
8827
+ }
8828
+
8834
8829
  class SheetClipboardHandler extends AbstractCellClipboardHandler {
8835
8830
  isPasteAllowed(sheetId, target, content, options) {
8836
8831
  if (!("cells" in content)) {
@@ -8998,7 +8993,8 @@ clipboardHandlersRegistries.cellHandlers
8998
8993
  .add("merge", MergeClipboardHandler)
8999
8994
  .add("border", BorderClipboardHandler)
9000
8995
  .add("table", TableClipboardHandler)
9001
- .add("conditionalFormat", ConditionalFormatClipboardHandler);
8996
+ .add("conditionalFormat", ConditionalFormatClipboardHandler)
8997
+ .add("references", ReferenceClipboardHandler);
9002
8998
 
9003
8999
  function transformZone(zone, executed) {
9004
9000
  if (executed.type === "REMOVE_COLUMNS_ROWS") {
@@ -10053,9 +10049,9 @@ const XLSX_CHART_TYPES = [
10053
10049
  /** In XLSX color format (no #) */
10054
10050
  const AUTO_COLOR = "000000";
10055
10051
  const XLSX_ICONSET_MAP = {
10056
- arrow: "3Arrows",
10052
+ arrows: "3Arrows",
10057
10053
  smiley: "3Symbols",
10058
- dot: "3TrafficLights1",
10054
+ dots: "3TrafficLights1",
10059
10055
  };
10060
10056
  const NAMESPACE = {
10061
10057
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -10727,6 +10723,7 @@ const ICON_SET_CONVERSION_MAP = {
10727
10723
  };
10728
10724
  /** Map between legend position in XLSX file and human readable position */
10729
10725
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
10726
+ none: "none",
10730
10727
  b: "bottom",
10731
10728
  t: "top",
10732
10729
  l: "left",
@@ -13483,7 +13480,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
13483
13480
  default: "ffffff",
13484
13481
  }).asString(),
13485
13482
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
13486
- default: "b",
13483
+ default: "none",
13487
13484
  }).asString()],
13488
13485
  stacked: barChartGrouping === "stacked",
13489
13486
  fontColor: "000000",
@@ -13517,7 +13514,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
13517
13514
  default: "ffffff",
13518
13515
  }).asString(),
13519
13516
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(chartElement, "c:legendPos", "val", {
13520
- default: "b",
13517
+ default: "none",
13521
13518
  }).asString()],
13522
13519
  stacked: barChartGrouping === "stacked",
13523
13520
  fontColor: "000000",
@@ -16709,7 +16706,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
16709
16706
  }
16710
16707
  }
16711
16708
  else if (dataSets.length === 1) {
16712
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
16709
+ const dataLength = getData(getters, dataSets[0]).length;
16710
+ for (let i = 0; i < dataLength; i++) {
16713
16711
  labels.formattedValues.push("");
16714
16712
  labels.values.push("");
16715
16713
  }
@@ -25955,11 +25953,26 @@ const SEARCH = {
25955
25953
  const _searchFor = toString(searchFor).toLowerCase();
25956
25954
  const _textToSearch = toString(textToSearch).toLowerCase();
25957
25955
  const _startingAt = toNumber(startingAt, this.locale);
25958
- assert(() => _textToSearch !== "", _t("The text_to_search must be non-empty."));
25959
- assert(() => _startingAt >= 1, _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt.toString()));
25956
+ if (_textToSearch === "") {
25957
+ return {
25958
+ value: CellErrorType.GenericError,
25959
+ message: _t("The text_to_search must be non-empty."),
25960
+ };
25961
+ }
25962
+ if (_startingAt < 1) {
25963
+ return {
25964
+ value: CellErrorType.GenericError,
25965
+ message: _t("The starting_at (%s) must be greater than or equal to 1.", _startingAt),
25966
+ };
25967
+ }
25960
25968
  const result = _textToSearch.indexOf(_searchFor, _startingAt - 1);
25961
- assert(() => result >= 0, _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch));
25962
- return result + 1;
25969
+ if (result === -1) {
25970
+ return {
25971
+ value: CellErrorType.GenericError,
25972
+ message: _t("In [[FUNCTION_NAME]] evaluation, cannot find '%s' within '%s'.", _searchFor, _textToSearch),
25973
+ };
25974
+ }
25975
+ return { value: result + 1 };
25963
25976
  },
25964
25977
  isExported: true,
25965
25978
  };
@@ -27809,11 +27822,14 @@ function compileTokens(tokens) {
27809
27822
  }
27810
27823
  }
27811
27824
  function compileTokensOrThrow(tokens) {
27812
- const { dependencies, constantValues, symbols } = formulaArguments(tokens);
27813
- const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
27825
+ const { dependencies, literalValues, symbols } = formulaArguments(tokens);
27826
+ const cacheKey = compilationCacheKey(tokens);
27814
27827
  if (!functionCache[cacheKey]) {
27815
27828
  const ast = parseTokens([...tokens]);
27816
27829
  const scope = new Scope();
27830
+ let stringCount = 0;
27831
+ let numberCount = 0;
27832
+ let dependencyCount = 0;
27817
27833
  if (ast.type === "BIN_OPERATION" && ast.value === ":") {
27818
27834
  throw new BadExpressionError(_t("Invalid formula"));
27819
27835
  }
@@ -27886,16 +27902,15 @@ function compileTokensOrThrow(tokens) {
27886
27902
  case "BOOLEAN":
27887
27903
  return code.return(`{ value: ${ast.value} }`);
27888
27904
  case "NUMBER":
27889
- return code.return(`{ value: this.constantValues.numbers[${constantValues.numbers.indexOf(ast.value)}] }`);
27905
+ return code.return(`this.literalValues.numbers[${numberCount++}]`);
27890
27906
  case "STRING":
27891
- return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
27907
+ return code.return(`this.literalValues.strings[${stringCount++}]`);
27892
27908
  case "REFERENCE":
27893
- const referenceIndex = dependencies.indexOf(ast.value);
27894
27909
  if ((!isMeta && ast.value.includes(":")) || hasRange) {
27895
- return code.return(`range(deps[${referenceIndex}])`);
27910
+ return code.return(`range(deps[${dependencyCount++}])`);
27896
27911
  }
27897
27912
  else {
27898
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
27913
+ return code.return(`ref(deps[${dependencyCount++}], ${isMeta ? "true" : "false"})`);
27899
27914
  }
27900
27915
  case "FUNCALL":
27901
27916
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -27927,7 +27942,7 @@ function compileTokensOrThrow(tokens) {
27927
27942
  const compiledFormula = {
27928
27943
  execute: functionCache[cacheKey],
27929
27944
  dependencies,
27930
- constantValues,
27945
+ literalValues,
27931
27946
  symbols,
27932
27947
  tokens,
27933
27948
  isBadExpression: false,
@@ -27939,33 +27954,31 @@ function compileTokensOrThrow(tokens) {
27939
27954
  * References, numbers and strings are replaced with placeholders because
27940
27955
  * the compiled formula does not depend on their actual value.
27941
27956
  * Both `=A1+1+"2"` and `=A2+2+"3"` are compiled to the exact same function.
27942
- *
27943
27957
  * Spaces are also ignored to compute the cache key.
27944
27958
  *
27945
- * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|0|+|1|+SUM(|N0|,|N0|,|S0|)`
27959
+ * A formula `=A1+A2+SUM(2, 2, "2")` have the cache key `=|C|+|C|+SUM(|N|,|N|,|S|)`
27946
27960
  */
27947
- function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
27961
+ function compilationCacheKey(tokens) {
27948
27962
  let cacheKey = "";
27949
27963
  for (const token of tokens) {
27950
27964
  switch (token.type) {
27951
27965
  case "STRING":
27952
- const value = removeStringQuotes(token.value);
27953
- cacheKey += `|S${constantValues.strings.indexOf(value)}|`;
27966
+ cacheKey += "|S|";
27954
27967
  break;
27955
27968
  case "NUMBER":
27956
- cacheKey += `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
27969
+ cacheKey += "|N|";
27957
27970
  break;
27958
27971
  case "REFERENCE":
27959
27972
  case "INVALID_REFERENCE":
27960
27973
  if (token.value.includes(":")) {
27961
- cacheKey += `R|${dependencies.indexOf(token.value)}|`;
27974
+ cacheKey += "|R|";
27962
27975
  }
27963
27976
  else {
27964
- cacheKey += `C|${dependencies.indexOf(token.value)}|`;
27977
+ cacheKey += "|C|";
27965
27978
  }
27966
27979
  break;
27967
27980
  case "SPACE":
27968
- cacheKey += "";
27981
+ // ignore spaces
27969
27982
  break;
27970
27983
  default:
27971
27984
  cacheKey += token.value;
@@ -27978,7 +27991,7 @@ function compilationCacheKey(tokens, dependencies, constantValues, symbols) {
27978
27991
  * Return formula arguments which are references, strings and numbers.
27979
27992
  */
27980
27993
  function formulaArguments(tokens) {
27981
- const constantValues = {
27994
+ const literalValues = {
27982
27995
  numbers: [],
27983
27996
  strings: [],
27984
27997
  };
@@ -27992,15 +28005,11 @@ function formulaArguments(tokens) {
27992
28005
  break;
27993
28006
  case "STRING":
27994
28007
  const value = removeStringQuotes(token.value);
27995
- if (!constantValues.strings.includes(value)) {
27996
- constantValues.strings.push(value);
27997
- }
28008
+ literalValues.strings.push({ value });
27998
28009
  break;
27999
28010
  case "NUMBER": {
28000
28011
  const value = parseNumber(token.value, DEFAULT_LOCALE);
28001
- if (!constantValues.numbers.includes(value)) {
28002
- constantValues.numbers.push(value);
28003
- }
28012
+ literalValues.numbers.push({ value });
28004
28013
  break;
28005
28014
  }
28006
28015
  case "SYMBOL": {
@@ -28011,7 +28020,7 @@ function formulaArguments(tokens) {
28011
28020
  }
28012
28021
  return {
28013
28022
  dependencies,
28014
- constantValues,
28023
+ literalValues,
28015
28024
  symbols,
28016
28025
  };
28017
28026
  }
@@ -28435,7 +28444,7 @@ autoCompleteProviders.add("pivot_group_values", {
28435
28444
  text,
28436
28445
  description: usedLabel,
28437
28446
  htmlContent: [{ value: text, color }],
28438
- fuzzySearchKey: value + usedLabel,
28447
+ fuzzySearchKey: text + usedLabel,
28439
28448
  };
28440
28449
  });
28441
28450
  },
@@ -38838,8 +38847,8 @@ class AbstractComposerStore extends SpreadsheetStore {
38838
38847
  this.updateRangeColor();
38839
38848
  }
38840
38849
  cancelEdition() {
38841
- this.cancelEditionAndActivateSheet();
38842
38850
  this.resetContent();
38851
+ this.cancelEditionAndActivateSheet();
38843
38852
  }
38844
38853
  setCurrentContent(content, selection) {
38845
38854
  if (selection && !this.isSelectionValid(content.length, selection.start, selection.end)) {
@@ -38855,8 +38864,8 @@ class AbstractComposerStore extends SpreadsheetStore {
38855
38864
  switch (cmd.type) {
38856
38865
  case "SELECT_FIGURE":
38857
38866
  if (cmd.id) {
38858
- this.cancelEditionAndActivateSheet();
38859
38867
  this.resetContent();
38868
+ this.cancelEditionAndActivateSheet();
38860
38869
  }
38861
38870
  break;
38862
38871
  case "START_CHANGE_HIGHLIGHT":
@@ -44751,6 +44760,7 @@ class RemoveDuplicatesPanel extends Component {
44751
44760
  columns: {},
44752
44761
  });
44753
44762
  setup() {
44763
+ this.updateColumns();
44754
44764
  onWillUpdateProps(() => this.updateColumns());
44755
44765
  }
44756
44766
  toggleHasHeader() {
@@ -46539,8 +46549,8 @@ class CellComposerStore extends AbstractComposerStore {
46539
46549
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
46540
46550
  if (!sheetIdExists && this.editionMode !== "inactive") {
46541
46551
  this.sheetId = this.getters.getActiveSheetId();
46542
- this.cancelEditionAndActivateSheet();
46543
46552
  this.resetContent();
46553
+ this.cancelEditionAndActivateSheet();
46544
46554
  this.notificationStore.raiseError(CELL_DELETED_MESSAGE);
46545
46555
  }
46546
46556
  break;
@@ -62896,12 +62906,7 @@ class SheetUIPlugin extends UIPlugin {
62896
62906
  }
62897
62907
  break;
62898
62908
  case "AUTORESIZE_ROWS":
62899
- this.dispatch("RESIZE_COLUMNS_ROWS", {
62900
- elements: cmd.rows,
62901
- dimension: "ROW",
62902
- size: null,
62903
- sheetId: cmd.sheetId,
62904
- });
62909
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
62905
62910
  break;
62906
62911
  }
62907
62912
  }
@@ -63066,6 +63071,48 @@ class SheetUIPlugin extends UIPlugin {
63066
63071
  }
63067
63072
  return "Success" /* CommandResult.Success */;
63068
63073
  }
63074
+ autoResizeRows(sheetId, rows) {
63075
+ const rowSizes = [];
63076
+ for (const row of rows) {
63077
+ let evaluatedRowSize = 0;
63078
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
63079
+ const cell = this.getters.getCellById(cellId);
63080
+ if (!cell) {
63081
+ continue;
63082
+ }
63083
+ const position = this.getters.getCellPosition(cell.id);
63084
+ const colSize = this.getters.getColSize(sheetId, position.col);
63085
+ if (cell.isFormula) {
63086
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
63087
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
63088
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
63089
+ evaluatedRowSize = evaluatedSize;
63090
+ }
63091
+ }
63092
+ else {
63093
+ const content = cell.content;
63094
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
63095
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
63096
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
63097
+ evaluatedRowSize = 0;
63098
+ }
63099
+ }
63100
+ }
63101
+ rowSizes.push(evaluatedRowSize || null);
63102
+ }
63103
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
63104
+ for (let i = 0; i < rowSizes.length; i++) {
63105
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
63106
+ }
63107
+ for (const [size, rows] of groupedSizes) {
63108
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
63109
+ elements: rows,
63110
+ dimension: "ROW",
63111
+ size,
63112
+ sheetId,
63113
+ });
63114
+ }
63115
+ }
63069
63116
  }
63070
63117
 
63071
63118
  class TableComputedStylePlugin extends UIPlugin {
@@ -68241,7 +68288,9 @@ css /* scss */ `
68241
68288
  border: 1px solid;
68242
68289
  font-family: ${DEFAULT_FONT};
68243
68290
 
68244
- .o-composer:empty:not(:focus):not(.active)::before {
68291
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
68292
+ .o-composer:empty:not(:focus):not(.active)::before,
68293
+ &.o-topbar-composer-readonly .o-composer:empty::before {
68245
68294
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
68246
68295
  position: relative;
68247
68296
  top: 20%;
@@ -71602,10 +71651,14 @@ function addIconSetRule(cf, rule) {
71602
71651
  continue;
71603
71652
  }
71604
71653
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
71654
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
71655
+ if (isIconSetReversed(rule.icons)) {
71656
+ iconSetAttrs.push(["reverse", "1"]);
71657
+ }
71605
71658
  conditionalFormats.push(escapeXml /*xml*/ `
71606
71659
  <conditionalFormatting sqref="${range}">
71607
71660
  <cfRule ${formatAttributes(ruleAttributes)}>
71608
- <iconSet iconSet="${getIconSet(rule.icons)}">
71661
+ <iconSet ${formatAttributes(iconSetAttrs)}>
71609
71662
  ${joinXmlNodes(cfValueObjectNodes)}
71610
71663
  </iconSet>
71611
71664
  </cfRule>
@@ -71623,9 +71676,21 @@ function commonCfAttributes(cf) {
71623
71676
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
71624
71677
  ];
71625
71678
  }
71679
+ function isIconSetReversed(iconSet) {
71680
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
71681
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
71682
+ }
71626
71683
  function getIconSet(iconSet) {
71627
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
71628
- "dots"];
71684
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
71685
+ }
71686
+ /**
71687
+ * Partial detection based on "upper" point only.
71688
+ * We support any arbitrary icon in the set, while excel doesn't allow
71689
+ * mixing icons from different types.
71690
+ */
71691
+ function detectIconsType(iconSet) {
71692
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
71693
+ return type;
71629
71694
  }
71630
71695
  function thresholdAttributes(threshold, position) {
71631
71696
  const type = getExcelThresholdType(threshold.type, position);
@@ -73506,6 +73571,6 @@ const constants = {
73506
73571
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, 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 };
73507
73572
 
73508
73573
 
73509
- __info__.version = "18.0.19";
73510
- __info__.date = "2025-03-12T15:33:28.300Z";
73511
- __info__.hash = "d8dea1b";
73574
+ __info__.version = "18.0.21";
73575
+ __info__.date = "2025-03-26T12:49:46.872Z";
73576
+ __info__.hash = "c687e1c";