@odoo/o-spreadsheet 18.2.19 → 18.2.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.2.19
6
- * @date 2025-06-23T15:07:13.023Z
7
- * @hash 430d931
5
+ * @version 18.2.21
6
+ * @date 2025-07-11T11:11:48.661Z
7
+ * @hash 1c32303
8
8
  */
9
9
 
10
10
  'use strict';
@@ -823,6 +823,7 @@ const specialWhiteSpaceSpecialCharacters = [
823
823
  ];
824
824
  const specialWhiteSpaceRegexp = new RegExp(specialWhiteSpaceSpecialCharacters.join("|"), "g");
825
825
  const newLineRegexp = /(\r\n|\r)/g;
826
+ const whiteSpaceCharacters = specialWhiteSpaceSpecialCharacters.concat([" "]);
826
827
  /**
827
828
  * Replace all different newlines characters by \n
828
829
  */
@@ -2839,8 +2840,9 @@ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
2839
2840
  const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
2840
2841
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
2841
2842
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
2842
- const dateSeparatorsRegex = /\/|-|\s/;
2843
- const dateRegexp = /^(\d{1,4})[\/-\s](\d{1,4})([\/-\s](\d{1,4}))?$/;
2843
+ const whiteSpaceChars = whiteSpaceCharacters.join("");
2844
+ const dateSeparatorsRegex = new RegExp(`\/|-|${whiteSpaceCharacters.join("|")}`);
2845
+ const dateRegexp = new RegExp(`^(\\d{1,4})[\/${whiteSpaceChars}\-](\\d{1,4})([\/${whiteSpaceChars}\-](\\d{1,4}))?$`);
2844
2846
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
2845
2847
  /** Convert a value number representing a date, or return undefined if it isn't possible */
