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