@odoo/o-spreadsheet 18.0.18 → 18.0.20

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.18
6
- * @date 2025-03-07T10:38:36.883Z
7
- * @hash 06c9bc5
5
+ * @version 18.0.20
6
+ * @date 2025-03-19T08:21:32.426Z
7
+ * @hash 3f48d8b
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';
@@ -419,7 +419,6 @@ function escapeRegExp(str) {
419
419
  * Sparse arrays remain sparse.
420
420
  */
421
421
  function deepCopy(obj) {
422
- const result = Array.isArray(obj) ? [] : {};
423
422
  switch (typeof obj) {
424
423
  case "object": {
425
424
  if (obj === null) {
@@ -431,8 +430,18 @@ function deepCopy(obj) {
431
430
  else if (!(isPlainObject(obj) || obj instanceof Array)) {
432
431
  throw new Error("Unsupported type: only objects and arrays are supported");
433
432
  }
434
- for (const key in obj) {
435
- result[key] = deepCopy(obj[key]);
433
+ const result = Array.isArray(obj) ? new Array(obj.length) : {};
434
+ if (Array.isArray(obj)) {
435
+ for (let i = 0, len = obj.length; i < len; i++) {
436
+ if (i in obj) {
437
+ result[i] = deepCopy(obj[i]);
438
+ }
439
+ }
440
+ }
441
+ else {
442
+ for (const key in obj) {
443
+ result[key] = deepCopy(obj[key]);
444
+ }
436
445
  }
437
446
  return result;
438
447
  }
@@ -2534,21 +2543,30 @@ function mergeContiguousZones(zones) {
2534
2543
  return mergedZones;
2535
2544
  }
2536
2545
 
2546
+ const globalReverseLookup$1 = new WeakMap();
2547
+ const globalIdCounter = new WeakMap();
2537
2548
  /**
2538
2549
  * Get the id of the given item (its key in the given dictionary).
2539
2550
  * If the given item does not exist in the dictionary, it creates one with a new id.
2540
2551
  */
2541
2552
  function getItemId(item, itemsDic) {
2542
- for (const key in itemsDic) {
2543
- if (deepEquals(itemsDic[key], item)) {
2544
- return parseInt(key, 10);
2545
- }
2553
+ if (!globalReverseLookup$1.has(itemsDic)) {
2554
+ globalReverseLookup$1.set(itemsDic, new Map());
2555
+ globalIdCounter.set(itemsDic, 0);
2556
+ }
2557
+ const reverseLookup = globalReverseLookup$1.get(itemsDic);
2558
+ const canonical = getCanonicalRepresentation(item);
2559
+ if (reverseLookup.has(canonical)) {
2560
+ const id = reverseLookup.get(canonical);
2561
+ itemsDic[id] = item;
2562
+ return id;
2546
2563
  }
2547
2564
  // Generate new Id if the item didn't exist in the dictionary
2548
- const ids = Object.keys(itemsDic);
2549
- const maxId = ids.length === 0 ? 0 : largeMax(ids.map((id) => parseInt(id, 10)));
2550
- itemsDic[maxId + 1] = item;
2551
- return maxId + 1;
2565
+ const newId = globalIdCounter.get(itemsDic) + 1;
2566
+ reverseLookup.set(canonical, newId);
2567
+ globalIdCounter.set(itemsDic, newId);
2568
+ itemsDic[newId] = item;
2569
+ return newId;
2552
2570
  }
2553
2571
  function groupItemIdsByZones(positionsByItemId) {
2554
2572
  const result = {};
@@ -2560,6 +2578,33 @@ function groupItemIdsByZones(positionsByItemId) {
2560
2578
  }
2561
2579
  return result;
2562
2580
  }
2581
+ function getCanonicalRepresentation(item) {
2582
+ if (item === null)
2583
+ return "null";
2584
+ if (item === undefined)
2585
+ return "undefined";
2586
+ if (typeof item !== "object")
2587
+ return String(item);
2588
+ if (Array.isArray(item)) {
2589
+ const len = item.length;
2590
+ let result = "[";
2591
+ for (let i = 0; i < len; i++) {
2592
+ if (i > 0)
2593
+ result += ",";
2594
+ result += getCanonicalRepresentation(item[i]);
2595
+ }
2596
+ return result + "]";
2597
+ }
2598
+ const keys = Object.keys(item).sort();
2599
+ let repr = "{";
2600
+ for (const key of keys) {
2601
+ if (item[key] !== undefined) {
2602
+ repr += `"${key}":${getCanonicalRepresentation(item[key])},`;
2603
+ }
2604
+ }
2605
+ repr += "}";
2606
+ return repr;
2607
+ }
2563
2608
 
2564
2609
  // -----------------------------------------------------------------------------
2565
2610
  // Date Type
@@ -6062,11 +6107,13 @@ function getDefaultCellHeight(ctx, cell, colSize) {
6062
6107
  if (!cell || (!cell.isFormula && !cell.content)) {
6063
6108
  return DEFAULT_CELL_HEIGHT;
6064
6109
  }
6065
- const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6066
- const numberOfLines = cell.isFormula
6067
- ? 1
6068
- : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
6069
- const fontSize = computeTextFontSizeInPixels(cell.style);
6110
+ const content = cell.isFormula ? "" : cell.content;
6111
+ return getCellContentHeight(ctx, content, cell.style, colSize);
6112
+ }
6113
+ function getCellContentHeight(ctx, content, style, colSize) {
6114
+ const maxWidth = style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
6115
+ const numberOfLines = splitTextToWidth(ctx, content, style, maxWidth).length;
6116
+ const fontSize = computeTextFontSizeInPixels(style);
6070
6117
  return computeTextLinesHeight(fontSize, numberOfLines) + 2 * PADDING_AUTORESIZE_VERTICAL;
6071
6118
  }
6072
6119
  function getDefaultContextFont(fontSize, bold = false, italic = false) {
@@ -8229,13 +8276,6 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8229
8276
  this.clearClippedZones(content);
8230
8277
  const selection = target[0];
8231
8278
  this.pasteZone(sheetId, selection.left, selection.top, content.cells, options);
8232
- this.dispatch("MOVE_RANGES", {
8233
- target: content.zones,
8234
- sheetId: content.sheetId,
8235
- targetSheetId: sheetId,
8236
- col: selection.left,
8237
- row: selection.top,
8238
- });
8239
8279
  }
8240
8280
  /**
8241
8281
  * Clear the clipped zones: remove the cells and clear the formatting
@@ -8744,14 +8784,15 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
8744
8784
  }
8745
8785
  merges.push(mergesInRow);
8746
8786
  }
8747
- return { merges };
8787
+ return { merges, sheetId };
8748
8788
  }
8749
8789
  /**
8750
8790
  * Paste the clipboard content in the given target
8751
8791
  */
8752
8792
  paste(target, content, options) {
8753
8793
  if (options.isCutOperation) {
8754
- return;
8794
+ const copiedMerges = content.merges.flat().filter(isDefined);
8795
+ this.dispatch("REMOVE_MERGE", { sheetId: content.sheetId, target: copiedMerges });
8755
8796
  }
8756
8797
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
8757
8798
  }
@@ -8786,6 +8827,27 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
8786
8827
  }
8787
8828
  }
8788
8829
 
8830
+ class ReferenceClipboardHandler extends AbstractCellClipboardHandler {
8831
+ copy(data) {
8832
+ return {
8833
+ zones: data.clippedZones,
8834
+ sheetId: data.sheetId,
8835
+ };
8836
+ }
8837
+ paste(target, content, options) {
8838
+ if (options.isCutOperation) {
8839
+ const selection = target.zones[0];
8840
+ this.dispatch("MOVE_RANGES", {
8841
+ target: content.zones,
8842
+ sheetId: content.sheetId,
8843
+ targetSheetId: target.sheetId,
8844
+ col: selection.left,
8845
+ row: selection.top,
8846
+ });
8847
+ }
8848
+ }
8849
+ }
8850
+
8789
8851
  class SheetClipboardHandler extends AbstractCellClipboardHandler {
8790
8852
  isPasteAllowed(sheetId, target, content, options) {
8791
8853
  if (!("cells" in content)) {
@@ -8953,7 +9015,8 @@ clipboardHandlersRegistries.cellHandlers
8953
9015
  .add("merge", MergeClipboardHandler)
8954
9016
  .add("border", BorderClipboardHandler)
8955
9017
  .add("table", TableClipboardHandler)
8956
- .add("conditionalFormat", ConditionalFormatClipboardHandler);
9018
+ .add("conditionalFormat", ConditionalFormatClipboardHandler)
9019
+ .add("references", ReferenceClipboardHandler);
8957
9020
 
8958
9021
  function transformZone(zone, executed) {
8959
9022
  if (executed.type === "REMOVE_COLUMNS_ROWS") {
@@ -11562,16 +11625,25 @@ function addRelsToFile(relsFiles, path, rel) {
11562
11625
  }
11563
11626
  return id;
11564
11627
  }
11628
+ const globalReverseLookup = new WeakMap();
11565
11629
  function pushElement(property, propertyList) {
11566
- let len = propertyList.length;
11567
- const operator = typeof property === "object" ? deepEquals : (a, b) => a === b;
11568
- for (let i = 0; i < len; i++) {
11569
- if (operator(property, propertyList[i])) {
11570
- return i;
11630
+ let reverseLookup = globalReverseLookup.get(propertyList);
11631
+ if (!reverseLookup) {
11632
+ reverseLookup = new Map();
11633
+ for (let i = 0; i < propertyList.length; i++) {
11634
+ const canonical = getCanonicalRepresentation(propertyList[i]);
11635
+ reverseLookup.set(canonical, i);
11571
11636
  }
11637
+ globalReverseLookup.set(propertyList, reverseLookup);
11638
+ }
11639
+ const canonical = getCanonicalRepresentation(property);
11640
+ if (reverseLookup.has(canonical)) {
11641
+ return reverseLookup.get(canonical);
11572
11642
  }
11573
- propertyList[propertyList.length] = property;
11574
- return propertyList.length - 1;
11643
+ const maxId = propertyList.length;
11644
+ propertyList.push(property);
11645
+ reverseLookup.set(canonical, maxId);
11646
+ return maxId;
11575
11647
  }
11576
11648
  const chartIds = [];
11577
11649
  /**
@@ -13424,7 +13496,7 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
13424
13496
  title: { text: chartTitle },
13425
13497
  type: CHART_TYPE_CONVERSION_MAP[chartType],
13426
13498
  dataSets: this.extractChartDatasets(this.querySelectorAll(rootChartElement, `c:${chartType}`), chartType),
13427
- labelRange: this.extractChildTextContent(rootChartElement, `c:ser ${chartType === "scatterChart" ? "c:numRef" : "c:cat"} c:f`),
13499
+ labelRange: this.extractLabelRange(chartType, rootChartElement),
13428
13500
  backgroundColor: this.extractChildAttr(rootChartElement, "c:chartSpace > c:spPr a:srgbClr", "val", {
13429
13501
  default: "ffffff",
13430
13502
  }).asString(),
@@ -13436,6 +13508,13 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
13436
13508
  };
13437
13509
  })[0];
13438
13510
  }
13511
+ extractLabelRange(chartType, rootChartElement) {
13512
+ if (chartType === "scatterChart") {
13513
+ return (this.extractChildTextContent(rootChartElement, `c:ser c:strRef c:f`) ||
13514
+ this.extractChildTextContent(rootChartElement, `c:ser c:numRef c:f`));
13515
+ }
13516
+ return this.extractChildTextContent(rootChartElement, `c:ser c:cat c:f`);
13517
+ }
13439
13518
  extractComboChart(chartElement) {
13440
13519
  // Title can be separated into multiple xml elements (for styling and such), we only import the text
13441
13520
  const chartTitle = this.mapOnElements({ parent: chartElement, query: "c:title a:t" }, (textElement) => {
@@ -16897,6 +16976,7 @@ class ScorecardChart extends Component {
16897
16976
  const autoCompleteProviders = new Registry();
16898
16977
 
16899
16978
  autoCompleteProviders.add("dataValidation", {
16979
+ displayAllOnInitialContent: true,
16900
16980
  getProposals(tokenAtCursor, content) {
16901
16981
  if (content.startsWith("=")) {
16902
16982
  return [];
@@ -28373,7 +28453,7 @@ autoCompleteProviders.add("pivot_group_values", {
28373
28453
  text,
28374
28454
  description: usedLabel,
28375
28455
  htmlContent: [{ value: text, color }],
28376
- fuzzySearchKey: value + usedLabel,
28456
+ fuzzySearchKey: text + usedLabel,
28377
28457
  };
28378
28458
  });
28379
28459
  },
@@ -38776,8 +38856,8 @@ class AbstractComposerStore extends SpreadsheetStore {
38776
38856
  this.updateRangeColor();
38777
38857
  }
38778
38858
  cancelEdition() {
38779
- this.cancelEditionAndActivateSheet();
38780
38859
  this.resetContent();
38860
+ this.cancelEditionAndActivateSheet();
38781
38861
  }
38782
38862
  setCurrentContent(content, selection) {
38783
38863
  if (selection && !this.isSelectionValid(content.length, selection.start, selection.end)) {
@@ -38793,8 +38873,8 @@ class AbstractComposerStore extends SpreadsheetStore {
38793
38873
  switch (cmd.type) {
38794
38874
  case "SELECT_FIGURE":
38795
38875
  if (cmd.id) {
38796
- this.cancelEditionAndActivateSheet();
38797
38876
  this.resetContent();
38877
+ this.cancelEditionAndActivateSheet();
38798
38878
  }
38799
38879
  break;
38800
38880
  case "START_CHANGE_HIGHLIGHT":
@@ -39145,6 +39225,15 @@ class AbstractComposerStore extends SpreadsheetStore {
39145
39225
  const exactMatch = proposals?.find((p) => p.text === tokenAtCursor.value);
39146
39226
  // remove tokens that are likely to be other parts of the formula that slipped in the token if it's a string
39147
39227
  const searchTerm = tokenAtCursor.value.replace(/[ ,\(\)]/g, "");
39228
+ if (this._currentContent === this.initialContent &&
39229
+ provider.displayAllOnInitialContent &&
39230
+ proposals?.length) {
39231
+ return {
39232
+ proposals,
39233
+ selectProposal: provider.selectProposal,
39234
+ autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
39235
+ };
39236
+ }
39148
39237
  if (exactMatch && this._currentContent !== this.initialContent) {
39149
39238
  // this means the user has chosen a proposal
39150
39239
  return;
@@ -44680,6 +44769,7 @@ class RemoveDuplicatesPanel extends Component {
44680
44769
  columns: {},
44681
44770
  });
44682
44771
  setup() {
44772
+ this.updateColumns();
44683
44773
  onWillUpdateProps(() => this.updateColumns());
44684
44774
  }
44685
44775
  toggleHasHeader() {
@@ -46468,8 +46558,8 @@ class CellComposerStore extends AbstractComposerStore {
46468
46558
  const sheetIdExists = !!this.getters.tryGetSheet(this.sheetId);
46469
46559
  if (!sheetIdExists && this.editionMode !== "inactive") {
46470
46560
  this.sheetId = this.getters.getActiveSheetId();
46471
- this.cancelEditionAndActivateSheet();
46472
46561
  this.resetContent();
46562
+ this.cancelEditionAndActivateSheet();
46473
46563
  this.notificationStore.raiseError(CELL_DELETED_MESSAGE);
46474
46564
  }
46475
46565
  break;
@@ -62825,12 +62915,7 @@ class SheetUIPlugin extends UIPlugin {
62825
62915
  }
62826
62916
  break;
62827
62917
  case "AUTORESIZE_ROWS":
62828
- this.dispatch("RESIZE_COLUMNS_ROWS", {
62829
- elements: cmd.rows,
62830
- dimension: "ROW",
62831
- size: null,
62832
- sheetId: cmd.sheetId,
62833
- });
62918
+ this.autoResizeRows(cmd.sheetId, cmd.rows);
62834
62919
  break;
62835
62920
  }
62836
62921
  }
@@ -62995,6 +63080,48 @@ class SheetUIPlugin extends UIPlugin {
62995
63080
  }
62996
63081
  return "Success" /* CommandResult.Success */;
62997
63082
  }
63083
+ autoResizeRows(sheetId, rows) {
63084
+ const rowSizes = [];
63085
+ for (const row of rows) {
63086
+ let evaluatedRowSize = 0;
63087
+ for (const cellId of this.getters.getRowCells(sheetId, row)) {
63088
+ const cell = this.getters.getCellById(cellId);
63089
+ if (!cell) {
63090
+ continue;
63091
+ }
63092
+ const position = this.getters.getCellPosition(cell.id);
63093
+ const colSize = this.getters.getColSize(sheetId, position.col);
63094
+ if (cell.isFormula) {
63095
+ const content = this.getters.getEvaluatedCell(position).formattedValue;
63096
+ const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
63097
+ if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
63098
+ evaluatedRowSize = evaluatedSize;
63099
+ }
63100
+ }
63101
+ else {
63102
+ const content = cell.content;
63103
+ const dynamicRowSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
63104
+ // Only keep the size of evaluated cells if it's bigger than the dynamic row size
63105
+ if (dynamicRowSize >= evaluatedRowSize && dynamicRowSize > DEFAULT_CELL_HEIGHT) {
63106
+ evaluatedRowSize = 0;
63107
+ }
63108
+ }
63109
+ }
63110
+ rowSizes.push(evaluatedRowSize || null);
63111
+ }
63112
+ const groupedSizes = new Map(rowSizes.map((size) => [size, []]));
63113
+ for (let i = 0; i < rowSizes.length; i++) {
63114
+ groupedSizes.get(rowSizes[i])?.push(rows[i]);
63115
+ }
63116
+ for (const [size, rows] of groupedSizes) {
63117
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
63118
+ elements: rows,
63119
+ dimension: "ROW",
63120
+ size,
63121
+ sheetId,
63122
+ });
63123
+ }
63124
+ }
62998
63125
  }
62999
63126
 
63000
63127
  class TableComputedStylePlugin extends UIPlugin {
@@ -72014,7 +72141,7 @@ function addStyles(styles) {
72014
72141
  }
72015
72142
  if (alignAttrs.length > 0) {
72016
72143
  attributes.push(["applyAlignment", "1"]); // for Libre Office
72017
- styleNodes.push(escapeXml /*xml*/ `<xf ${formatAttributes(attributes)}>${escapeXml /*xml*/ `<alignment ${formatAttributes(alignAttrs)} />`}</xf> `);
72144
+ styleNodes.push(escapeXml /*xml*/ `<xf ${formatAttributes(attributes)}><alignment ${formatAttributes(alignAttrs)} /></xf> `);
72018
72145
  }
72019
72146
  else {
72020
72147
  styleNodes.push(escapeXml /*xml*/ `<xf ${formatAttributes(attributes)} />`);
@@ -73435,6 +73562,6 @@ const constants = {
73435
73562
  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 };
73436
73563
 
73437
73564
 
73438
- __info__.version = "18.0.18";
73439
- __info__.date = "2025-03-07T10:38:36.883Z";
73440
- __info__.hash = "06c9bc5";
73565
+ __info__.version = "18.0.20";
73566
+ __info__.date = "2025-03-19T08:21:32.426Z";
73567
+ __info__.hash = "3f48d8b";