@odoo/o-spreadsheet 18.3.10 → 18.3.12

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.3.10
6
- * @date 2025-06-23T15:05:03.747Z
7
- * @hash 748e300
5
+ * @version 18.3.12
6
+ * @date 2025-07-11T11:11:58.998Z
7
+ * @hash d14dc96
8
8
  */
9
9
 
10
10
  'use strict';
@@ -827,6 +827,7 @@ const specialWhiteSpaceSpecialCharacters = [
827
827
  ];
828
828
  const specialWhiteSpaceRegexp = new RegExp(specialWhiteSpaceSpecialCharacters.join("|"), "g");
829
829
  const newLineRegexp = /(\r\n|\r)/g;
830
+ const whiteSpaceCharacters = specialWhiteSpaceSpecialCharacters.concat([" "]);
830
831
  /**
831
832
  * Replace all different newlines characters by \n
832
833
  */
@@ -2802,8 +2803,9 @@ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
2802
2803
  const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
2803
2804
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
2804
2805
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
2805
- const dateSeparatorsRegex = /\/|-|\s/;
2806
- const dateRegexp = /^(\d{1,4})[\/-\s](\d{1,4})([\/-\s](\d{1,4}))?$/;
2806
+ const whiteSpaceChars = whiteSpaceCharacters.join("");
2807
+ const dateSeparatorsRegex = new RegExp(`\/|-|${whiteSpaceCharacters.join("|")}`);
2808
+ const dateRegexp = new RegExp(`^(\\d{1,4})[\/${whiteSpaceChars}\-](\\d{1,4})([\/${whiteSpaceChars}\-](\\d{1,4}))?$`);
2807
2809
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
2808
2810
  /** Convert a value number representing a date, or return undefined if it isn't possible */
