@odoo/o-spreadsheet 17.0.53 → 17.0.55

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 17.0.53
6
- * @date 2025-03-12T15:26:58.496Z
7
- * @hash 47dfd62
5
+ * @version 17.0.55
6
+ * @date 2025-03-26T12:44:29.690Z
7
+ * @hash c3a4769
8
8
  */
9
9
 
10
10
  'use strict';
@@ -949,7 +949,10 @@ function rgbaStringToHex(color) {
949
949
  }
950
950
  else if (stringVals.length === 4) {
951
951
  const alpha = parseFloat(stringVals.pop() || "1");
952
- alphaHex = Math.round((alpha || 1) * 255);
952
+ if (isNaN(alpha)) {
953
+ throw new Error("invalid alpha value");
954
+ }
955
+ alphaHex = Math.round(alpha * 255);
953
956
  }
954
957
  const vals = stringVals.map((val) => parseInt(val, 10));
955
958
  if (alphaHex !== 255) {
@@ -4494,11 +4497,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
4494
4497
  if (!cell || (!cell.isFormula && !cell.content)) {
4495
4498
  return DEFAULT_CELL_HEIGHT;
4496
4499
  }
4497
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4498
- const numberOfLines = cell.isFormula
4499
- ? 1
4500
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
4501
- const fontSize = computeTextFontSizeInPixels(cell.style);
4500
+ const content = cell.isFormula ? "" : cell.content;
4501
+ return getCellContentHeight(ctx, content, cell.style, colSize);
4502
+ }
4503
+ function getCellContentHeight(ctx, content, style, colSize) {
4504
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4505
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
4506
+ const fontSize = computeTextFontSizeInPixels(style);
4502
4507
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
4503
4508
  }
4504
4509
  const textWidthCache = {};
