@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
  import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -947,7 +947,10 @@ function rgbaStringToHex(color) {
947
947
  }
948
948
  else if (stringVals.length === 4) {
949
949
  const alpha = parseFloat(stringVals.pop() || "1");
950
- alphaHex = Math.round((alpha || 1) * 255);
950
+ if (isNaN(alpha)) {
951
+ throw new Error("invalid alpha value");
952
+ }
953
+ alphaHex = Math.round(alpha * 255);
951
954
  }
952
955
  const vals = stringVals.map((val) => parseInt(val, 10));
953
956
  if (alphaHex !== 255) {
@@ -4492,11 +4495,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
4492
4495
  if (!cell || (!cell.isFormula && !cell.content)) {
4493
4496
  return DEFAULT_CELL_HEIGHT;
4494
4497
  }
4495
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4496
- const numberOfLines = cell.isFormula
4497
- ? 1
4498
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
4499
- const fontSize = computeTextFontSizeInPixels(cell.style);
4498
+ const content = cell.isFormula ? "" : cell.content;
4499
+ return getCellContentHeight(ctx, content, cell.style, colSize);
4500
+ }
4501
+ function getCellContentHeight(ctx, content, style, colSize) {
4502
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4503
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
4504
+ const fontSize = computeTextFontSizeInPixels(style);
4500
4505
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
4501
4506
  }
4502
4507
  const textWidthCache = {};