2809
2811
  function valueToDateNumber(value, locale) {
@@ -6795,6 +6797,8 @@ function splitWordToSpecificWidth(ctx, word, width, style) {
6795
6797
  function splitTextToWidth(ctx, text, style, width) {
6796
6798
  if (!style)
6797
6799
  style = {};
6800
+ if (isMarkdownLink(text))
6801
+ text = parseMarkdownLink(text).label;
6798
6802
  const brokenText = [];
6799
6803
  // Checking if text contains NEWLINE before split makes it very slightly slower if text contains it,
6800
6804
  // but 5-10x faster if it doesn't
@@ -8809,6 +8813,10 @@ function toNormalizedPivotValue(dimension, groupValue) {
8809
8813
  if (groupValue === null || groupValue === "null") {
8810
8814
  return null;
8811
8815
  }
8816
+ const extractedGroupValue = typeof groupValue === "object" ? groupValue.value : groupValue;
8817
+ if (isEvaluationError(extractedGroupValue)) {
8818
+ return extractedGroupValue;
8819
+ }
8812
8820
  const groupValueString = typeof groupValue === "boolean"
8813
8821
  ? toString(groupValue).toLocaleLowerCase()
8814
8822
  : toString(groupValue);
@@ -23097,7 +23105,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23097
23105
  }
23098
23106
  captureSelection(zone, col, row) {
23099
23107
  this.model.selection.capture(this, {
23100
- cell: { col: col || zone.left, row: row || zone.right },
23108
+ cell: { col: col ?? zone.left, row: row ?? zone.right },
23101
23109
  zone,
23102
23110
  }, {
23103
23111
  handleEvent: this.handleEvent.bind(this),
@@ -32905,40 +32913,112 @@ function convertPivotTableConfig(pivotTable) {
32905
32913
  * In all the sheets, replace the table-only references in the formula cells with standard references.
32906
32914
  */
32907
32915
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
32916
+ let deconstructedSheets = null;
32908
32917
  for (let tableSheet of convertedSheets) {
32909
32918
  const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
32919
+ if (!tables || tables.length === 0) {
32920
+ continue;
32921
+ }
32922
+ // Only deconstruct sheets if we are sure there are tables to process
32923
+ if (!deconstructedSheets) {
32924
+ deconstructedSheets = deconstructSheets(convertedSheets);
32925
+ }
32910
32926
  for (let table of tables) {
32911
- const tabRef = table.name + "[";
32912
- for (let sheet of convertedSheets) {
32913
- for (let xc in sheet.cells) {
32914
- const cell = sheet.cells[xc];
32915
- let cellContent = sheet.cells[xc];
32916
- if (cell && cellContent && cellContent.startsWith("=")) {
32917
- let refIndex;
32918
- while ((refIndex = cellContent.indexOf(tabRef)) !== -1) {
32919
- let endIndex = refIndex + tabRef.length;
32920
- let openBrackets = 1;
32921
- while (openBrackets > 0 && endIndex < cellContent.length) {
32922
- if (cellContent[endIndex] === "[") {
32923
- openBrackets++;
32924
- }
32925
- else if (cellContent[endIndex] === "]") {
32926
- openBrackets--;
32927
- }
32928
- endIndex++;
32929
- }
32930
- let reference = cellContent.slice(refIndex + tabRef.length, endIndex - 1);
32931
- const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
32932
- const convertedRef = convertTableReference(sheetPrefix, reference, table, xc);
32933
- cellContent =
32934
- cellContent.slice(0, refIndex) + convertedRef + cellContent.slice(endIndex);
32927
+ for (let sheetId in deconstructedSheets) {
32928
+ const sheet = convertedSheets.find((s) => s.id === sheetId);
32929
+ for (let xc in deconstructedSheets[sheetId]) {
32930
+ const deconstructedCell = deconstructedSheets[sheetId][xc];
32931
+ for (let i = deconstructedCell.length - 3; i >= 0; i -= 2) {
32932
+ const possibleTable = deconstructedSheets[sheetId][xc][i];
32933
+ if (!possibleTable.endsWith(table.name)) {
32934
+ continue;
32935
32935
  }
32936
+ const possibleRef = deconstructedSheets[sheetId][xc][i + 1];
32937
+ const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
32938
+ const convertedRef = convertTableReference(sheetPrefix, possibleRef, table, xc);
32939
+ deconstructedSheets[sheetId][xc][i + 2] =
32940
+ possibleTable.slice(0, possibleTable.indexOf(table.name)) +
32941
+ convertedRef +
32942
+ deconstructedSheets[sheetId][xc][i + 2];
32943
+ deconstructedSheets[sheetId][xc].splice(i, 2);
32936
32944
  }
32937
- sheet.cells[xc] = cellContent;
32945
+ // sheet.cells[xc] = cellContent;
32938
32946
  }
32939
32947
  }
32940
32948
  }
32941
32949
  }
32950
+ if (!deconstructedSheets) {
32951
+ return;
32952
+ }
32953
+ for (let sheetId in deconstructedSheets) {
32954
+ const sheet = convertedSheets.find((s) => s.id === sheetId);
32955
+ for (let xc in deconstructedSheets[sheetId]) {
32956
+ const deconstructedCell = deconstructedSheets[sheetId][xc];
32957
+ if (deconstructedCell.length === 1) {
32958
+ sheet.cells[xc] = deconstructedCell[0];
32959
+ continue;
32960
+ }
32961
+ let newContent = "";
32962
+ for (let i = 0; i < deconstructedCell.length; i += 2) {
32963
+ newContent += deconstructedCell[i] + "[" + deconstructedCell[i + 1] + "]";
32964
+ }
32965
+ newContent += deconstructedCell[deconstructedCell.length - 1];
32966
+ sheet.cells[xc] = newContent;
32967
+ }
32968
+ }
32969
+ }
32970
+ /**
32971
+ * Deconstruct the content of the cells in the sheets to extract possible table references.
32972
+ * Example from "=AVERAGE(Table1[colName1])-AVERAGE(Table2[colName2])":
32973
+ * return --> ["=AVERAGE(Table1", "colName1", ")-AVERAGE(Table2", "colName2", ")"]
32974
+ */
32975
+ function deconstructSheets(convertedSheets) {
32976
+ const deconstructedSheets = {};
32977
+ for (let sheet of convertedSheets) {
32978
+ for (let xc in sheet.cells) {
32979
+ const cellContent = sheet.cells[xc];
32980
+ if (!cellContent || !cellContent.startsWith("=")) {
32981
+ continue;
32982
+ }
32983
+ const startIndex = cellContent.indexOf("[");
32984
+ if (startIndex === -1) {
32985
+ continue;
32986
+ }
32987
+ const deconstructedCell = [];
32988
+ let possibleTable = cellContent.slice(0, startIndex);
32989
+ let possibleRef = "";
32990
+ let openBrackets = 1;
32991
+ let mainPossibleTableIndex = 0;
32992
+ let mainOpenBracketIndex = startIndex;
32993
+ for (let index = startIndex + 1; index < cellContent.length; index++) {
32994
+ if (cellContent[index] === "[") {
32995
+ if (openBrackets === 0) {
32996
+ possibleTable = cellContent.slice(mainPossibleTableIndex, index);
32997
+ mainOpenBracketIndex = index;
32998
+ }
32999
+ openBrackets++;
33000
+ continue;
33001
+ }
33002
+ if (cellContent[index] === "]") {
33003
+ openBrackets--;
33004
+ if (openBrackets === 0) {
33005
+ possibleRef = cellContent.slice(mainOpenBracketIndex + 1, index);
33006
+ deconstructedCell.push(possibleTable);
33007
+ deconstructedCell.push(possibleRef);
33008
+ mainPossibleTableIndex = index + 1;
33009
+ }
33010
+ }
33011
+ }
33012
+ if (deconstructedCell.length) {
33013
+ if (!deconstructedSheets[sheet.id]) {
33014
+ deconstructedSheets[sheet.id] = {};
33015
+ }
33016
+ deconstructedCell.push(cellContent.slice(mainPossibleTableIndex));
33017
+ deconstructedSheets[sheet.id][xc] = [...deconstructedCell];
33018
+ }
33019
+ }
33020
+ }
33021
+ return deconstructedSheets;
32942
33022
  }
32943
33023
  /**
32944
33024
  * Convert table-specific references in formulas into standard references. A table reference is composed of columns names,
@@ -36000,6 +36080,9 @@ class ErrorToolTip extends owl.Component {
36000
36080
  return undefined;
36001
36081
  }
36002
36082
  get errorOriginPositionString() {
36083
+ if (this.env.model.getters.isDashboard()) {
36084
+ return "";
36085
+ }
36003
36086
  const evaluationError = this.evaluationError;
36004
36087
  const position = evaluationError?.errorOriginPosition;
36005
36088
  if (!position || deepEquals(position, this.props.cellPosition)) {
@@ -45858,6 +45941,9 @@ class ConditionalFormattingPanel extends owl.Component {
45858
45941
  this.switchToList();
45859
45942
  }
45860
45943
  }
45944
+ else if (!this.editedCF) {
45945
+ this.switchToList();
45946
+ }
45861
45947
  });
45862
45948
  }
45863
45949
  get conditionalFormats() {
@@ -49776,10 +49862,7 @@ class SpreadsheetPivot {
49776
49862
  if (finalCell.value === null) {
49777
49863
  return { value: _t("(Undefined)") };
49778
49864
  }
49779
- return {
49780
- value: finalCell.value,
49781
- format: finalCell.format,
49782
- };
49865
+ return finalCell;
49783
49866
  }
49784
49867
  getPivotCellValueAndFormat(measureId, domain) {
49785
49868
  const dataEntries = this.filterDataEntriesFromDomain(this.dataEntries, domain);
@@ -50008,7 +50091,6 @@ pivotRegistry.add("SPREADSHEET", {
50008
50091
  ui: SpreadsheetPivot,
50009
50092
  definition: SpreadsheetPivotRuntimeDefinition,
50010
50093
  externalData: false,
50011
- onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
50012
50094
  dateGranularities: [...dateGranularities],
50013
50095
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
50014
50096
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -62962,7 +63044,7 @@ const onIterationEndEvaluationRegistry = new Registry();
62962
63044
  onIterationEndEvaluationRegistry.add("pivots", (getters) => {
62963
63045
  for (const pivotId of getters.getPivotIds()) {
62964
63046
  const pivot = getters.getPivot(pivotId);
62965
- pivotRegistry.get(pivot.type).onIterationEndEvaluation(pivot);
63047
+ pivot.markAsDirtyForEvaluation?.();
62966
63048
  }
62967
63049
  });
62968
63050
 
@@ -65984,13 +66066,13 @@ function withPivotPresentationLayer (PivotClass) {
65984
66066
  super(custom, params);
65985
66067
  this.getters = params.getters;
65986
66068
  }
65987
- init(params) {
66069
+ markAsDirtyForEvaluation() {
65988
66070
  this.cache = {};
65989
66071
  this.rankAsc = {};
65990
66072
  this.rankDesc = {};
65991
66073
  this.runningTotal = {};
65992
66074
  this.runningTotalInPercent = {};
65993
- super.init(params);
66075
+ super.markAsDirtyForEvaluation?.();
65994
66076
  }
65995
66077
  getPivotCellValueAndFormat(measureName, domain) {
65996
66078
  return this.getMeasureDisplayValue(measureName, domain);
@@ -66115,7 +66197,7 @@ function withPivotPresentationLayer (PivotClass) {
66115
66197
  return this.getSubTreeMatchingDomain(node.children, domain, domainLevel + 1);
66116
66198
  }
66117
66199
  }
66118
- return tree;
66200
+ return [];
66119
66201
  }
66120
66202
  treeToLeafDomains(tree, parentDomain = []) {
66121
66203
  const domains = [];
@@ -77342,26 +77424,28 @@ class SelectionStreamProcessorImpl {
77342
77424
  bottom: Math.min(this.getters.getNumberRows(sheetId) - 1, bottom),
77343
77425
  };
77344
77426
  };
77345
- const { col: refCol, row: refRow } = this.getReferencePosition();
77427
+ const { cell: refCell, zone: refZone } = this.getReferenceAnchor();
77428
+ const { col: refCol, row: refRow } = refCell;
77346
77429
  // check if we can shrink selection
77347
77430
  let n = 0;
77348
77431
  while (result !== null) {
77349
77432
  n++;
77350
77433
  if (deltaCol < 0) {
77351
77434
  const newRight = this.getNextAvailableCol(deltaCol, right - (n - 1), refRow);
77352
- result = refCol <= right - n ? expand({ top, left, bottom, right: newRight }) : null;
77435
+ result = refZone.right <= right - n ? expand({ top, left, bottom, right: newRight }) : null;
77353
77436
  }
77354
77437
  if (deltaCol > 0) {
77355
77438
  const newLeft = this.getNextAvailableCol(deltaCol, left + (n - 1), refRow);
77356
- result = left + n <= refCol ? expand({ top, left: newLeft, bottom, right }) : null;
77439
+ result = left + n <= refZone.left ? expand({ top, left: newLeft, bottom, right }) : null;
77357
77440
  }
77358
77441
  if (deltaRow < 0) {
77359
77442
  const newBottom = this.getNextAvailableRow(deltaRow, refCol, bottom - (n - 1));
77360
- result = refRow <= bottom - n ? expand({ top, left, bottom: newBottom, right }) : null;
77443
+ result =
77444
+ refZone.bottom <= bottom - n ? expand({ top, left, bottom: newBottom, right }) : null;
77361
77445
  }
77362
77446
  if (deltaRow > 0) {
77363
77447
  const newTop = this.getNextAvailableRow(deltaRow, refCol, top + (n - 1));
77364
- result = top + n <= refRow ? expand({ top: newTop, left, bottom, right }) : null;
77448
+ result = top + n <= refZone.top ? expand({ top: newTop, left, bottom, right }) : null;
77365
77449
  }
77366
77450
  result = result ? reorderZone(result) : result;
77367
77451
  if (result && !isEqual(result, anchor.zone)) {
@@ -77591,18 +77675,26 @@ class SelectionStreamProcessorImpl {
77591
77675
  * If the anchor is hidden, browses from left to right and top to bottom to
77592
77676
  * find a visible cell.
77593
77677
  */
77594
- getReferencePosition() {
77678
+ getReferenceAnchor() {
77595
77679
  const sheetId = this.getters.getActiveSheetId();
77596
77680
  const anchor = this.anchor;
77597
77681
  const { left, right, top, bottom } = anchor.zone;
77598
77682
  const { col: anchorCol, row: anchorRow } = anchor.cell;
77683
+ const col = this.getters.isColHidden(sheetId, anchorCol)
77684
+ ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
77685
+ : anchorCol;
77686
+ const row = this.getters.isRowHidden(sheetId, anchorRow)
77687
+ ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
77688
+ : anchorRow;
77689
+ const zone = this.getters.expandZone(sheetId, {
77690
+ left: col,
77691
+ right: col,
77692
+ top: row,
77693
+ bottom: row,
77694
+ });
77599
77695
  return {
77600
- col: this.getters.isColHidden(sheetId, anchorCol)
77601
- ? this.getters.findVisibleHeader(sheetId, "COL", left, right) || anchorCol
77602
- : anchorCol,
77603
- row: this.getters.isRowHidden(sheetId, anchorRow)
77604
- ? this.getters.findVisibleHeader(sheetId, "ROW", top, bottom) || anchorRow
77605
- : anchorRow,
77696
+ cell: { col, row },
77697
+ zone,
77606
77698
  };
77607
77699
  }
77608
77700
  deltaToTarget(position, direction, step) {
@@ -80758,6 +80850,6 @@ exports.tokenColors = tokenColors;
80758
80850
  exports.tokenize = tokenize;
80759
80851
 
80760
80852
 
80761
- __info__.version = "18.3.10";
80762
- __info__.date = "2025-06-23T15:05:03.747Z";
80763
- __info__.hash = "748e300";
80853
+ __info__.version = "18.3.12";
80854
+ __info__.date = "2025-07-11T11:11:58.998Z";
80855
+ __info__.hash = "d14dc96";
@@ -5684,6 +5684,7 @@ interface Pivot<T = PivotRuntimeDefinition> {
5684
5684
  label: string;
5685
5685
  }[];
5686
5686
  needsReevaluation: boolean;
5687
+ markAsDirtyForEvaluation?(): void;
5687
5688
  }
5688
5689
 
5689
5690
  declare class PivotUIPlugin extends CoreViewPlugin {
@@ -6603,7 +6604,6 @@ interface PivotRegistryItem {
6603
6604
  ui: PivotUIConstructor;
6604
6605
  definition: PivotDefinitionConstructor;
6605
6606
  externalData: boolean;
6606
- onIterationEndEvaluation: (pivot: Pivot) => void;
6607
6607
  dateGranularities: string[];
6608
6608
  datetimeGranularities: string[];
6609
6609
  isMeasureCandidate: (field: PivotField) => boolean;
@@ -10494,7 +10494,7 @@ declare function createPivotFormula(formulaId: string, cell: PivotTableCell): st
10494
10494
  * e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
10495
10495
  * the two group values are "42" and "won".
10496
10496
  */
10497
- declare function toNormalizedPivotValue(dimension: Pick<PivotDimension$1, "type" | "displayName" | "granularity">, groupValue: any): CellValue;
10497
+ declare function toNormalizedPivotValue(dimension: Pick<PivotDimension$1, "type" | "displayName" | "granularity">, groupValue: Maybe<CellValue | FunctionResultObject>): CellValue;
10498
10498
  declare function toFunctionPivotValue(value: CellValue, dimension: Pick<PivotDimension$1, "type" | "granularity">): string;
10499
10499
 
10500
10500
  interface Props$h {