@@ -7769,7 +7774,7 @@ function isValidLocale(locale) {
7769
7774
  */
7770
7775
  function canonicalizeNumberContent(content, locale) {
7771
7776
  return content.startsWith("=")
7772
- ? canonicalizeFormula$1(content, locale)
7777
+ ? canonicalizeFormula(content, locale)
7773
7778
  : canonicalizeNumberLiteral(content, locale);
7774
7779
  }
7775
7780
  /**
@@ -7784,7 +7789,7 @@ function canonicalizeNumberContent(content, locale) {
7784
7789
  */
7785
7790
  function canonicalizeContent(content, locale) {
7786
7791
  return content.startsWith("=")
7787
- ? canonicalizeFormula$1(content, locale)
7792
+ ? canonicalizeFormula(content, locale)
7788
7793
  : canonicalizeLiteral(content, locale);
7789
7794
  }
7790
7795
  /**
@@ -7800,15 +7805,21 @@ function localizeContent(content, locale) {
7800
7805
  ? localizeFormula(content, locale)
7801
7806
  : localizeLiteral(content, locale);
7802
7807
  }
7808
+ /** Change a number string to its canonical form (en_US locale) */
7809
+ function canonicalizeNumberValue(content, locale) {
7810
+ return content.startsWith("=")
7811
+ ? canonicalizeFormula(content, locale)
7812
+ : canonicalizeNumberLiteral(content, locale);
7813
+ }
7803
7814
  /** Change a formula to its canonical form (en_US locale) */
7804
- function canonicalizeFormula$1(formula, locale) {
7805
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
7815
+ function canonicalizeFormula(formula, locale) {
7816
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7806
7817
  }
7807
7818
  /** Change a formula from the canonical form to the given locale */
7808
7819
  function localizeFormula(formula, locale) {
7809
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
7820
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
7810
7821
  }
7811
- function _localizeFormula$1(formula, fromLocale, toLocale) {
7822
+ function _localizeFormula(formula, fromLocale, toLocale) {
7812
7823
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7813
7824
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7814
7825
  return formula;
@@ -8562,7 +8573,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
8562
8573
  }
8563
8574
  }
8564
8575
  else if (dataSets.length === 1) {
8565
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
8576
+ const dataLength = getData(getters, dataSets[0]).length;
8577
+ for (let i = 0; i < dataLength; i++) {
8566
8578
  labels.formattedValues.push("");
8567
8579
  labels.values.push("");
8568
8580
  }
@@ -24292,6 +24304,7 @@ class RemoveDuplicatesPanel extends owl.Component {
24292
24304
  columns: {},
24293
24305
  });
24294
24306
  setup() {
24307
+ this.updateColumns();
24295
24308
  owl.onWillUpdateProps(() => this.updateColumns());
24296
24309
  }
24297
24310
  toggleHasHeader() {
@@ -30362,9 +30375,9 @@ const XLSX_CHART_TYPES = [
30362
30375
  /** In XLSX color format (no #) */
30363
30376
  const AUTO_COLOR = "000000";
30364
30377
  const XLSX_ICONSET_MAP = {
30365
- arrow: "3Arrows",
30378
+ arrows: "3Arrows",
30366
30379
  smiley: "3Symbols",
30367
- dot: "3TrafficLights1",
30380
+ dots: "3TrafficLights1",
30368
30381
  };
30369
30382
  const NAMESPACE = {
30370
30383
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -30780,6 +30793,7 @@ const ICON_SET_CONVERSION_MAP = {
30780
30793
  };
30781
30794
  /** Map between legend position in XLSX file and human readable position */
30782
30795
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
30796
+ none: "none",
30783
30797
  b: "bottom",
30784
30798
  t: "top",
30785
30799
  l: "left",
@@ -33012,7 +33026,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
33012
33026
  ? "right"
33013
33027
  : "left",
33014
33028
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
33015
- default: "b",
33029
+ default: "none",
33016
33030
  }).asString()],
33017
33031
  stacked: barChartGrouping === "stacked",
33018
33032
  fontColor: "000000",
@@ -45470,12 +45484,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45470
45484
  continue;
45471
45485
  }
45472
45486
  const position = { col: col + c, row: row + r, sheetId: sheetId };
45473
- // TODO: refactor this part. the "Paste merge" action is also executed with
45474
- // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
45475
- // is not appropriate
45476
- if (this.operation !== "CUT") {
45477
- this.pasteMergeIfExist(origin.position, position);
45478
- }
45487
+ this.pasteMergeIfExist(origin.position, position, this.operation);
45479
45488
  this.pasteCell(origin, position, this.operation, clipboardOptions);
45480
45489
  if (shouldPasteCF) {
45481
45490
  this.pasteCf(origin.position, position);
@@ -45532,7 +45541,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45532
45541
  * If the origin position given is the top left of a merge, merge the target
45533
45542
  * position.
45534
45543
  */
45535
- pasteMergeIfExist(origin, target) {
45544
+ pasteMergeIfExist(origin, target, operation) {
45536
45545
  let { sheetId, col, row } = origin;
45537
45546
  const { col: mainCellColOrigin, row: mainCellRowOrigin } = this.getters.getMainCellPosition(origin);
45538
45547
  if (mainCellColOrigin === col && mainCellRowOrigin === row) {
@@ -45540,6 +45549,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45540
45549
  if (!merge) {
45541
45550
  return;
45542
45551
  }
45552
+ if (operation === "CUT") {
45553
+ this.dispatch("REMOVE_MERGE", { sheetId, target: [merge] });
45554
+ }
45543
45555
  ({ sheetId, col, row } = target);
45544
45556
  this.dispatch("ADD_MERGE", {
45545
45557
  sheetId,
@@ -47831,12 +47843,7 @@ class SheetUIPlugin extends UIPlugin {
47831
47843
  }
47832
47844
  break;
47833
47845
  case "AUTORESIZE_ROWS":
47834
- this.dispatch("RESIZE_COLUMNS_ROWS", {
47835
- elements: cmd.rows,
47836
- dimension: "ROW",
47837
- size: null,
47838
- sheetId: cmd.sheetId,
47839
- });
47846
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
47840
47847
  break;
47841
47848
  }
47842
47849
  }
@@ -47934,6 +47941,48 @@ class SheetUIPlugin extends UIPlugin {
47934
47941
  }
47935
47942
  return "Success" /* CommandResult.Success */;
47936
47943
  }
47944
+ autoResizeRows(sheetId, rows) {
47945
+ const rowSizes = [];
47946
+ for (const row of rows) {
47947
+ let evaluatedRowSize = 0;
47948
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
47949
+ const cell = this.getters.getCellById(cellId);
47950
+ if (!cell) {
47951
+ continue;
47952
+ }
47953
+ const position = this.getters.getCellPosition(cell.id);
47954
+ const colSize = this.getters.getColSize(sheetId, position.col);
47955
+ if (cell.isFormula) {
47956
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
47957
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
47958
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
47959
+ evaluatedRowSize = evaluatedSize;
47960
+ }
47961
+ }
47962
+ else {
47963
+ const content = cell.content;
47964
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
47965
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
47966
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
47967
+ evaluatedRowSize = 0;
47968
+ }
47969
+ }
47970
+ }
47971
+ rowSizes.push(evaluatedRowSize || null);
47972
+ }
47973
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
47974
+ for (let i = 0; i < rowSizes.length; i++) {
47975
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
47976
+ }
47977
+ for (const [size, rows] of groupedSizes) {
47978
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
47979
+ elements: rows,
47980
+ dimension: "ROW",
47981
+ size,
47982
+ sheetId,
47983
+ });
47984
+ }
47985
+ }
47937
47986
  }