@@ -7767,7 +7772,7 @@ function isValidLocale(locale) {
7767
7772
  */
7768
7773
  function canonicalizeNumberContent(content, locale) {
7769
7774
  return content.startsWith("=")
7770
- ? canonicalizeFormula$1(content, locale)
7775
+ ? canonicalizeFormula(content, locale)
7771
7776
  : canonicalizeNumberLiteral(content, locale);
7772
7777
  }
7773
7778
  /**
@@ -7782,7 +7787,7 @@ function canonicalizeNumberContent(content, locale) {
7782
7787
  */
7783
7788
  function canonicalizeContent(content, locale) {
7784
7789
  return content.startsWith("=")
7785
- ? canonicalizeFormula$1(content, locale)
7790
+ ? canonicalizeFormula(content, locale)
7786
7791
  : canonicalizeLiteral(content, locale);
7787
7792
  }
7788
7793
  /**
@@ -7798,15 +7803,21 @@ function localizeContent(content, locale) {
7798
7803
  ? localizeFormula(content, locale)
7799
7804
  : localizeLiteral(content, locale);
7800
7805
  }
7806
+ /** Change a number string to its canonical form (en_US locale) */
7807
+ function canonicalizeNumberValue(content, locale) {
7808
+ return content.startsWith("=")
7809
+ ? canonicalizeFormula(content, locale)
7810
+ : canonicalizeNumberLiteral(content, locale);
7811
+ }
7801
7812
  /** Change a formula to its canonical form (en_US locale) */
7802
- function canonicalizeFormula$1(formula, locale) {
7803
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
7813
+ function canonicalizeFormula(formula, locale) {
7814
+ return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7804
7815
  }
7805
7816
  /** Change a formula from the canonical form to the given locale */
7806
7817
  function localizeFormula(formula, locale) {
7807
- return _localizeFormula$1(formula, DEFAULT_LOCALE, locale);
7818
+ return _localizeFormula(formula, DEFAULT_LOCALE, locale);
7808
7819
  }
7809
- function _localizeFormula$1(formula, fromLocale, toLocale) {
7820
+ function _localizeFormula(formula, fromLocale, toLocale) {
7810
7821
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
7811
7822
  fromLocale.decimalSeparator === toLocale.decimalSeparator) {
7812
7823
  return formula;
@@ -8560,7 +8571,8 @@ function getChartLabelValues(getters, dataSets, labelRange) {
8560
8571
  }
8561
8572
  }
8562
8573
  else if (dataSets.length === 1) {
8563
- for (let i = 0; i < getData(getters, dataSets[0]).length; i++) {
8574
+ const dataLength = getData(getters, dataSets[0]).length;
8575
+ for (let i = 0; i < dataLength; i++) {
8564
8576
  labels.formattedValues.push("");
8565
8577
  labels.values.push("");
8566
8578
  }
@@ -24290,6 +24302,7 @@ class RemoveDuplicatesPanel extends Component {
24290
24302
  columns: {},
24291
24303
  });
24292
24304
  setup() {
24305
+ this.updateColumns();
24293
24306
  onWillUpdateProps(() => this.updateColumns());
24294
24307
  }
24295
24308
  toggleHasHeader() {
@@ -30360,9 +30373,9 @@ const XLSX_CHART_TYPES = [
30360
30373
  /** In XLSX color format (no #) */
30361
30374
  const AUTO_COLOR = "000000";
30362
30375
  const XLSX_ICONSET_MAP = {
30363
- arrow: "3Arrows",
30376
+ arrows: "3Arrows",
30364
30377
  smiley: "3Symbols",
30365
- dot: "3TrafficLights1",
30378
+ dots: "3TrafficLights1",
30366
30379
  };
30367
30380
  const NAMESPACE = {
30368
30381
  styleSheet: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
@@ -30778,6 +30791,7 @@ const ICON_SET_CONVERSION_MAP = {
30778
30791
  };
30779
30792
  /** Map between legend position in XLSX file and human readable position */
30780
30793
  const DRAWING_LEGEND_POSITION_CONVERSION_MAP = {
30794
+ none: "none",
30781
30795
  b: "bottom",
30782
30796
  t: "top",
30783
30797
  l: "left",
@@ -33010,7 +33024,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
33010
33024
  ? "right"
33011
33025
  : "left",
33012
33026
  legendPosition: DRAWING_LEGEND_POSITION_CONVERSION_MAP[this.extractChildAttr(rootChartElement, "c:legendPos", "val", {
33013
- default: "b",
33027
+ default: "none",
33014
33028
  }).asString()],
33015
33029
  stacked: barChartGrouping === "stacked",
33016
33030
  fontColor: "000000",
@@ -45468,12 +45482,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45468
45482
  continue;
45469
45483
  }
45470
45484
  const position = { col: col + c, row: row + r, sheetId: sheetId };
45471
- // TODO: refactor this part. the "Paste merge" action is also executed with
45472
- // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
45473
- // is not appropriate
45474
- if (this.operation !== "CUT") {
45475
- this.pasteMergeIfExist(origin.position, position);
45476
- }
45485
+ this.pasteMergeIfExist(origin.position, position, this.operation);
45477
45486
  this.pasteCell(origin, position, this.operation, clipboardOptions);
45478
45487
  if (shouldPasteCF) {
45479
45488
  this.pasteCf(origin.position, position);
@@ -45530,7 +45539,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45530
45539
  * If the origin position given is the top left of a merge, merge the target
45531
45540
  * position.
45532
45541
  */
45533
- pasteMergeIfExist(origin, target) {
45542
+ pasteMergeIfExist(origin, target, operation) {
45534
45543
  let { sheetId, col, row } = origin;
45535
45544
  const { col: mainCellColOrigin, row: mainCellRowOrigin } = this.getters.getMainCellPosition(origin);
45536
45545
  if (mainCellColOrigin === col && mainCellRowOrigin === row) {
@@ -45538,6 +45547,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
45538
45547
  if (!merge) {
45539
45548
  return;
45540
45549
  }
45550
+ if (operation === "CUT") {
45551
+ this.dispatch("REMOVE_MERGE", { sheetId, target: [merge] });
45552
+ }
45541
45553
  ({ sheetId, col, row } = target);
45542
45554
  this.dispatch("ADD_MERGE", {
45543
45555
  sheetId,
@@ -47829,12 +47841,7 @@ class SheetUIPlugin extends UIPlugin {
47829
47841
  }
47830
47842
  break;
47831
47843
  case "AUTORESIZE_ROWS":
47832
- this.dispatch("RESIZE_COLUMNS_ROWS", {
47833
- elements: cmd.rows,
47834
- dimension: "ROW",
47835
- size: null,
47836
- sheetId: cmd.sheetId,
47837
- });
47844
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
47838
47845
  break;
47839
47846
  }
47840
47847
  }
@@ -47932,6 +47939,48 @@ class SheetUIPlugin extends UIPlugin {
47932
47939
  }
47933
47940
  return "Success" /* CommandResult.Success */;
47934
47941
  }
47942
+ autoResizeRows(sheetId, rows) {
47943
+ const rowSizes = [];
47944
+ for (const row of rows) {
47945
+ let evaluatedRowSize = 0;
47946
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
47947
+ const cell = this.getters.getCellById(cellId);
47948
+ if (!cell) {
47949
+ continue;
47950
+ }
47951
+ const position = this.getters.getCellPosition(cell.id);
47952
+ const colSize = this.getters.getColSize(sheetId, position.col);
47953
+ if (cell.isFormula) {
47954
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
47955
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
47956
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
47957
+ evaluatedRowSize = evaluatedSize;
47958
+ }
47959
+ }
47960
+ else {
47961
+ const content = cell.content;
47962
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
47963
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
47964
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
47965
+ evaluatedRowSize = 0;
47966
+ }
47967
+ }
47968
+ }
47969
+ rowSizes.push(evaluatedRowSize || null);
47970
+ }
47971
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
47972
+ for (let i = 0; i < rowSizes.length; i++) {
47973
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
47974
+ }
47975
+ for (const [size, rows] of groupedSizes) {
47976
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
47977
+ elements: rows,
47978
+ dimension: "ROW",
47979
+ size,
47980
+ sheetId,
47981
+ });
47982
+ }
47983
+ }
47935
47984
  }
47936
47985
 
47937
47986
  const genericRepeatsTransforms = [
@@ -48581,37 +48630,6 @@ class ClipboardFigureImage {
48581
48630
  }
48582
48631
  }
48583
48632
 
48584
- /** Change a number string to its canonical form (en_US locale) */
48585
- function canonicalizeNumberValue(content, locale) {
48586
- return content.startsWith("=")
48587
- ? canonicalizeFormula(content, locale)
48588
- : canonicalizeNumberLiteral(content, locale);
48589
- }
48590
- /** Change a formula to its canonical form (en_US locale) */
48591
- function canonicalizeFormula(formula, locale) {
48592
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
48593
- }
48594
- function _localizeFormula(formula, fromLocale, toLocale) {
48595
- if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
48596
- fromLocale.decimalSeparator === toLocale.decimalSeparator) {
48597
- return formula;
48598
- }
48599
- const tokens = tokenize(formula, fromLocale);
48600
- let localizedFormula = "";
48601
- for (const token of tokens) {
48602
- if (token.type === "NUMBER") {
48603
- localizedFormula += token.value.replace(fromLocale.decimalSeparator, toLocale.decimalSeparator);
48604
- }
48605
- else if (token.type === "ARG_SEPARATOR") {
48606
- localizedFormula += toLocale.formulaArgSeparator;
48607
- }
48608
- else {
48609
- localizedFormula += token.value;
48610
- }
48611
- }
48612
- return localizedFormula;
48613
- }
48614
-
48615
48633
  /** State of the clipboard when copying/cutting from the OS clipboard*/
48616
48634
  class ClipboardOsState extends ClipboardCellsAbstractState {
48617
48635
  values;
@@ -49188,8 +49206,8 @@ class EditionPlugin extends UIPlugin {
49188
49206
  this.colorIndexByRange = {};
49189
49207
  break;
49190
49208
  case "CANCEL_EDITION":
49191
- this.cancelEditionAndActivateSheet();
49192
49209
  this.resetContent();
49210
+ this.cancelEditionAndActivateSheet();
49193
49211
  this.colorIndexByRange = {};
49194
49212
  break;
49195
49213
  case "SET_CURRENT_CONTENT":
@@ -49200,8 +49218,8 @@ class EditionPlugin extends UIPlugin {
49200
49218
  this.replaceSelection(cmd.text);
49201
49219
  break;
49202
49220
  case "SELECT_FIGURE":
49203
- this.cancelEditionAndActivateSheet();
49204
49221
  this.resetContent();
49222
+ this.cancelEditionAndActivateSheet();
49205
49223
  break;
49206
49224
  case "ADD_COLUMNS_ROWS":
49207
49225
  this.onAddElements(cmd);
@@ -49244,8 +49262,8 @@ class EditionPlugin extends UIPlugin {
49244
49262
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
49245
49263
  if (!sheetIdExists && this.mode !== "inactive") {
49246
49264
  this.sheetId = this.getters.getActiveSheetId();
49247
- this.cancelEditionAndActivateSheet();
49248
49265
  this.resetContent();
49266
+ this.cancelEditionAndActivateSheet();
49249
49267
  this.ui.raiseBlockingErrorUI(CELL_DELETED_MESSAGE);
49250
49268
  }
49251
49269
  break;
@@ -53467,7 +53485,9 @@ css /* scss */ `
53467
53485
  margin-top: -1px;
53468
53486
  border: 1px solid;
53469
53487
 
53470
- .o-composer:empty:not(:focus):not(.active)::before {
53488
+ /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
53489
+ .o-composer:empty:not(:focus):not(.active)::before,
53490
+ &.o-topbar-composer-readonly .o-composer:empty::before {
53471
53491
  content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
53472
53492
  position: relative;
53473
53493
  top: 20%;
@@ -56468,10 +56488,14 @@ function addIconSetRule(cf, rule) {
56468
56488
  continue;
56469
56489
  }
56470
56490
  const cfValueObjectNodes = cfValueObject.map((attrs) => escapeXml /*xml*/ `<cfvo ${formatAttributes(attrs)} />`);
56491
+ const iconSetAttrs = [["iconSet", getIconSet(rule.icons)]];
56492
+ if (isIconSetReversed(rule.icons)) {
56493
+ iconSetAttrs.push(["reverse", "1"]);
56494
+ }
56471
56495
  conditionalFormats.push(escapeXml /*xml*/ `
56472
56496
  <conditionalFormatting sqref="${range}">
56473
56497
  <cfRule ${formatAttributes(ruleAttributes)}>
56474
- <iconSet iconSet="${getIconSet(rule.icons)}">
56498
+ <iconSet ${formatAttributes(iconSetAttrs)}>
56475
56499
  ${joinXmlNodes(cfValueObjectNodes)}
56476
56500
  </iconSet>
56477
56501
  </cfRule>
@@ -56489,9 +56513,21 @@ function commonCfAttributes(cf) {
56489
56513
  ["stopIfTrue", cf.stopIfTrue ? 1 : 0],
56490
56514
  ];
56491
56515
  }
56516
+ function isIconSetReversed(iconSet) {
56517
+ const defaultIconSet = ICON_SETS[detectIconsType(iconSet)];
56518
+ return iconSet.upper === defaultIconSet.bad && iconSet.lower === defaultIconSet.good;
56519
+ }
56492
56520
  function getIconSet(iconSet) {
56493
- return XLSX_ICONSET_MAP[Object.keys(XLSX_ICONSET_MAP).find((key) => iconSet.upper.toLowerCase().startsWith(key)) ||
56494
- "dots"];
56521
+ return XLSX_ICONSET_MAP[detectIconsType(iconSet)];
56522
+ }
56523
+ /**
56524
+ * Partial detection based on "upper" point only.
56525
+ * We support any arbitrary icon in the set, while excel doesn't allow
56526
+ * mixing icons from different types.
56527
+ */
56528
+ function detectIconsType(iconSet) {
56529
+ const type = Object.keys(ICON_SETS).find((type) => Object.values(ICON_SETS[type]).includes(iconSet.upper)) || "dots";
56530
+ return type;
56495
56531
  }
56496
56532
  function thresholdAttributes(threshold, position) {
56497
56533
  const type = getExcelThresholdType(threshold.type, position);
@@ -58032,6 +58068,6 @@ const constants = {
58032
58068
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
58033
58069
 
58034
58070
 
58035
- __info__.version = "17.0.53";
58036
- __info__.date = "2025-03-12T15:26:58.496Z";
58037
- __info__.hash = "47dfd62";
58071
+ __info__.version = "17.0.55";
58072
+ __info__.date = "2025-03-26T12:44:29.690Z";
58073
+ __info__.hash = "c3a4769";