@odoo/o-spreadsheet 18.4.12 → 18.4.14

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.4.12
6
- * @date 2025-09-23T12:37:43.708Z
7
- * @hash 0c91305
5
+ * @version 18.4.14
6
+ * @date 2025-10-16T06:39:40.249Z
7
+ * @hash bc55c40
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -905,9 +905,7 @@
905
905
  return newArray;
906
906
  }
907
907
  function insertItemsAtIndex(array, items, index) {
908
- const newArray = [...array];
909
- newArray.splice(index, 0, ...items);
910
- return newArray;
908
+ return array.slice(0, index).concat(items).concat(array.slice(index));
911
909
  }
912
910
  function replaceItemAtIndex(array, newItem, index) {
913
911
  const newArray = [...array];
@@ -3195,7 +3193,17 @@
3195
3193
  return toMatrix(data).map((row) => {
3196
3194
  return row.map((cell) => {
3197
3195
  if (typeof cell.value !== "number") {
3198
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
3196
+ let message = "";
3197
+ if (typeof cell === "object") {
3198
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
3199
+ }
3200
+ else if (typeof cell === "string") {
3201
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
3202
+ }
3203
+ else if (typeof cell === "boolean") {
3204
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
3205
+ }
3206
+ throw new EvaluationError(message);
3199
3207
  }
3200
3208
  return cell.value;
3201
3209
  });
@@ -4314,7 +4322,7 @@
4314
4322
  * Replace in place tokens "mm" and "m" that denote minutes in date format with "MM" to avoid confusion with months.
4315
4323
  *
4316
4324
  * As per OpenXML specification, in date formats if a date token "m" or "mm" is followed by a date token "s" or
4317
- * preceded by a data token "h", then it's not a month but an minute.
4325
+ * preceded by a data token "h", then it's not a month but a minute.
4318
4326
  */
4319
4327
  function convertTokensToMinutesInDateFormat(tokens) {
4320
4328
  const dateParts = tokens.filter((token) => token.type === "DATE_PART");
@@ -4357,6 +4365,9 @@
4357
4365
  case "REPEATED_CHAR":
4358
4366
  format += "*" + token.value;
4359
4367
  break;
4368
+ case "DATE_PART":
4369
+ format += token.value === "MM" ? "mm" : token.value; // Convert "MM" back to "mm" for minutes
4370
+ break;
4360
4371
  default:
4361
4372
  format += token.value;
4362
4373
  }
@@ -9080,7 +9091,7 @@
9080
9091
  pasteCell(origin, target, clipboardOption) {
9081
9092
  const { sheetId, col, row } = target;
9082
9093
  const targetCell = this.getters.getEvaluatedCell(target);
9083
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9094
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9084
9095
  if (clipboardOption?.pasteOption === "asValue") {
9085
9096
  this.dispatch("UPDATE_CELL", {
9086
9097
  ...target,
@@ -13177,7 +13188,7 @@ stores.inject(MyMetaStore, storeInstance);
13177
13188
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13178
13189
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13179
13190
  }
13180
- return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "the first argument (known_data_y)")), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
13191
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13181
13192
  },
13182
13193
  };
13183
13194
  // -----------------------------------------------------------------------------
@@ -13250,7 +13261,7 @@ stores.inject(MyMetaStore, storeInstance);
13250
13261
  if (dataY.length === 0 || dataY[0].length === 0) {
13251
13262
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13252
13263
  }
13253
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13264
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13254
13265
  },
13255
13266
  isExported: true,
13256
13267
  };
@@ -13269,7 +13280,7 @@ stores.inject(MyMetaStore, storeInstance);
13269
13280
  if (dataY.length === 0 || dataY[0].length === 0) {
13270
13281
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13271
13282
  }
13272
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13283
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13273
13284
  for (let i = 0; i < coeffs.length; i++) {
13274
13285
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13275
13286
  }