47938
47987
 
47939
47988
  const genericRepeatsTransforms = [
@@ -48583,37 +48632,6 @@ class ClipboardFigureImage {
48583
48632
  }
48584
48633
  }
48585
48634
 
48586
- /** Change a number string to its canonical form (en_US locale) */
48587
- function canonicalizeNumberValue(content, locale) {
48588
- return content.startsWith("=")
48589
- ? canonicalizeFormula(content, locale)
48590
- : canonicalizeNumberLiteral(content, locale);
48591
- }
48592
- /** Change a formula to its canonical form (en_US locale) */
48593
- function canonicalizeFormula(formula, locale) {
48594
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
48595
- }
48596
- function _localizeFormula(formula, fromLocale, toLocale) {
48597
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
48598
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
48599
- return formula;
48600
- }
48601
- const tokens = tokenize(formula, fromLocale);
48602
- let localizedFormula = "";
48603
- for (const token of tokens) {
48604
- if (token.type === "NUMBER") {
48605
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
48606
- }
48607
- else if (token.type === "ARG_SEPARATOR") {
48608
- localizedFormula += toLocale.formulaArgSeparator;
48609
- }
48610
- else {
48611
- localizedFormula += token.value;
48612
- }
48613
- }
48614
- return localizedFormula;
48615
- }
48616
-
48617
48635
  /** State of the clipboard when copying/cutting from the OS clipboard*/