2846
2848
  function valueToDateNumber(value, locale) {
@@ -6543,6 +6545,8 @@ function splitWordToSpecificWidth(ctx, word, width, style) {
6543
6545
  function splitTextToWidth(ctx, text, style, width) {
6544
6546
  if (!style)
6545
6547
  style = {};
6548
+ if (isMarkdownLink(text))
6549
+ text = parseMarkdownLink(text).label;
6546
6550
  const brokenText = [];
6547
6551
  // Checking if text contains NEWLINE before split makes it very slightly slower if text contains it,
6548
6552
  // but 5-10x faster if it doesn't
@@ -8507,6 +8511,10 @@ function toNormalizedPivotValue(dimension, groupValue) {
8507
8511
  if (groupValue === null || groupValue === "null") {
8508
8512
  return null;
8509
8513
  }
8514
+ const extractedGroupValue = typeof groupValue === "object" ? groupValue.value : groupValue;
8515
+ if (isEvaluationError(extractedGroupValue)) {
8516
+ return extractedGroupValue;
8517
+ }
8510
8518
  const groupValueString = typeof groupValue === "boolean"
8511
8519
  ? toString(groupValue).toLocaleLowerCase()
8512
8520
  : toString(groupValue);
@@ -21200,7 +21208,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21200
21208
  }
21201
21209
  captureSelection(zone, col, row) {
21202
21210
  this.model.selection.capture(this, {
21203
- cell: { col: col || zone.left, row: row || zone.right },
21211
+ cell: { col: col ?? zone.left, row: row ?? zone.right },
21204
21212
  zone,
21205
21213
  }, {
21206
21214
  handleEvent: this.handleEvent.bind(this),
@@ -25811,40 +25819,112 @@ function convertPivotTableConfig(pivotTable) {
25811
25819
  * In all the sheets, replace the table-only references in the formula cells with standard references.
25812
25820
  */
25813
25821
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
25822
+ let deconstructedSheets = null;
25814
25823
  for (let tableSheet of convertedSheets) {
25815
25824
  const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
25825
+ if (!tables || tables.length === 0) {
25826
+ continue;
25827
+ }
25828
+ // Only deconstruct sheets if we are sure there are tables to process
25829
+ if (!deconstructedSheets) {
25830
+ deconstructedSheets = deconstructSheets(convertedSheets);
25831
+ }
25816
25832
  for (let table of tables) {
25817
- const tabRef = table.name + "[";
25818
- for (let sheet of convertedSheets) {
25819
- for (let xc in sheet.cells) {
25820
- const cell = sheet.cells[xc];
25821
- let cellContent = sheet.cells[xc];
25822
- if (cell && cellContent && cellContent.startsWith("=")) {
25823
- let refIndex;
25824
- while ((refIndex = cellContent.indexOf(tabRef)) !== -1) {
25825
- let endIndex = refIndex + tabRef.length;
25826
- let openBrackets = 1;
25827
- while (openBrackets > 0 && endIndex < cellContent.length) {
25828
- if (cellContent[endIndex] === "[") {
25829
- openBrackets++;
25830
- }
25831
- else if (cellContent[endIndex] === "]") {
25832
- openBrackets--;
25833
- }
25834
- endIndex++;
25835
- }
25836
- let reference = cellContent.slice(refIndex + tabRef.length, endIndex - 1);
25837
- const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
25838
- const convertedRef = convertTableReference(sheetPrefix, reference, table, xc);
25839
- cellContent =
25840
- cellContent.slice(0, refIndex) + convertedRef + cellContent.slice(endIndex);
25833
+ for (let sheetId in deconstructedSheets) {
25834
+ const sheet = convertedSheets.find((s) => s.id === sheetId);
25835
+ for (let xc in deconstructedSheets[sheetId]) {
25836
+ const deconstructedCell = deconstructedSheets[sheetId][xc];
25837
+ for (let i = deconstructedCell.length - 3; i >= 0; i -= 2) {
25838
+ const possibleTable = deconstructedSheets[sheetId][xc][i];
25839
+ if (!possibleTable.endsWith(table.name)) {
25840
+ continue;
25841
25841
  }
25842
+ const possibleRef = deconstructedSheets[sheetId][xc][i + 1];
25843
+ const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
25844
+ const convertedRef = convertTableReference(sheetPrefix, possibleRef, table, xc);
25845
+ deconstructedSheets[sheetId][xc][i + 2] =
25846
+ possibleTable.slice(0, possibleTable.indexOf(table.name)) +
25847
+ convertedRef +
25848
+ deconstructedSheets[sheetId][xc][i + 2];
25849
+ deconstructedSheets[sheetId][xc].splice(i, 2);
25842
25850
  }
25843
- sheet.cells[xc] = cellContent;
25851
+ // sheet.cells[xc] = cellContent;
25844
25852
  }
25845
25853
  }
25846
25854
  }
25847
25855
  }
25856
+ if (!deconstructedSheets) {
25857
+ return;
25858
+ }
25859
+ for (let sheetId in deconstructedSheets) {
25860
+ const sheet = convertedSheets.find((s) => s.id === sheetId);
25861
+ for (let xc in deconstructedSheets[sheetId]) {
25862
+ const deconstructedCell = deconstructedSheets[sheetId][xc];
25863
+ if (deconstructedCell.length === 1) {
25864
+ sheet.cells[xc] = deconstructedCell[0];
25865
+ continue;
25866
+ }
25867
+ let newContent = "";
25868
+ for (let i = 0; i < deconstructedCell.length; i += 2) {
25869
+ newContent += deconstructedCell[i] + "[" + deconstructedCell[i + 1] + "]";
25870
+ }
25871
+ newContent += deconstructedCell[deconstructedCell.length - 1];
25872
+ sheet.cells[xc] = newContent;
25873
+ }
25874
+ }
25875
+ }
25876
+ /**
25877
+ * Deconstruct the content of the cells in the sheets to extract possible table references.
25878
+ * Example from "=AVERAGE(Table1[colName1])-AVERAGE(Table2[colName2])":
25879
+ * return --> ["=AVERAGE(Table1", "colName1", ")-AVERAGE(Table2", "colName2", ")"]
25880
+ */
25881
+ function deconstructSheets(convertedSheets) {
25882
+ const deconstructedSheets = {};
25883
+ for (let sheet of convertedSheets) {
25884
+ for (let xc in sheet.cells) {
25885
+ const cellContent = sheet.cells[xc];
25886
+ if (!cellContent || !cellContent.startsWith("=")) {
25887
+ continue;
25888
+ }
25889
+ const startIndex = cellContent.indexOf("[");
25890
+ if (startIndex === -1) {
25891
+ continue;
25892
+ }
25893
+ const deconstructedCell = [];
25894
+ let possibleTable = cellContent.slice(0, startIndex);
25895
+ let possibleRef = "";
25896
+ let openBrackets = 1;
25897
+ let mainPossibleTableIndex = 0;
25898
+ let mainOpenBracketIndex = startIndex;
25899
+ for (let index = startIndex + 1; index < cellContent.length; index++) {
25900
+ if (cellContent[index] === "[") {
25901
+ if (openBrackets === 0) {
25902
+ possibleTable = cellContent.slice(mainPossibleTableIndex, index);
25903
+ mainOpenBracketIndex = index;
25904
+ }
25905
+ openBrackets++;
25906
+ continue;
25907
+ }
25908
+ if (cellContent[index] === "]") {
25909
+ openBrackets--;
25910
+ if (openBrackets === 0) {
25911
+ possibleRef = cellContent.slice(mainOpenBracketIndex + 1, index);
25912
+ deconstructedCell.push(possibleTable);
25913
+ deconstructedCell.push(possibleRef);
25914
+ mainPossibleTableIndex = index + 1;
25915
+ }
25916
+ }
25917
+ }
25918
+ if (deconstructedCell.length) {
25919
+ if (!deconstructedSheets[sheet.id]) {
25920
+ deconstructedSheets[sheet.id] = {};
25921
+ }
25922
+ deconstructedCell.push(cellContent.slice(mainPossibleTableIndex));
25923
+ deconstructedSheets[sheet.id][xc] = [...deconstructedCell];
25924
+ }
25925
+ }
25926
+ }
25927
+ return deconstructedSheets;
25848
25928
  }
25849
25929
  /**
25850
25930
  * Convert table-specific references in formulas into standard references. A table reference is composed of columns names,
@@ -33288,6 +33368,9 @@ class ErrorToolTip extends owl.Component {
33288
33368
  return undefined;
33289
33369
  }
33290
33370
  get errorOriginPositionString() {
33371
+ if (this.env.model.getters.isDashboard()) {
33372
+ return "";
33373
+ }
33291
33374
  const evaluationError = this.evaluationError;
33292
33375
  const position = evaluationError?.errorOriginPosition;
33293
33376
  if (!position || deepEquals(position, this.props.cellPosition)) {
@@ -43144,6 +43227,9 @@ class ConditionalFormattingPanel extends owl.Component {
43144
43227
  this.switchToList();
43145
43228
  }
43146
43229
  }
43230
+ else if (!this.editedCF) {
43231
+ this.switchToList();
43232
+ }
43147
43233
  });
43148
43234
  }
43149
43235
  get conditionalFormats() {
@@ -47059,10 +47145,7 @@ class SpreadsheetPivot {
47059
47145
  if (finalCell.value === null) {
47060
47146
  return { value: _t("(Undefined)") };
47061
47147
  }
47062
- return {
47063
- value: finalCell.value,
47064
- format: finalCell.format,
47065
- };
47148
+ return finalCell;
47066
47149
  }
47067
47150
  getPivotCellValueAndFormat(measureId, domain) {
47068
47151
  const dataEntries = this.filterDataEntriesFromDomain(this.dataEntries, domain);
@@ -47291,7 +47374,6 @@ pivotRegistry.add("SPREADSHEET", {
47291
47374
  ui: SpreadsheetPivot,
47292
47375
  definition: SpreadsheetPivotRuntimeDefinition,
47293
47376
  externalData: false,
47294
- onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
47295
47377
  dateGranularities: [...dateGranularities],
47296
47378
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
47297
47379
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -60057,7 +60139,7 @@ const onIterationEndEvaluationRegistry = new Registry();
60057
60139
  onIterationEndEvaluationRegistry.add("pivots", (getters) => {
60058
60140
  for (const pivotId of getters.getPivotIds()) {
60059
60141
  const pivot = getters.getPivot(pivotId);
60060
- pivotRegistry.get(pivot.type).onIterationEndEvaluation(pivot);
60142
+ pivot.markAsDirtyForEvaluation?.();
60061
60143
  }
60062
60144
  });
60063
60145
 
@@ -63063,13 +63145,13 @@ function withPivotPresentationLayer (PivotClass) {
63063
63145
  super(custom, params);
63064
63146
  this.getters = params.getters;
63065
63147
  }
63066
- init(params) {
63148
+ markAsDirtyForEvaluation() {
63067
63149
  this.cache = {};
63068
63150
  this.rankAsc = {};
63069
63151
  this.rankDesc = {};
63070
63152
  this.runningTotal = {};
63071
63153
  this.runningTotalInPercent = {};
63072
- super.init(params);
63154
+ super.markAsDirtyForEvaluation?.();
63073
63155
  }
63074
63156
  getPivotCellValueAndFormat(measureName, domain) {
63075
63157
  return this.getMeasureDisplayValue(measureName, domain);
@@ -63194,7 +63276,7 @@ function withPivotPresentationLayer (PivotClass) {
63194
63276
  return this.getSubTreeMatchingDomain(node.children, domain, domainLevel + 1);
63195
63277
  }
63196
63278
  }
63197
- return tree;
63279
+ return [];
63198
63280
  }
63199
63281
  treeToLeafDomains(tree, parentDomain = []) {
63200
63282
  const domains = [];
@@ -73692,26 +73774,28 @@ class SelectionStreamProcessorImpl {
73692
73774
  bottom: Math.min(this.getters.getNumberRows(sheetId) - 1, bottom),
73693
73775
  };
73694
73776
  };
73695
- const { col: refCol, row: refRow } = this.getReferencePosition();
73777
+ const { cell: refCell, zone: refZone } = this.getReferenceAnchor();
73778
+ const { col: refCol, row: refRow } = refCell;
73696
73779
  // check if we can shrink selection
73697
73780
  let n = 0;
73698
73781
  while (result !== null) {
73699
73782
  n++;
73700
73783
  if (deltaCol < 0) {
73701
73784
  const newRight = this.getNextAvailableCol(deltaCol, right - (n - 1), refRow);
73702
- result = refCol <= right - n ? expand({ top, left, bottom, right: newRight }) : null;
73785
+ result = refZone.right <= right - n ? expand({ top, left, bottom, right: newRight }) : null;
73703
73786
  }
73704
73787
  if (deltaCol > 0) {
73705
73788
  const newLeft = this.getNextAvailableCol(deltaCol, left + (n - 1), refRow);
73706
- result = left + n <= refCol ? expand({ top, left: newLeft, bottom, right }) : null;
73789
+ result = left + n <= refZone.left ? expand({ top, left: newLeft, bottom, right }) : null;
73707
73790
  }
73708
73791
  if (deltaRow < 0) {
73709
73792
  const newBottom = this.getNextAvailableRow(deltaRow, refCol, bottom - (n - 1));
73710
- result = refRow <= bottom - n ? expand({ top, left, bottom: newBottom, right }) : null;
73793
+ result =
73794
+ refZone.bottom <= bottom - n ? expand({ top, left, bottom: newBottom, right }) : null;
73711
73795
  }
73712
73796
  if (deltaRow > 0) {
73713
73797
  const newTop = this.getNextAvailableRow(deltaRow, refCol, top + (n - 1));
73714
- result = top + n <= refRow ? expand({ top: newTop, left, bottom, right }) : null;
73798
+ result = top + n <= refZone.top ? expand({ top: newTop, left, bottom, right }) : null;
73715
73799
  }
73716
73800
  result = result ? reorderZone(result) : result;
73717
73801
  if (result && !isEqual(result, anchor.zone)) {
@@ -73941,18 +74025,26 @@ class SelectionStreamProcessorImpl {
73941
74025
  * If the anchor is hidden, browses from left to right and top to bottom to
73942
74026
  * find a visible cell.
73943
74027
  */
73944
- getReferencePosition() {
74028
+ getReferenceAnchor() {
73945
74029
  const sheetId = this.getters.getActiveSheetId();
73946
74030
  const anchor = this.anchor;
73947
74031
  const { left, right, top, bottom } = anchor.zone;
73948
74032
  const { col: anchorCol, row: anchorRow } = anchor.cell;
74033
+ const col = this.getters.isColHidden(sheetId, anchorCol)
74034
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
74035
+ : anchorCol;
74036
+ const row = this.getters.isRowHidden(sheetId, anchorRow)
74037
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
74038
+ : anchorRow;
74039
+ const zone = this.getters.expandZone(sheetId, {
74040
+ left: col,
74041
+ right: col,
74042
+ top: row,
74043
+ bottom: row,
74044
+ });
73949
74045
  return {
73950
- col: this.getters.isColHidden(sheetId, anchorCol)
73951
- ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
73952
- : anchorCol,
73953
- row: this.getters.isRowHidden(sheetId, anchorRow)
73954
- ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
73955
- : anchorRow,
74046
+ cell: { col, row },
74047
+ zone,
73956
74048
  };
73957
74049
  }
73958
74050
  deltaToTarget(position, direction, step) {
@@ -77095,6 +77187,6 @@ exports.tokenColors = tokenColors;
77095
77187
  exports.tokenize = tokenize;
77096
77188
 
77097
77189
 
77098
- __info__.version = "18.2.19";
77099
- __info__.date = "2025-06-23T15:07:13.023Z";
77100
- __info__.hash = "430d931";
77190
+ __info__.version = "18.2.21";
77191
+ __info__.date = "2025-07-11T11:11:48.661Z";
77192
+ __info__.hash = "1c32303";
@@ -5453,6 +5453,7 @@ interface Pivot<T = PivotRuntimeDefinition> {
5453
5453
  label: string;
5454
5454
  }[];
5455
5455
  needsReevaluation: boolean;
5456
+ markAsDirtyForEvaluation?(): void;
5456
5457
  }
5457
5458
 
5458
5459
  declare class PivotUIPlugin extends CoreViewPlugin {
@@ -6359,7 +6360,6 @@ interface PivotRegistryItem {
6359
6360
  ui: PivotUIConstructor;
6360
6361
  definition: PivotDefinitionConstructor;
6361
6362
  externalData: boolean;
6362
- onIterationEndEvaluation: (pivot: Pivot) => void;
6363
6363
  dateGranularities: string[];
6364
6364
  datetimeGranularities: string[];
6365
6365
  isMeasureCandidate: (field: PivotField) => boolean;
@@ -10218,7 +10218,7 @@ declare function createPivotFormula(formulaId: string, cell: PivotTableCell): st
10218
10218
  * e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
10219
10219
  * the two group values are "42" and "won".
10220
10220
  */
10221
- declare function toNormalizedPivotValue(dimension: Pick<PivotDimension$1, "type" | "displayName" | "granularity">, groupValue: any): CellValue;
10221
+ declare function toNormalizedPivotValue(dimension: Pick<PivotDimension$1, "type" | "displayName" | "granularity">, groupValue: Maybe<CellValue | FunctionResultObject>): CellValue;
10222
10222
 
10223
10223
  interface Props$i {
10224
10224
  pivotId: string;