@@ -13883,7 +13894,7 @@ stores.inject(MyMetaStore, storeInstance);
13883
13894
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13884
13895
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13885
13896
  }
13886
- return predictLinearValues(toNumberMatrix(knownDataY, "the first argument (known_data_y)"), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
13897
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
13887
13898
  },
13888
13899
  };
13889
13900
  // -----------------------------------------------------------------------------
@@ -21877,6 +21888,10 @@ stores.inject(MyMetaStore, storeInstance);
21877
21888
  }
21878
21889
  const ctx = chart.ctx;
21879
21890
  ctx.save();
21891
+ const { left, top, height, width } = chart.chartArea;
21892
+ ctx.beginPath();
21893
+ ctx.rect(left, top, width, height);
21894
+ ctx.clip();
21880
21895
  ctx.textAlign = "center";
21881
21896
  ctx.textBaseline = "middle";
21882
21897
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -22912,7 +22927,18 @@ stores.inject(MyMetaStore, storeInstance);
22912
22927
  this.chart.update();
22913
22928
  }
22914
22929
  hasChartDataChanged() {
22915
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
22930
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
22931
+ }
22932
+ getChartDataInRuntime(runtime) {
22933
+ const data = runtime.chartJsConfig.data;
22934
+ return {
22935
+ labels: data.labels,
22936
+ dataset: data.datasets.map((dataset) => ({
22937
+ data: dataset.data,
22938
+ label: dataset.label,
22939
+ tree: dataset.tree,
22940
+ })),
22941
+ };
22916
22942
  }
22917
22943
  enableAnimationInChartData(chartData) {
22918
22944
  return {
@@ -24534,6 +24560,7 @@ stores.inject(MyMetaStore, storeInstance);
24534
24560
  parser: luxonFormat,
24535
24561
  displayFormats,
24536
24562
  unit: timeUnit ?? false,
24563
+ tooltipFormat: luxonFormat,
24537
24564
  };
24538
24565
  }
24539
24566
  /**
@@ -25614,6 +25641,7 @@ stores.inject(MyMetaStore, storeInstance);
25614
25641
  };
25615
25642
  Object.assign(scales.x, axis);
25616
25643
  scales.x.ticks.maxTicksLimit = 15;
25644
+ delete scales?.x?.ticks?.callback;
25617
25645
  }
25618
25646
  else if (axisType === "linear") {
25619
25647
  scales.x.type = "linear";
@@ -30662,7 +30690,7 @@ stores.inject(MyMetaStore, storeInstance);
30662
30690
  }
30663
30691
  openContextMenu(ev) {
30664
30692
  this.menuState.isOpen = true;
30665
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
30693
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
30666
30694
  this.menuState.menuItems = getChartMenuActions(this.props.figureUI.id, () => { }, this.env);
30667
30695
  }
30668
30696
  get fullScreenMenuItem() {
@@ -37464,6 +37492,74 @@ stores.inject(MyMetaStore, storeInstance);
37464
37492
  return path2D;
37465
37493
  }
37466
37494
 
37495
+ /**
37496
+ * Get the relative path between two files
37497
+ *
37498
+ * Eg.:
37499
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
37500
+ */
37501
+ function getRelativePath(from, to) {
37502
+ const fromPathParts = from.split("/");
37503
+ const toPathParts = to.split("/");
37504
+ let relPath = "";
37505
+ let startIndex = 0;
37506
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
37507
+ if (fromPathParts[i] === toPathParts[i]) {
37508
+ startIndex++;
37509
+ }
37510
+ else {
37511
+ relPath += "../";
37512
+ }
37513
+ }
37514
+ relPath += toPathParts.slice(startIndex).join("/");
37515
+ return relPath;
37516
+ }
37517
+ /**
37518
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
37519
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
37520
+ *
37521
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
37522
+ */
37523
+ function arrayToObject(array, indexOffset = 0) {
37524
+ const obj = {};
37525
+ for (let i = 0; i < array.length; i++) {
37526
+ if (array[i]) {
37527
+ obj[i + indexOffset] = array[i];
37528
+ }
37529
+ }
37530
+ return obj;
37531
+ }
37532
+ /**
37533
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
37534
+ * Replace with characters understandable by JS
37535
+ */
37536
+ function fixXlsxUnicode(str) {
37537
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
37538
+ return String.fromCharCode(parseInt(code, 16));
37539
+ });
37540
+ }
37541
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
37542
+ function getSheetDataHeader(sheetData, dimension, index) {
37543
+ if (dimension === "COL") {
37544
+ if (!sheetData.cols[index]) {
37545
+ sheetData.cols[index] = {};
37546
+ }
37547
+ return sheetData.cols[index];
37548
+ }
37549
+ if (!sheetData.rows[index]) {
37550
+ sheetData.rows[index] = {};
37551
+ }
37552
+ return sheetData.rows[index];
37553
+ }
37554
+ /** Prefix the string by "=" if the string looks like a formula */
37555
+ function prefixFormulaWithEqual(formula) {
37556
+ if (formula[0] === "=") {
37557
+ return formula;
37558
+ }
37559
+ const tokens = tokenize(formula);
37560
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
37561
+ }
37562
+
37467
37563
  /**
37468
37564
  * Map of the different types of conversions warnings and their name in error messages
37469
37565
  */
