@odoo/o-spreadsheet 18.5.0-alpha.10 → 18.5.0-alpha.11

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.5.0-alpha.10
6
- * @date 2025-08-21T06:42:10.830Z
7
- * @hash 5800d1b
5
+ * @version 18.5.0-alpha.11
6
+ * @date 2025-08-26T10:14:05.357Z
7
+ * @hash b913e49
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -724,7 +724,7 @@
724
724
  /*
725
725
  * Concatenate an array of strings.
726
726
  */
727
- function concat(chars) {
727
+ function concat$1(chars) {
728
728
  // ~40% faster than chars.join("")
729
729
  let output = "";
730
730
  for (let i = 0, len = chars.length; i < len; i++) {
@@ -1196,7 +1196,7 @@
1196
1196
  if (alphaHex !== 255) {
1197
1197
  vals.push(alphaHex);
1198
1198
  }
1199
- return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1199
+ return "#" + concat$1(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1200
1200
  }
1201
1201
  /**
1202
1202
  * RGBA to HEX representation (#RRGGBBAA).
@@ -5110,6 +5110,9 @@
5110
5110
  if (content === "") {
5111
5111
  return null;
5112
5112
  }
5113
+ if (content.includes("\n")) {
5114
+ return content;
5115
+ }
5113
5116
  if (isNumber(content, DEFAULT_LOCALE)) {
5114
5117
  return parseNumber(content, DEFAULT_LOCALE);
5115
5118
  }
@@ -7318,14 +7321,11 @@
7318
7321
  const width = content[0].length, height = content.length;
7319
7322
  return target.map((t) => splitZoneForPaste(t, width, height)).flat();
7320
7323
  }
7321
- function parseOSClipboardContent(content, clipboardId) {
7324
+ function parseOSClipboardContent(content) {
7322
7325
  let spreadsheetContent = undefined;
7323
7326
  if (content[ClipboardMIMEType.Html]) {
7324
7327
  const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
7325
- const oSheetClipboardData = htmlDocument
7326
- .querySelector("div")
7327
- ?.getAttribute("data-osheet-clipboard");
7328
- spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
7328
+ spreadsheetContent = getOSheetDataFromHTML(htmlDocument);
7329
7329
  }
7330
7330
  const textContent = content[ClipboardMIMEType.PlainText] || "";
7331
7331
  let imageBlob = undefined;
@@ -7344,6 +7344,17 @@
7344
7344
  };
7345
7345
  return osClipboardContent;
7346
7346
  }
7347
+ function getOSheetDataFromHTML(htmlDocument) {
7348
+ const attributes = [...htmlDocument.documentElement.attributes];
7349
+ // Check if it's a Microsoft Office clipboard data (it will have some namespaces defined in the root element)
7350
+ if (attributes.some((attr) => attr.value.includes("microsoft"))) {
7351
+ return undefined;
7352
+ }
7353
+ const oSheetClipboardData = htmlDocument
7354
+ .querySelector("div")
7355
+ ?.getAttribute("data-osheet-clipboard");
7356
+ return oSheetClipboardData && JSON.parse(oSheetClipboardData);
7357
+ }
7347
7358
  /**
7348
7359
  * Applies each clipboard handler to paste its corresponding data into the target.
7349
7360
  */
@@ -18337,7 +18348,6 @@ stores.inject(MyMetaStore, storeInstance);
18337
18348
  const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
18338
18349
  const UNARY_OPERATORS_PREFIX = ["-", "+"];
18339
18350
  const UNARY_OPERATORS_POSTFIX = ["%"];
18340
- const ASSOCIATIVE_OPERATORS = ["*", "+", "&"];
18341
18351
  class TokenList {
18342
18352
  tokens;
18343
18353
  currentIndex = 0;
@@ -18358,8 +18368,8 @@ stores.inject(MyMetaStore, storeInstance);
18358
18368
  }
18359
18369
  }
18360
18370
  const OP_PRIORITY = {
18371
+ "%": 40,
18361
18372
  "^": 30,
18362
- "%": 30,
18363
18373
  "*": 20,
18364
18374
  "/": 20,
18365
18375
  "+": 15,
@@ -18640,64 +18650,6 @@ stores.inject(MyMetaStore, storeInstance);
18640
18650
  return ast;
18641
18651
  }
18642
18652
  }
18643
- /**
18644
- * Converts an ast formula to the corresponding string
18645
- */
18646
- function astToFormula(ast) {
18647
- switch (ast.type) {
18648
- case "FUNCALL":
18649
- const args = ast.args.map((arg) => astToFormula(arg));
18650
- return `${ast.value}(${args.join(",")})`;
18651
- case "NUMBER":
18652
- return ast.value.toString();
18653
- case "REFERENCE":
18654
- return ast.value;
18655
- case "STRING":
18656
- return `"${ast.value}"`;
18657
- case "BOOLEAN":
18658
- return ast.value ? "TRUE" : "FALSE";
18659
- case "UNARY_OPERATION":
18660
- return ast.postfix
18661
- ? leftOperandToFormula(ast) + ast.value
18662
- : ast.value + rightOperandToFormula(ast);
18663
- case "BIN_OPERATION":
18664
- return leftOperandToFormula(ast) + ast.value + rightOperandToFormula(ast);
18665
- default:
18666
- return ast.value;
18667
- }
18668
- }
18669
- /**
18670
- * Convert the left operand of a binary operation to the corresponding string
18671
- * and enclose the result inside parenthesis if necessary.
18672
- */
18673
- function leftOperandToFormula(operationAST) {
18674
- const mainOperator = operationAST.value;
18675
- const leftOperation = "left" in operationAST ? operationAST.left : operationAST.operand;
18676
- const leftOperator = leftOperation.value;
18677
- const needParenthesis = leftOperation.type === "BIN_OPERATION" && OP_PRIORITY[leftOperator] < OP_PRIORITY[mainOperator];
18678
- return needParenthesis ? `(${astToFormula(leftOperation)})` : astToFormula(leftOperation);
18679
- }
18680
- /**
18681
- * Convert the right operand of a binary or unary operation to the corresponding string
18682
- * and enclose the result inside parenthesis if necessary.
18683
- */
18684
- function rightOperandToFormula(operationAST) {
18685
- const mainOperator = operationAST.value;
18686
- const rightOperation = "right" in operationAST ? operationAST.right : operationAST.operand;
18687
- const rightPriority = OP_PRIORITY[rightOperation.value];
18688
- const mainPriority = OP_PRIORITY[mainOperator];
18689
- let needParenthesis = false;
18690
- if (rightOperation.type !== "BIN_OPERATION") {
18691
- needParenthesis = false;
18692
- }
18693
- else if (rightPriority < mainPriority) {
18694
- needParenthesis = true;
18695
- }
18696
- else if (rightPriority === mainPriority && !ASSOCIATIVE_OPERATORS.includes(mainOperator)) {
18697
- needParenthesis = true;
18698
- }
18699
- return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
18700
- }
18701
18653
 
18702
18654
  /**
18703
18655
  * Add the following information on tokens:
@@ -22196,7 +22148,7 @@ stores.inject(MyMetaStore, storeInstance);
22196
22148
  };
22197
22149
  }
22198
22150
  }
22199
- return concat(tokens.map((token) => token.value));
22151
+ return concat$1(tokens.map((token) => token.value));
22200
22152
  }
22201
22153
  function adaptStringRange(defaultSheetId, sheetXC, applyChange) {
22202
22154
  const sheetName = splitReference(sheetXC).sheetName;
@@ -28281,10 +28233,6 @@ stores.inject(MyMetaStore, storeInstance);
28281
28233
  };
28282
28234
  }
28283
28235
  getDefinitionForExcel() {
28284
- // Excel does not support aggregating labels
28285
- if (this.aggregated) {
28286
- return undefined;
28287
- }
28288
28236
  const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28289
28237
  const definition = this.getDefinition();
28290
28238
  return {
@@ -29486,9 +29434,6 @@ stores.inject(MyMetaStore, storeInstance);
29486
29434
  };
29487
29435
  }
29488
29436
  getDefinitionForExcel() {
29489
- if (this.aggregated) {
29490
- return undefined;
29491
- }
29492
29437
  const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
29493
29438
  const definition = this.getDefinition();
29494
29439
  return {
@@ -29636,10 +29581,6 @@ stores.inject(MyMetaStore, storeInstance);
29636
29581
  return new ScatterChart(definition, this.sheetId, this.getters);
29637
29582
  }
29638
29583
  getDefinitionForExcel() {
29639
- // Excel does not support aggregating labels
29640
- if (this.aggregated) {
29641
- return undefined;
29642
- }
29643
29584
  const dataSets = this.dataSets
29644
29585
  .map((ds) => toExcelDataset(this.getters, ds))
29645
29586
  .filter((ds) => ds.range !== "");
@@ -30958,7 +30899,7 @@ stores.inject(MyMetaStore, storeInstance);
30958
30899
  const value = expressions[i] instanceof XMLString ? expressions[i] : xmlEscape(expressions[i]);
30959
30900
  str.push(value + strings[i + 1]);
30960
30901
  }
30961
- return new XMLString(concat(str));
30902
+ return new XMLString(concat$1(str));
30962
30903
  }
30963
30904
  /**
30964
30905
  * Removes the escaped namespace of all the xml tags in the string.
@@ -32044,10 +31985,10 @@ stores.inject(MyMetaStore, storeInstance);
32044
31985
  const item = chartSubtypeRegistry.get(type);
32045
31986
  return {
32046
31987
  id: item.chartType,
32047
- label: item.displayName,
31988
+ label: _t("Show as %(chart_type)s chart", { chart_type: item.displayName.toLowerCase() }),
32048
31989
  onClick: () => this.updateType(item.chartType),
32049
- isSelected: item.chartType === this.getters.getChartDefinition(this.chartId).type,
32050
- iconClass: this.getIconClasses(item.chartType),
31990
+ class: item.chartType === definition.type ? "active" : "",
31991
+ preview: item.preview,
32051
31992
  };
32052
31993
  });
32053
31994
  }
@@ -32083,18 +32024,6 @@ stores.inject(MyMetaStore, storeInstance);
32083
32024
  sheetId: this.getters.getActiveSheetId(),
32084
32025
  });
32085
32026
  }
32086
- getIconClasses(type) {
32087
- if (type.includes("bar")) {
32088
- return "fa fa-bar-chart";
32089
- }
32090
- if (type.includes("line")) {
32091
- return "fa fa-line-chart";
32092
- }
32093
- if (type.includes("pie")) {
32094
- return "fa fa-pie-chart";
32095
- }
32096
- return "";
32097
- }
32098
32027
  }
32099
32028
 
32100
32029
  class ChartDashboardMenu extends owl.Component {
@@ -32133,20 +32062,11 @@ stores.inject(MyMetaStore, storeInstance);
32133
32062
  if (definition.type === "scorecard") {
32134
32063
  return undefined;
32135
32064
  }
32136
- if (this.props.chartId === this.fullScreenFigureStore.fullScreenFigure?.id) {
32137
- return {
32138
- id: "fullScreenChart",
32139
- label: _t("Exit Full Screen"),
32140
- iconClass: "fa fa-compress",
32141
- onClick: () => {
32142
- this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32143
- },
32144
- };
32145
- }
32065
+ const isFullScreen = this.props.chartId === this.fullScreenFigureStore.fullScreenFigure?.id;
32146
32066
  return {
32147
32067
  id: "fullScreenChart",
32148
- label: _t("Full Screen"),
32149
- iconClass: "fa fa-expand",
32068
+ label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
32069
+ class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
32150
32070
  onClick: () => {
32151
32071
  this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32152
32072
  },
@@ -34328,7 +34248,7 @@ stores.inject(MyMetaStore, storeInstance);
34328
34248
  if (isFormula(content)) {
34329
34249
  const missing = this.getNumberOfMissingParenthesis(this.currentTokens);
34330
34250
  if (missing > 0) {
34331
- content += concat(new Array(missing).fill(")"));
34251
+ content += concat$1(new Array(missing).fill(")"));
34332
34252
  }
34333
34253
  }
34334
34254
  }
@@ -34380,9 +34300,10 @@ stores.inject(MyMetaStore, storeInstance);
34380
34300
  if (isNewCurrentContent || this.editionMode !== "inactive") {
34381
34301
  const locale = this.getters.getLocale();
34382
34302
  this.currentTokens = isFormula(text) ? composerTokenize(text, locale) : [];
34383
- if (this.currentTokens.length > 100) {
34303
+ const nonSpaceTokensCount = this.currentTokens.filter((token) => token.type !== "SPACE").length;
34304
+ if (nonSpaceTokensCount > 1000) {
34384
34305
  if (raise) {
34385
- this.notificationStore.raiseError(_t("This formula has over 100 parts. It can't be processed properly, consider splitting it into multiple cells"));
34306
+ this.notificationStore.raiseError(_t("This formula has over 1000 parts. It can't be processed properly, consider splitting it into multiple cells"));
34386
34307
  }
34387
34308
  }
34388
34309
  }
@@ -34803,6 +34724,8 @@ stores.inject(MyMetaStore, storeInstance);
34803
34724
  padding-right: 3px;
34804
34725
  outline: none;
34805
34726
 
34727
+ tab-size: 4;
34728
+
34806
34729
  p {
34807
34730
  margin-bottom: 0px;
34808
34731
 
@@ -48653,6 +48576,280 @@ stores.inject(MyMetaStore, storeInstance);
48653
48576
  }
48654
48577
  }
48655
48578
 
48579
+ const ASSOCIATIVE_OPERATORS = ["*", "+", "&"];
48580
+ /**
48581
+ * Pretty-prints formula ASTs into readable formulas.
48582
+ *
48583
+ * Implements a Wadler-inspired pretty printer:
48584
+ * it converts an AST into a `Doc` structure,
48585
+ * and then chooses between compact (flat) or expanded (with
48586
+ * line breaks and indentation) layouts depending on space.
48587
+ *
48588
+ * References:
48589
+ * - https://lik.ai/blog/how-a-pretty-printer-works/
48590
+ * - Wadler, "A prettier printer": https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf
48591
+ */
48592
+ function prettify(ast, width = 60) {
48593
+ return "=" + print(astToDoc(ast), width - 1); // width-1 because of the leading '='
48594
+ }
48595
+ /**
48596
+ * A possible line break.
48597
+ * Printed as either space or newline.
48598
+ */
48599
+ function line() {
48600
+ return { type: "insertLine" };
48601
+ }
48602
+ /**
48603
+ * Increase indentation for a nested block.
48604
+ */
48605
+ function nest(indentLevel, doc) {
48606
+ return { type: "nest", indentLevel, doc };
48607
+ }
48608
+ /**
48609
+ * Combines multiple docs into a single doc, concatenating them side by side
48610
+ * without any line breaks or indentation.
48611
+ */
48612
+ function concat(docs) {
48613
+ return { type: "concat", docs };
48614
+ }
48615
+ /**
48616
+ * Marks a document as a unit to be printed "flat" (all on one line)
48617
+ * if it fits within the width, otherwise with line breaks.
48618
+ */
48619
+ function group(doc) {
48620
+ return chooseBetween(flatten(doc), doc);
48621
+ }
48622
+ /**
48623
+ * Creates a choice between two alternative layouts.
48624
+ * The formatter tries `doc1`; if it does not fit within
48625
+ * the line width, it falls back to `doc2`.
48626
+ */
48627
+ function chooseBetween(doc1, doc2) {
48628
+ return { type: "chooseBetween", doc1, doc2 };
48629
+ }
48630
+ /**
48631
+ * Flattens a doc into its single-line form.
48632
+ */
48633
+ function flatten(doc) {
48634
+ if (typeof doc === "string") {
48635
+ return doc;
48636
+ }
48637
+ if (doc.type === "chooseBetween") {
48638
+ // Normally should be "chooseBetween(flatten(doc.doc1), flatten(doc.doc2))",
48639
+ // but this is simplified for performance reasons.
48640
+ return flatten(doc.doc1);
48641
+ }
48642
+ if (doc.type === "concat") {
48643
+ return concat(doc.docs.map(flatten));
48644
+ }
48645
+ if (doc.type === "nest") {
48646
+ return {
48647
+ type: "nest",
48648
+ indentLevel: doc.indentLevel,
48649
+ doc: flatten(doc.doc),
48650
+ };
48651
+ }
48652
+ if (doc.type === "insertLine") {
48653
+ return "";
48654
+ }
48655
+ return doc;
48656
+ }
48657
+ const getIndentationString = memoize(function getIndentationString(indentLevel) {
48658
+ return "\n" + "\t".repeat(indentLevel);
48659
+ });
48660
+ /**
48661
+ * Converts a `Doc` into a string representation that fits within
48662
+ * the specified width.
48663
+ */
48664
+ function print(doc, width) {
48665
+ return stringify(selectBestLayout(width, doc));
48666
+ }
48667
+ /**
48668
+ * Join all segments of a LinkedString into the final string.
48669
+ */
48670
+ function stringify(linkedString) {
48671
+ let result = "";
48672
+ while (linkedString) {
48673
+ result += linkedString.subString;
48674
+ linkedString = linkedString.next;
48675
+ }
48676
+ return result;
48677
+ }
48678
+ /**
48679
+ * Layout selection for a `Doc` that fits within the given width.
48680
+ */
48681
+ function selectBestLayout(width, doc) {
48682
+ const head = {
48683
+ indentLevel: 0,
48684
+ doc,
48685
+ next: null,
48686
+ };
48687
+ return _selectBestLayout(width, 0, head);
48688
+ }
48689
+ function _selectBestLayout(width, currentIndentLevel, head) {
48690
+ if (head === null) {
48691
+ return null;
48692
+ }
48693
+ const { indentLevel, doc, next } = head;
48694
+ if (typeof doc === "string") {
48695
+ return {
48696
+ subString: doc,
48697
+ next: _selectBestLayout(width, currentIndentLevel + doc.length, next),
48698
+ };
48699
+ }
48700
+ if (doc.type === "concat") {
48701
+ let newHead = next;
48702
+ for (let i = doc.docs.length - 1; i >= 0; i--) {
48703
+ newHead = { indentLevel, doc: doc.docs[i], next: newHead };
48704
+ }
48705
+ return _selectBestLayout(width, currentIndentLevel, newHead);
48706
+ }
48707
+ if (doc.type === "nest") {
48708
+ return _selectBestLayout(width, currentIndentLevel, {
48709
+ indentLevel: indentLevel + doc.indentLevel,
48710
+ doc: doc.doc,
48711
+ next,
48712
+ });
48713
+ }
48714
+ if (doc.type === "insertLine") {
48715
+ return {
48716
+ subString: getIndentationString(indentLevel),
48717
+ next: _selectBestLayout(width, indentLevel, next),
48718
+ };
48719
+ }
48720
+ if (doc.type === "chooseBetween") {
48721
+ const head1 = { indentLevel, doc: doc.doc1, next };
48722
+ const possibleLinkedString = _selectBestLayout(width, currentIndentLevel, head1);
48723
+ if (fits(width - currentIndentLevel, possibleLinkedString)) {
48724
+ return possibleLinkedString;
48725
+ }
48726
+ const head2 = { indentLevel, doc: doc.doc2, next };
48727
+ return _selectBestLayout(width, currentIndentLevel, head2);
48728
+ }
48729
+ return null;
48730
+ }
48731
+ /**
48732
+ * Check if a layout fits on a single line within width.
48733
+ */
48734
+ function fits(width, linkedString) {
48735
+ while (linkedString) {
48736
+ if (linkedString.subString[0] === "\n") {
48737
+ return true;
48738
+ }
48739
+ width -= linkedString.subString.length;
48740
+ if (width < 0)
48741
+ return false;
48742
+ linkedString = linkedString.next;
48743
+ }
48744
+ return true;
48745
+ }
48746
+ function astToDoc(ast) {
48747
+ switch (ast.type) {
48748
+ case "NUMBER":
48749
+ return String(ast.value);
48750
+ case "STRING":
48751
+ return `"${ast.value}"`;
48752
+ case "BOOLEAN":
48753
+ return ast.value ? "TRUE" : "FALSE";
48754
+ case "REFERENCE":
48755
+ return ast.value;
48756
+ case "FUNCALL":
48757
+ const docs = ast.args.map(astToDoc);
48758
+ return wrapInParentheses(concat(docs.map((doc, i) => (i < 1 ? doc : concat([", ", line(), doc])))), ast.value);
48759
+ case "UNARY_OPERATION":
48760
+ const operandDoc = astToDoc(ast.operand);
48761
+ const needParenthesis = ast.postfix
48762
+ ? leftOperandNeedsParenthesis(ast)
48763
+ : rightOperandNeedsParenthesis(ast);
48764
+ const finalOperandDoc = needParenthesis ? wrapInParentheses(operandDoc) : operandDoc;
48765
+ return ast.postfix
48766
+ ? concat([finalOperandDoc, ast.value])
48767
+ : concat([ast.value, finalOperandDoc]);
48768
+ case "BIN_OPERATION": {
48769
+ const leftDoc = astToDoc(ast.left);
48770
+ const needParenthesisLeftDoc = leftOperandNeedsParenthesis(ast);
48771
+ const finalLeftDoc = needParenthesisLeftDoc ? wrapInParentheses(leftDoc) : leftDoc;
48772
+ const rightDoc = astToDoc(ast.right);
48773
+ const needParenthesisRightDoc = rightOperandNeedsParenthesis(ast);
48774
+ const finalRightDoc = needParenthesisRightDoc ? wrapInParentheses(rightDoc) : rightDoc;
48775
+ const operator = `${ast.value}`;
48776
+ return group(concat([finalLeftDoc, operator, nest(1, concat([line(), finalRightDoc]))]));
48777
+ }
48778
+ case "SYMBOL":
48779
+ return ast.value;
48780
+ case "EMPTY":
48781
+ return "";
48782
+ }
48783
+ }
48784
+ /**
48785
+ * Wraps a `Doc` in parentheses (with optional function name).
48786
+ */
48787
+ function wrapInParentheses(doc, functionName = undefined) {
48788
+ const docToConcat = ["(", nest(1, concat([line(), doc])), line(), ")"];
48789
+ if (functionName) {
48790
+ docToConcat.unshift(functionName);
48791
+ }
48792
+ return group(concat(docToConcat));
48793
+ }
48794
+ /**
48795
+ * Converts an ast formula to the corresponding string
48796
+ */
48797
+ function astToFormula(ast) {
48798
+ switch (ast.type) {
48799
+ case "FUNCALL":
48800
+ const args = ast.args.map((arg) => astToFormula(arg));
48801
+ return `${ast.value}(${args.join(",")})`;
48802
+ case "NUMBER":
48803
+ return ast.value.toString();
48804
+ case "REFERENCE":
48805
+ return ast.value;
48806
+ case "STRING":
48807
+ return `"${ast.value}"`;
48808
+ case "BOOLEAN":
48809
+ return ast.value ? "TRUE" : "FALSE";
48810
+ case "UNARY_OPERATION":
48811
+ if (ast.postfix) {
48812
+ const leftOperand = leftOperandNeedsParenthesis(ast)
48813
+ ? `(${astToFormula(ast.operand)})`
48814
+ : astToFormula(ast.operand);
48815
+ return leftOperand + ast.value;
48816
+ }
48817
+ const rightOperand = rightOperandNeedsParenthesis(ast)
48818
+ ? `(${astToFormula(ast.operand)})`
48819
+ : astToFormula(ast.operand);
48820
+ return ast.value + rightOperand;
48821
+ case "BIN_OPERATION":
48822
+ const leftOperation = leftOperandNeedsParenthesis(ast)
48823
+ ? `(${astToFormula(ast.left)})`
48824
+ : astToFormula(ast.left);
48825
+ const rightOperation = rightOperandNeedsParenthesis(ast)
48826
+ ? `(${astToFormula(ast.right)})`
48827
+ : astToFormula(ast.right);
48828
+ return leftOperation + ast.value + rightOperation;
48829
+ default:
48830
+ return ast.value;
48831
+ }
48832
+ }
48833
+ function leftOperandNeedsParenthesis(operationAST) {
48834
+ const mainOperator = operationAST.value;
48835
+ const leftOperation = "left" in operationAST ? operationAST.left : operationAST.operand;
48836
+ const leftOperator = leftOperation.value;
48837
+ return (leftOperation.type === "BIN_OPERATION" && OP_PRIORITY[leftOperator] < OP_PRIORITY[mainOperator]);
48838
+ }
48839
+ function rightOperandNeedsParenthesis(operationAST) {
48840
+ const mainOperator = operationAST.value;
48841
+ const rightOperation = "right" in operationAST ? operationAST.right : operationAST.operand;
48842
+ const rightPriority = OP_PRIORITY[rightOperation.value];
48843
+ const mainPriority = OP_PRIORITY[mainOperator];
48844
+ if (rightOperation.type !== "BIN_OPERATION") {
48845
+ return false;
48846
+ }
48847
+ if (rightPriority < mainPriority) {
48848
+ return true;
48849
+ }
48850
+ return rightPriority === mainPriority && !ASSOCIATIVE_OPERATORS.includes(mainOperator);
48851
+ }
48852
+
48656
48853
  const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
48657
48854
  class CellComposerStore extends AbstractComposerStore {
48658
48855
  canStopEdition() {
@@ -48796,7 +48993,10 @@ stores.inject(MyMetaStore, storeInstance);
48796
48993
  const locale = this.getters.getLocale();
48797
48994
  const cell = this.getters.getCell(position);
48798
48995
  if (cell?.isFormula) {
48799
- return localizeFormula(cell.content, locale);
48996
+ const prettifiedContent = cell.compiledFormula.isBadExpression
48997
+ ? cell.content
48998
+ : prettify(parseTokens(cell.compiledFormula.tokens), 80);
48999
+ return localizeFormula(prettifiedContent, locale);
48800
49000
  }
48801
49001
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
48802
49002
  if (spreader) {
@@ -61794,10 +61994,10 @@ stores.inject(MyMetaStore, storeInstance);
61794
61994
  */
61795
61995
  getFormulaString(sheetId, tokens, dependencies, useBoundedReference = false) {
61796
61996
  if (!dependencies.length) {
61797
- return concat(tokens.map((token) => token.value));
61997
+ return concat$1(tokens.map((token) => token.value));
61798
61998
  }
61799
61999
  let rangeIndex = 0;
61800
- return concat(tokens.map((token) => {
62000
+ return concat$1(tokens.map((token) => {
61801
62001
  if (token.type === "REFERENCE") {
61802
62002
  const range = dependencies[rangeIndex++];
61803
62003
  return this.getters.getRangeString(range, sheetId, { useBoundedReference });
@@ -62072,11 +62272,11 @@ stores.inject(MyMetaStore, storeInstance);
62072
62272
  };
62073
62273
  }
62074
62274
  get content() {
62075
- return concat(this.compiledFormula.tokens.map((token) => token.value));
62275
+ return concat$1(this.compiledFormula.tokens.map((token) => token.value));
62076
62276
  }
62077
62277
  get contentWithFixedReferences() {
62078
62278
  let rangeIndex = 0;
62079
- return concat(this.compiledFormula.tokens.map((token) => {
62279
+ return concat$1(this.compiledFormula.tokens.map((token) => {
62080
62280
  if (token.type === "REFERENCE") {
62081
62281
  const index = rangeIndex++;
62082
62282
  return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
@@ -81236,7 +81436,7 @@ stores.inject(MyMetaStore, storeInstance);
81236
81436
  }
81237
81437
  }
81238
81438
 
81239
- const COMPOSER_MAX_HEIGHT = 100;
81439
+ const COMPOSER_MAX_HEIGHT = 300;
81240
81440
  /* svg free of use from https://uxwing.com/formula-fx-icon/ */
81241
81441
  const FX_SVG = /*xml*/ `
81242
81442
  <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
@@ -87479,9 +87679,9 @@ stores.inject(MyMetaStore, storeInstance);
87479
87679
  exports.tokenize = tokenize;
87480
87680
 
87481
87681
 
87482
- __info__.version = "18.5.0-alpha.10";
87483
- __info__.date = "2025-08-21T06:42:10.830Z";
87484
- __info__.hash = "5800d1b";
87682
+ __info__.version = "18.5.0-alpha.11";
87683
+ __info__.date = "2025-08-26T10:14:05.357Z";
87684
+ __info__.hash = "b913e49";
87485
87685
 
87486
87686
 
87487
87687
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);