48618
48636
  class ClipboardOsState extends ClipboardCellsAbstractState {
48619
48637
  values;
@@ -49190,8 +49208,8 @@ class EditionPlugin extends UIPlugin {
49190
49208
  this.colorIndexByRange = {};
49191
49209
  break;
49192
49210
  case "CANCEL_EDITION":
49193
- this.cancelEditionAndActivateSheet();
49194
49211
  this.resetContent();
49212
+ this.cancelEditionAndActivateSheet();
49195
49213
  this.colorIndexByRange = {};
49196
49214
  break;
49197
49215
  case "SET_CURRENT_CONTENT":
@@ -49202,8 +49220,8 @@ class EditionPlugin extends UIPlugin {
49202
49220
  this.replaceSelection(cmd.text);
49203
49221
  break;
49204
49222
  case "SELECT_FIGURE":
49205
- this.cancelEditionAndActivateSheet();
49206
49223
  this.resetContent();
49224
+ this.cancelEditionAndActivateSheet();
49207
49225
  break;
49208
49226
  case "ADD_COLUMNS_ROWS":
49209
49227
  this.onAddElements(cmd);
@@ -49246,8 +49264,8 @@ class EditionPlugin extends UIPlugin {
49246
49264
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
49247
49265
  if (!sheetIdExists && this.mode !== "inactive") {
49248
49266
  this.sheetId = this.getters.getActiveSheetId();
49249
- this.cancelEditionAndActivateSheet();
49250
49267
  this.resetContent();
49268
+ this.cancelEditionAndActivateSheet();
49251
49269
  this.ui.raiseBlockingErrorUI(CELL_DELETED_MESSAGE);
49252
49270
  }
49253
49271
  break;
@@ -53469,7 +53487,9 @@ css /* scss */ `
53469
53487
  margin-top: -1px;
53470
53488
  border: 1px solid;
53471
53489
 
53472
- .o-composer:empty:not(:focus):not(.active)::before {
53490
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
53491
+ .o-composer:empty:not(:focus):not(.active)::before,
53492
+ &.o-topbar-composer-readonly .o-composer:empty::before {
53473
53493
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
53474
53494
  position: relative;
53475
53495
  top: 20%;
@@ -56470,10 +56490,14 @@ function addIconSetRule(cf, rule) {
56470
56490
  continue;
56471
56491
  }
56472
56492
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
56493
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
56494
+ if (isIconSetReversed(rule.icons)) {
56495
+ iconSetAttrs.push(["reverse", "1"]);
56496
+ }
56473
56497
  conditionalFormats.push(escapeXml /*xml*/ `
56474
56498
  <conditionalFormatting sqref="${range}">
56475
56499
  <cfRule ${formatAttributes(ruleAttributes)}>
56476
- <iconSet iconSet="${getIconSet(rule.icons)}">
56500
+ <iconSet ${formatAttributes(iconSetAttrs)}>
56477
56501
  ${joinXmlNodes(cfValueObjectNodes)}
56478
56502
  </iconSet>
56479
56503
  </cfRule>
@@ -56491,9 +56515,21 @@ function commonCfAttributes(cf) {
56491
56515
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
56492
56516
  ];
56493
56517
  }
56518
+ function isIconSetReversed(iconSet) {
56519
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
56520
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
56521
+ }
56494
56522
  function getIconSet(iconSet) {
56495
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
56496
- "dots"];
56523
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
56524
+ }
56525
+ /**
56526
+ * Partial detection based on "upper" point only.
56527
+ * We support any arbitrary icon in the set, while excel doesn't allow
56528
+ * mixing icons from different types.
56529
+ */
56530
+ function detectIconsType(iconSet) {
56531
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
56532
+ return type;
56497
56533
  }
56498
56534
  function thresholdAttributes(threshold, position) {
56499
56535
  const type = getExcelThresholdType(threshold.type, position);
@@ -58069,6 +58105,6 @@ exports.setTranslationMethod = setTranslationMethod;
58069
58105
  exports.tokenize = tokenize;
58070
58106
 
58071
58107
 
58072
- __info__.version = "17.0.53";
58073
- __info__.date = "2025-03-12T15:26:58.496Z";
58074
- __info__.hash = "47dfd62";
58108
+ __info__.version = "17.0.55";
58109
+ __info__.date = "2025-03-26T12:44:29.690Z";
58110
+ __info__.hash = "c3a4769";
@@ -4869,6 +4869,7 @@ declare class SheetUIPlugin extends UIPlugin {
4869
4869
  * not outside the sheet.
4870
4870
  */
4871
4871
  private checkZonesAreInSheet;
4872
+ private autoResizeRows;
4872
4873
  }
4873
4874
 
4874
4875
  /**