@@ -37986,66 +38082,6 @@ stores.inject(MyMetaStore, storeInstance);
37986
38082
  */
37987
38083
  const DEFAULT_SYSTEM_COLOR = "FF000000";
37988
38084
 
37989
- /**
37990
- * Get the relative path between two files
37991
- *
37992
- * Eg.:
37993
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
37994
- */
37995
- function getRelativePath(from, to) {
37996
- const fromPathParts = from.split("/");
37997
- const toPathParts = to.split("/");
37998
- let relPath = "";
37999
- let startIndex = 0;
38000
- for (let i = 0; i < fromPathParts.length - 1; i++) {
38001
- if (fromPathParts[i] === toPathParts[i]) {
38002
- startIndex++;
38003
- }
38004
- else {
38005
- relPath += "../";
38006
- }
38007
- }
38008
- relPath += toPathParts.slice(startIndex).join("/");
38009
- return relPath;
38010
- }
38011
- /**
38012
- * Convert an array of element into an object where the objects keys were the elements position in the array.
38013
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
38014
- *
38015
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
38016
- */
38017
- function arrayToObject(array, indexOffset = 0) {
38018
- const obj = {};
38019
- for (let i = 0; i < array.length; i++) {
38020
- if (array[i]) {
38021
- obj[i + indexOffset] = array[i];
38022
- }
38023
- }
38024
- return obj;
38025
- }
38026
- /**
38027
- * In xlsx we can have string with unicode characters with the format _x00fa_.
38028
- * Replace with characters understandable by JS
38029
- */
38030
- function fixXlsxUnicode(str) {
38031
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
38032
- return String.fromCharCode(parseInt(code, 16));
38033
- });
38034
- }
38035
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
38036
- function getSheetDataHeader(sheetData, dimension, index) {
38037
- if (dimension === "COL") {
38038
- if (!sheetData.cols[index]) {
38039
- sheetData.cols[index] = {};
38040
- }
38041
- return sheetData.cols[index];
38042
- }
38043
- if (!sheetData.rows[index]) {
38044
- sheetData.rows[index] = {};
38045
- }
38046
- return sheetData.rows[index];
38047
- }
38048
-
38049
38085
  const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/m|\s|-|\/|\.|:)+$/i;
38050
38086
  /**
38051
38087
  * Convert excel format to o_spreadsheet format
@@ -38255,9 +38291,9 @@ stores.inject(MyMetaStore, storeInstance);
38255
38291
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
38256
38292
  continue;
38257
38293
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
38258
- values.push(prefixFormula(rule.formula[0]));
38294
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
38259
38295
  if (rule.formula.length === 2) {
38260
- values.push(prefixFormula(rule.formula[1]));
38296
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
38261
38297
  }
38262
38298
  break;
38263
38299
  }
@@ -38415,11 +38451,6 @@ stores.inject(MyMetaStore, storeInstance);
38415
38451
  ? ICON_SETS[iconSet].neutral
38416
38452
  : ICON_SETS[iconSet].good;
38417
38453
  }
38418
- /** Prefix the string by "=" if the string looks like a formula */
38419
- function prefixFormula(formula) {
38420
- const tokens = tokenize(formula);
38421
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
38422
- }
38423
38454
  // ---------------------------------------------------------------------------
38424
38455
  // Warnings
38425
38456
  // ---------------------------------------------------------------------------
@@ -38891,7 +38922,7 @@ stores.inject(MyMetaStore, storeInstance);
38891
38922
  dvRules.push(decimalRule);
38892
38923
  break;
38893
38924
  case "list":
38894
- const listRule = convertListrule(dvId++, dv);
38925
+ const listRule = convertListRule(dvId++, dv);
38895
38926
  dvRules.push(listRule);
38896
38927
  break;
38897
38928
  case "date":
@@ -38911,9 +38942,9 @@ stores.inject(MyMetaStore, storeInstance);
38911
38942
  return dvRules;
38912
38943
  }
38913
38944
  function convertDecimalRule(id, dv) {
38914
- const values = [dv.formula1.toString()];
38945
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
38915
38946
  if (dv.formula2) {
38916
- values.push(dv.formula2.toString());
38947
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
38917
38948
  }
38918
38949
  return {
38919
38950
  id: id.toString(),
@@ -38925,7 +38956,7 @@ stores.inject(MyMetaStore, storeInstance);
38925
38956
  },
38926
38957
  };
38927
38958
  }
38928
- function convertListrule(id, dv) {
38959
+ function convertListRule(id, dv) {
38929
38960
  const formula1 = dv.formula1.toString();
38930
38961
  const isRangeRule = rangeReference.test(formula1);
38931
38962
  return {
@@ -38941,9 +38972,9 @@ stores.inject(MyMetaStore, storeInstance);
38941
38972
  }
38942
38973
  function convertDateRule(id, dv) {
38943
38974
  let criterion;
38944
- const values = [dv.formula1.toString()];
38975
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
38945
38976
  if (dv.formula2) {
38946
- values.push(dv.formula2.toString());
38977
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
38947
38978
  criterion = {
38948
38979
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
38949
38980
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -38970,7 +39001,7 @@ stores.inject(MyMetaStore, storeInstance);
38970
39001
  isBlocking: dv.errorStyle !== "warning",
38971
39002
  criterion: {
38972
39003
  type: "customFormula",
38973
- values: [`=${dv.formula1.toString()}`],
39004
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
38974
39005
  },
38975
39006
  };
38976
39007
  }
@@ -52861,12 +52892,13 @@ stores.inject(MyMetaStore, storeInstance);
52861
52892
  onCloseSidePanel: { type: Function, optional: true },
52862
52893
  };
52863
52894
  state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
52895
+ editingSheetId;
52864
52896
  setup() {
52897
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
52865
52898
  if (this.props.rule) {
52866
- const sheetId = this.env.model.getters.getActiveSheetId();
52867
52899
  this.state.rule = {
52868
52900
  ...this.props.rule,
52869
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
52901
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
52870
52902
  };
52871
52903
  this.state.rule.criterion.type = this.props.rule.criterion.type;
52872
52904
  }
@@ -52900,7 +52932,6 @@ stores.inject(MyMetaStore, storeInstance);
52900
52932
  const locale = this.env.model.getters.getLocale();
52901
52933
  const criterion = rule.criterion;
52902
52934
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
52903
- const sheetId = this.env.model.getters.getActiveSheetId();
52904
52935
  const values = criterion.values
52905
52936
  .slice(0, criterionEvaluator.numberOfValues(criterion))
52906
52937
  .map((value) => value?.trim())
@@ -52908,8 +52939,8 @@ stores.inject(MyMetaStore, storeInstance);
52908
52939
  .map((value) => canonicalizeContent(value, locale));
52909
52940
  rule.criterion = { ...criterion, values };
52910
52941
  return {
52911
- sheetId,
52912
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
52942
+ sheetId: this.editingSheetId,
52943
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
52913
52944
  rule,
52914
52945
  };
52915
52946
  }
@@ -53436,6 +53467,7 @@ stores.inject(MyMetaStore, storeInstance);
53436
53467
  .o-button {
53437
53468
  height: 19px;
53438
53469
  width: 19px;
53470
+ box-sizing: content-box;
53439
53471
  .o-icon {
53440
53472
  height: 14px;
53441
53473
  width: 14px;
@@ -54245,7 +54277,7 @@ stores.inject(MyMetaStore, storeInstance);
54245
54277
  return undefined;
54246
54278
  }
54247
54279
  get isCalculatedMeasureInvalid() {
54248
- return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
54280
+ return compile(this.props.measure.computedBy?.formula ?? "").isBadExpression;
54249
54281
  }
54250
54282
  }
54251
54283
 
@@ -61257,11 +61289,11 @@ stores.inject(MyMetaStore, storeInstance);
61257
61289
  break;
61258
61290
  }
61259
61291
  case "ADD_COLUMNS_ROWS": {
61260
- const sizes = [...this.sizes[cmd.sheetId][cmd.dimension]];
61292
+ const sizes = this.sizes[cmd.sheetId][cmd.dimension];
61261
61293
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
61262
61294
  const baseSize = sizes[cmd.base];
61263
- sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
61264
- this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
61295
+ const newSizes = insertItemsAtIndex(sizes, Array(cmd.quantity).fill(baseSize), addIndex);
61296
+ this.history.update("sizes", cmd.sheetId, cmd.dimension, newSizes);
61265
61297
  break;
61266
61298
  }
61267
61299
  case "RESIZE_COLUMNS_ROWS":
@@ -61412,9 +61444,8 @@ stores.inject(MyMetaStore, storeInstance);
61412
61444
  break;
61413
61445
  }
61414
61446
  case "ADD_COLUMNS_ROWS": {
61415
- const hiddenHeaders = [...this.hiddenHeaders[cmd.sheetId][cmd.dimension]];
61416
61447
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
61417
- hiddenHeaders.splice(addIndex, 0, ...Array(cmd.quantity).fill(false));
61448
+ const hiddenHeaders = insertItemsAtIndex([...this.hiddenHeaders[cmd.sheetId][cmd.dimension]], Array(cmd.quantity).fill(false), addIndex);
61418
61449
  this.history.update("hiddenHeaders", cmd.sheetId, cmd.dimension, hiddenHeaders);
61419
61450
  break;
61420
61451
  }
@@ -65449,12 +65480,12 @@ stores.inject(MyMetaStore, storeInstance);
65449
65480
  this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
65450
65481
  }
65451
65482
  rtreeItemComparer(left, right) {
65452
- return (left.data === right.data &&
65453
- left.boundingBox.sheetId === right.boundingBox.sheetId &&
65483
+ return (left.boundingBox.sheetId === right.boundingBox.sheetId &&
65454
65484
  left.boundingBox?.zone.left === right.boundingBox.zone.left &&
65455
65485
  left.boundingBox?.zone.top === right.boundingBox.zone.top &&
65456
65486
  left.boundingBox?.zone.right === right.boundingBox.zone.right &&
65457
- left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
65487
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom &&
65488
+ deepEquals(left.data, right.data));
65458
65489
  }
65459
65490
  }
65460
65491
  /**
@@ -65527,7 +65558,7 @@ stores.inject(MyMetaStore, storeInstance);
65527
65558
  * in the correct order they should be evaluated.
65528
65559
  * This is called a topological ordering (excluding cycles)
65529
65560
  */
65530
- getCellsDependingOn(ranges) {
65561
+ getCellsDependingOn(ranges, ignore) {
65531
65562
  const visited = this.createEmptyPositionSet();
65532
65563
  const queue = Array.from(ranges).reverse();
65533
65564
  while (queue.length > 0) {
@@ -65542,7 +65573,7 @@ stores.inject(MyMetaStore, storeInstance);
65542
65573
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
65543
65574
  const nextInQueue = {};
65544
65575
  for (const position of impactedPositions) {
65545
- if (!visited.has(position)) {
65576
+ if (!visited.has(position) && !ignore.has(position)) {
65546
65577
  if (!nextInQueue[position.sheetId]) {
65547
65578
  nextInQueue[position.sheetId] = [];
65548
65579
  }
@@ -66099,7 +66130,7 @@ stores.inject(MyMetaStore, storeInstance);
66099
66130
  }
66100
66131
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
66101
66132
  // the result matrix is split in 2 zones to exclude the array formula position
66102
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
66133
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
66103
66134
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
66104
66135
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
66105
66136
  }
@@ -66217,7 +66248,7 @@ stores.inject(MyMetaStore, storeInstance);
66217
66248
  for (const sheetId in zonesBySheetIds) {
66218
66249
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
66219
66250
  }
66220
- return this.formulaDependencies().getCellsDependingOn(ranges);
66251
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
66221
66252
  }
66222
66253
  }
66223
66254
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -67690,7 +67721,8 @@ stores.inject(MyMetaStore, storeInstance);
67690
67721
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
67691
67722
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
67692
67723
  if (!parentSpreadingCell) {
67693
- return false;
67724
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
67725
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
67694
67726
  }
67695
67727
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
67696
67728
  return true;
@@ -78591,6 +78623,7 @@ stores.inject(MyMetaStore, storeInstance);
78591
78623
  static components = { Menu };
78592
78624
  rootItems = topbarMenuRegistry.getMenuItems();
78593
78625
  menuRef = owl.useRef("menu");
78626
+ containerRef = owl.useRef("container");
78594
78627
  state = owl.useState({
78595
78628
  menuItems: this.rootItems,
78596
78629
  title: _t("Menu Bar"),
@@ -78598,6 +78631,7 @@ stores.inject(MyMetaStore, storeInstance);
78598
78631
  });
78599
78632
  setup() {
78600
78633
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
78634
+ owl.onMounted(this.updateShadows);
78601
78635
  }
78602
78636
  onExternalClick(ev) {
78603
78637
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -78610,6 +78644,7 @@ stores.inject(MyMetaStore, storeInstance);
78610
78644
  this.state.parentState = { ...this.state };
78611
78645
  this.state.menuItems = children;
78612
78646
  this.state.title = menu.name(this.env);
78647
+ this.containerRef.el?.scrollTo({ top: 0 });
78613
78648
  }
78614
78649
  else {
78615
78650
  this.state.menuItems = this.rootItems;
@@ -78631,6 +78666,19 @@ stores.inject(MyMetaStore, storeInstance);
78631
78666
  height: `${this.props.height}px`,
78632
78667
  });
78633
78668
  }
78669
+ updateShadows() {
78670
+ if (!this.containerRef.el) {
78671
+ return;
78672
+ }
78673
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
78674
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
78675
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
78676
+ this.containerRef.el.classList.add("scroll-bottom");
78677
+ }
78678
+ if (this.containerRef.el.scrollTop > 0) {
78679
+ this.containerRef.el.classList.add("scroll-top");
78680
+ }
78681
+ }
78634
78682
  onClickBack() {
78635
78683
  if (!this.state.parentState) {
78636
78684
  this.props.onClose();
@@ -78639,6 +78687,7 @@ stores.inject(MyMetaStore, storeInstance);
78639
78687
  this.state.menuItems = this.state.parentState.menuItems;
78640
78688
  this.state.title = this.state.parentState.title;
78641
78689
  this.state.parentState = this.state.parentState.parentState;
78690
+ this.containerRef.el?.scrollTo({ top: 0 });
78642
78691
  }
78643
78692
  get backTitle() {
78644
78693
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -78694,6 +78743,11 @@ stores.inject(MyMetaStore, storeInstance);
78694
78743
  ? this.composerFocusStore.focusMode
78695
78744
  : "inactive";
78696
78745
  }
78746
+ get showFxIcon() {
78747
+ return (this.focus === "inactive" &&
78748
+ !this.composerStore.currentContent &&
78749
+ !this.composerStore.placeholder);
78750
+ }
78697
78751
  get rect() {
78698
78752
  return this.composerRef.el
78699
78753
  ? getBoundingRectAsPOJO(this.composerRef.el)
@@ -78718,6 +78772,7 @@ stores.inject(MyMetaStore, storeInstance);
78718
78772
  "max-height": `130px`,
78719
78773
  }),
78720
78774
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
78775
+ placeholder: this.composerStore.placeholder,
78721
78776
  };
78722
78777
  }
78723
78778
  get symbols() {
@@ -78736,12 +78791,6 @@ stores.inject(MyMetaStore, storeInstance);
78736
78791
  }
78737
78792
 
78738
78793
  const COMPOSER_MAX_HEIGHT = 100;
78739
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
78740
- const FX_SVG = /*xml*/ `
78741
- <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
78742
- <path d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='#BDBDBD'/>
78743
- </svg>
78744
- `;
78745
78794
  css /* scss */ `
78746
78795
  .o-topbar-composer-container {
78747
78796
  height: ${DESKTOP_TOPBAR_TOOLBAR_HEIGHT}px;
@@ -78753,14 +78802,6 @@ stores.inject(MyMetaStore, storeInstance);
78753
78802
  margin-bottom: -1px;
78754
78803
  border: 1px solid;
78755
78804
  font-family: ${DEFAULT_FONT};
78756
-
78757
- /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
78758
- .o-composer:empty:not(:focus):not(.active)::before,
78759
- &.o-topbar-composer-readonly .o-composer:empty::before {
78760
- content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
78761
- position: relative;
78762
- top: 20%;
78763
- }
78764
78805
  }
78765
78806
 
78766
78807
  .user-select-text {
@@ -78793,6 +78834,11 @@ stores.inject(MyMetaStore, storeInstance);
78793
78834
  ? this.composerFocusStore.focusMode
78794
78835
  : "inactive";
78795
78836
  }
78837
+ get showFxIcon() {
78838
+ return (this.focus === "inactive" &&
78839
+ !this.composerStore.currentContent &&
78840
+ !this.composerStore.placeholder);
78841
+ }
78796
78842
  get composerStyle() {
78797
78843
  const style = {
78798
78844
  padding: "5px 0px 5px 8px",
@@ -84858,9 +84904,9 @@ stores.inject(MyMetaStore, storeInstance);
84858
84904
  exports.tokenize = tokenize;
84859
84905
 
84860
84906
 
84861
- __info__.version = "18.4.12";
84862
- __info__.date = "2025-09-23T12:37:43.708Z";
84863
- __info__.hash = "0c91305";
84907
+ __info__.version = "18.4.14";
84908
+ __info__.date = "2025-10-16T06:39:40.249Z";
84909
+ __info__.hash = "bc55c40";
84864
84910
 
84865
84911
 
84866
84912
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);