@odoo/o-spreadsheet 18.4.13 → 18.4.16

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.13
6
- * @date 2025-10-07T10:00:55.413Z
7
- * @hash d4df70e
5
+ * @version 18.4.16
6
+ * @date 2025-11-03T12:31:57.153Z
7
+ * @hash 1ba569f
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -3192,7 +3192,17 @@ function toNumberMatrix(data, argName) {
3192
3192
  return toMatrix(data).map((row) => {
3193
3193
  return row.map((cell) => {
3194
3194
  if (typeof cell.value !== "number") {
3195
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
3195
+ let message = "";
3196
+ if (typeof cell === "object") {
3197
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
3198
+ }
3199
+ else if (typeof cell === "string") {
3200
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
3201
+ }
3202
+ else if (typeof cell === "boolean") {
3203
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
3204
+ }
3205
+ throw new EvaluationError(message);
3196
3206
  }
3197
3207
  return cell.value;
3198
3208
  });
@@ -9080,7 +9090,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9080
9090
  pasteCell(origin, target, clipboardOption) {
9081
9091
  const { sheetId, col, row } = target;
9082
9092
  const targetCell = this.getters.getEvaluatedCell(target);
9083
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9093
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9084
9094
  if (clipboardOption?.pasteOption === "asValue") {
9085
9095
  this.dispatch("UPDATE_CELL", {
9086
9096
  ...target,
@@ -13177,7 +13187,7 @@ const GROWTH = {
13177
13187
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13178
13188
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13179
13189
  }
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)));
13190
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13181
13191
  },
13182
13192
  };
13183
13193
  // -----------------------------------------------------------------------------
@@ -13250,7 +13260,7 @@ const LINEST = {
13250
13260
  if (dataY.length === 0 || dataY[0].length === 0) {
13251
13261
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13252
13262
  }
13253
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13263
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13254
13264
  },
13255
13265
  isExported: true,
13256
13266
  };
@@ -13269,7 +13279,7 @@ const LOGEST = {
13269
13279
  if (dataY.length === 0 || dataY[0].length === 0) {
13270
13280
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13271
13281
  }
13272
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13282
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13273
13283
  for (let i = 0; i < coeffs.length; i++) {
13274
13284
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13275
13285
  }
@@ -13883,7 +13893,7 @@ const TREND = {
13883
13893
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13884
13894
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13885
13895
  }
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));
13896
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
13887
13897
  },
13888
13898
  };
13889
13899
  // -----------------------------------------------------------------------------
@@ -21877,6 +21887,10 @@ const chartShowValuesPlugin = {
21877
21887
  }
21878
21888
  const ctx = chart.ctx;
21879
21889
  ctx.save();
21890
+ const { left, top, height, width } = chart.chartArea;
21891
+ ctx.beginPath();
21892
+ ctx.rect(left, top, width, height);
21893
+ ctx.clip();
21880
21894
  ctx.textAlign = "center";
21881
21895
  ctx.textBaseline = "middle";
21882
21896
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -22912,7 +22926,18 @@ class ChartJsComponent extends Component {
22912
22926
  this.chart.update();
22913
22927
  }
22914
22928
  hasChartDataChanged() {
22915
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
22929
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
22930
+ }
22931
+ getChartDataInRuntime(runtime) {
22932
+ const data = runtime.chartJsConfig.data;
22933
+ return {
22934
+ labels: data.labels,
22935
+ dataset: data.datasets.map((dataset) => ({
22936
+ data: dataset.data,
22937
+ label: dataset.label,
22938
+ tree: dataset.tree,
22939
+ })),
22940
+ };
22916
22941
  }
22917
22942
  enableAnimationInChartData(chartData) {
22918
22943
  return {
@@ -24534,6 +24559,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
24534
24559
  parser: luxonFormat,
24535
24560
  displayFormats,
24536
24561
  unit: timeUnit ?? false,
24562
+ tooltipFormat: luxonFormat,
24537
24563
  };
24538
24564
  }
24539
24565
  /**
@@ -25614,6 +25640,7 @@ function getLineChartScales(definition, args) {
25614
25640
  };
25615
25641
  Object.assign(scales.x, axis);
25616
25642
  scales.x.ticks.maxTicksLimit = 15;
25643
+ delete scales?.x?.ticks?.callback;
25617
25644
  }
25618
25645
  else if (axisType === "linear") {
25619
25646
  scales.x.type = "linear";
@@ -30662,7 +30689,7 @@ class ChartDashboardMenu extends Component {
30662
30689
  }
30663
30690
  openContextMenu(ev) {
30664
30691
  this.menuState.isOpen = true;
30665
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
30692
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
30666
30693
  this.menuState.menuItems = getChartMenuActions(this.props.figureUI.id, () => { }, this.env);
30667
30694
  }
30668
30695
  get fullScreenMenuItem() {
@@ -32206,7 +32233,7 @@ class ContentEditableHelper {
32206
32233
  else {
32207
32234
  text += NEWLINE;
32208
32235
  }
32209
- emptyParagraph = ["<br>", "<span><br></span>"].includes(current.value.innerHTML);
32236
+ emptyParagraph = isEmptyParagraph(current.value);
32210
32237
  continue;
32211
32238
  }
32212
32239
  if (!current.value.hasChildNodes()) {
@@ -32227,6 +32254,21 @@ function compareContentToSpanElement(content, node) {
32227
32254
  const sameContent = node.innerText === content.value;
32228
32255
  return sameColor && sameClass && sameContent;
32229
32256
  }
32257
+ const doc = new DOMParser();
32258
+ const brNode = doc.parseFromString("<br>", "text/html").body.firstChild;
32259
+ const spanBrNode = doc.parseFromString("<span><br></span>", "text/html").body.firstChild;
32260
+ function isEmptyParagraph(node) {
32261
+ if (node.childNodes.length > 1)
32262
+ return false;
32263
+ const node2 = node.firstChild?.cloneNode(true);
32264
+ if (!node2)
32265
+ return true;
32266
+ if (!(node2 instanceof Element))
32267
+ return false;
32268
+ node2.removeAttribute("class");
32269
+ node2.removeAttribute("style");
32270
+ return node2.isEqualNode(brNode) || node2.isEqualNode(spanBrNode) || false;
32271
+ }
32230
32272
 
32231
32273
  // -----------------------------------------------------------------------------
32232
32274
  // Formula Assistant component
@@ -37464,6 +37506,74 @@ function getPath2D(svgPath) {
37464
37506
  return path2D;
37465
37507
  }
37466
37508
 
37509
+ /**
37510
+ * Get the relative path between two files
37511
+ *
37512
+ * Eg.:
37513
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
37514
+ */
37515
+ function getRelativePath(from, to) {
37516
+ const fromPathParts = from.split("/");
37517
+ const toPathParts = to.split("/");
37518
+ let relPath = "";
37519
+ let startIndex = 0;
37520
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
37521
+ if (fromPathParts[i] === toPathParts[i]) {
37522
+ startIndex++;
37523
+ }
37524
+ else {
37525
+ relPath += "../";
37526
+ }
37527
+ }
37528
+ relPath += toPathParts.slice(startIndex).join("/");
37529
+ return relPath;
37530
+ }
37531
+ /**
37532
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
37533
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
37534
+ *
37535
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
37536
+ */
37537
+ function arrayToObject(array, indexOffset = 0) {
37538
+ const obj = {};
37539
+ for (let i = 0; i < array.length; i++) {
37540
+ if (array[i]) {
37541
+ obj[i + indexOffset] = array[i];
37542
+ }
37543
+ }
37544
+ return obj;
37545
+ }
37546
+ /**
37547
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
37548
+ * Replace with characters understandable by JS
37549
+ */
37550
+ function fixXlsxUnicode(str) {
37551
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
37552
+ return String.fromCharCode(parseInt(code, 16));
37553
+ });
37554
+ }
37555
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
37556
+ function getSheetDataHeader(sheetData, dimension, index) {
37557
+ if (dimension === "COL") {
37558
+ if (!sheetData.cols[index]) {
37559
+ sheetData.cols[index] = {};
37560
+ }
37561
+ return sheetData.cols[index];
37562
+ }
37563
+ if (!sheetData.rows[index]) {
37564
+ sheetData.rows[index] = {};
37565
+ }
37566
+ return sheetData.rows[index];
37567
+ }
37568
+ /** Prefix the string by "=" if the string looks like a formula */
37569
+ function prefixFormulaWithEqual(formula) {
37570
+ if (formula[0] === "=") {
37571
+ return formula;
37572
+ }
37573
+ const tokens = tokenize(formula);
37574
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
37575
+ }
37576
+
37467
37577
  /**
37468
37578
  * Map of the different types of conversions warnings and their name in error messages
37469
37579
  */
@@ -37986,66 +38096,6 @@ function hexaToInt(hex) {
37986
38096
  */
37987
38097
  const DEFAULT_SYSTEM_COLOR = "FF000000";
37988
38098
 
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
38099
  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
38100
  /**
38051
38101
  * Convert excel format to o_spreadsheet format
@@ -38255,9 +38305,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
38255
38305
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
38256
38306
  continue;
38257
38307
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
38258
- values.push(prefixFormula(rule.formula[0]));
38308
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
38259
38309
  if (rule.formula.length === 2) {
38260
- values.push(prefixFormula(rule.formula[1]));
38310
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
38261
38311
  }
38262
38312
  break;
38263
38313
  }
@@ -38415,11 +38465,6 @@ function convertIcons(xlsxIconSet, index) {
38415
38465
  ? ICON_SETS[iconSet].neutral
38416
38466
  : ICON_SETS[iconSet].good;
38417
38467
  }
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
38468
  // ---------------------------------------------------------------------------
38424
38469
  // Warnings
38425
38470
  // ---------------------------------------------------------------------------
@@ -38891,7 +38936,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
38891
38936
  dvRules.push(decimalRule);
38892
38937
  break;
38893
38938
  case "list":
38894
- const listRule = convertListrule(dvId++, dv);
38939
+ const listRule = convertListRule(dvId++, dv);
38895
38940
  dvRules.push(listRule);
38896
38941
  break;
38897
38942
  case "date":
@@ -38911,9 +38956,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
38911
38956
  return dvRules;
38912
38957
  }
38913
38958
  function convertDecimalRule(id, dv) {
38914
- const values = [dv.formula1.toString()];
38959
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
38915
38960
  if (dv.formula2) {
38916
- values.push(dv.formula2.toString());
38961
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
38917
38962
  }
38918
38963
  return {
38919
38964
  id: id.toString(),
@@ -38925,7 +38970,7 @@ function convertDecimalRule(id, dv) {
38925
38970
  },
38926
38971
  };
38927
38972
  }
38928
- function convertListrule(id, dv) {
38973
+ function convertListRule(id, dv) {
38929
38974
  const formula1 = dv.formula1.toString();
38930
38975
  const isRangeRule = rangeReference.test(formula1);
38931
38976
  return {
@@ -38941,9 +38986,9 @@ function convertListrule(id, dv) {
38941
38986
  }
38942
38987
  function convertDateRule(id, dv) {
38943
38988
  let criterion;
38944
- const values = [dv.formula1.toString()];
38989
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
38945
38990
  if (dv.formula2) {
38946
- values.push(dv.formula2.toString());
38991
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
38947
38992
  criterion = {
38948
38993
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
38949
38994
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -38970,7 +39015,7 @@ function convertCustomRule(id, dv) {
38970
39015
  isBlocking: dv.errorStyle !== "warning",
38971
39016
  criterion: {
38972
39017
  type: "customFormula",
38973
- values: [`=${dv.formula1.toString()}`],
39018
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
38974
39019
  },
38975
39020
  };
38976
39021
  }
@@ -41783,8 +41828,10 @@ const LEGACY_VERSION_MAPPING = {
41783
41828
  17: "17.4",
41784
41829
  16: "17.3",
41785
41830
  15: "17.2",
41831
+ "14.5": "16.4.1",
41786
41832
  14: "16.4",
41787
41833
  13: "16.3",
41834
+ "12.5": "15.4.1",
41788
41835
  12: "15.4",
41789
41836
  // not accurate starting at this point
41790
41837
  11: "0.10",
@@ -52861,12 +52908,13 @@ class DataValidationEditor extends Component {
52861
52908
  onCloseSidePanel: { type: Function, optional: true },
52862
52909
  };
52863
52910
  state = useState({ rule: this.defaultDataValidationRule, errors: [] });
52911
+ editingSheetId;
52864
52912
  setup() {
52913
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
52865
52914
  if (this.props.rule) {
52866
- const sheetId = this.env.model.getters.getActiveSheetId();
52867
52915
  this.state.rule = {
52868
52916
  ...this.props.rule,
52869
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
52917
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
52870
52918
  };
52871
52919
  this.state.rule.criterion.type = this.props.rule.criterion.type;
52872
52920
  }
@@ -52900,7 +52948,6 @@ class DataValidationEditor extends Component {
52900
52948
  const locale = this.env.model.getters.getLocale();
52901
52949
  const criterion = rule.criterion;
52902
52950
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
52903
- const sheetId = this.env.model.getters.getActiveSheetId();
52904
52951
  const values = criterion.values
52905
52952
  .slice(0, criterionEvaluator.numberOfValues(criterion))
52906
52953
  .map((value) => value?.trim())
@@ -52908,8 +52955,8 @@ class DataValidationEditor extends Component {
52908
52955
  .map((value) => canonicalizeContent(value, locale));
52909
52956
  rule.criterion = { ...criterion, values };
52910
52957
  return {
52911
- sheetId,
52912
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
52958
+ sheetId: this.editingSheetId,
52959
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
52913
52960
  rule,
52914
52961
  };
52915
52962
  }
@@ -53436,6 +53483,7 @@ css /* scss */ `
53436
53483
  .o-button {
53437
53484
  height: 19px;
53438
53485
  width: 19px;
53486
+ box-sizing: content-box;
53439
53487
  .o-icon {
53440
53488
  height: 14px;
53441
53489
  width: 14px;
@@ -65526,7 +65574,7 @@ class FormulaDependencyGraph {
65526
65574
  * in the correct order they should be evaluated.
65527
65575
  * This is called a topological ordering (excluding cycles)
65528
65576
  */
65529
- getCellsDependingOn(ranges) {
65577
+ getCellsDependingOn(ranges, ignore) {
65530
65578
  const visited = this.createEmptyPositionSet();
65531
65579
  const queue = Array.from(ranges).reverse();
65532
65580
  while (queue.length > 0) {
@@ -65541,7 +65589,7 @@ class FormulaDependencyGraph {
65541
65589
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
65542
65590
  const nextInQueue = {};
65543
65591
  for (const position of impactedPositions) {
65544
- if (!visited.has(position)) {
65592
+ if (!visited.has(position) && !ignore.has(position)) {
65545
65593
  if (!nextInQueue[position.sheetId]) {
65546
65594
  nextInQueue[position.sheetId] = [];
65547
65595
  }
@@ -66098,7 +66146,7 @@ class Evaluator {
66098
66146
  }
66099
66147
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
66100
66148
  // the result matrix is split in 2 zones to exclude the array formula position
66101
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
66149
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
66102
66150
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
66103
66151
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
66104
66152
  }
@@ -66216,7 +66264,7 @@ class Evaluator {
66216
66264
  for (const sheetId in zonesBySheetIds) {
66217
66265
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
66218
66266
  }
66219
- return this.formulaDependencies().getCellsDependingOn(ranges);
66267
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
66220
66268
  }
66221
66269
  }
66222
66270
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -67689,7 +67737,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
67689
67737
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
67690
67738
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
67691
67739
  if (!parentSpreadingCell) {
67692
- return false;
67740
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
67741
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
67693
67742
  }
67694
67743
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
67695
67744
  return true;
@@ -78590,6 +78639,7 @@ class RibbonMenu extends Component {
78590
78639
  static components = { Menu };
78591
78640
  rootItems = topbarMenuRegistry.getMenuItems();
78592
78641
  menuRef = useRef("menu");
78642
+ containerRef = useRef("container");
78593
78643
  state = useState({
78594
78644
  menuItems: this.rootItems,
78595
78645
  title: _t("Menu Bar"),
@@ -78597,6 +78647,7 @@ class RibbonMenu extends Component {
78597
78647
  });
78598
78648
  setup() {
78599
78649
  useExternalListener(window, "click", this.onExternalClick, { capture: true });
78650
+ onMounted(this.updateShadows);
78600
78651
  }
78601
78652
  onExternalClick(ev) {
78602
78653
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -78609,6 +78660,7 @@ class RibbonMenu extends Component {
78609
78660
  this.state.parentState = { ...this.state };
78610
78661
  this.state.menuItems = children;
78611
78662
  this.state.title = menu.name(this.env);
78663
+ this.containerRef.el?.scrollTo({ top: 0 });
78612
78664
  }
78613
78665
  else {
78614
78666
  this.state.menuItems = this.rootItems;
@@ -78630,6 +78682,19 @@ class RibbonMenu extends Component {
78630
78682
  height: `${this.props.height}px`,
78631
78683
  });
78632
78684
  }
78685
+ updateShadows() {
78686
+ if (!this.containerRef.el) {
78687
+ return;
78688
+ }
78689
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
78690
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
78691
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
78692
+ this.containerRef.el.classList.add("scroll-bottom");
78693
+ }
78694
+ if (this.containerRef.el.scrollTop > 0) {
78695
+ this.containerRef.el.classList.add("scroll-top");
78696
+ }
78697
+ }
78633
78698
  onClickBack() {
78634
78699
  if (!this.state.parentState) {
78635
78700
  this.props.onClose();
@@ -78638,6 +78703,7 @@ class RibbonMenu extends Component {
78638
78703
  this.state.menuItems = this.state.parentState.menuItems;
78639
78704
  this.state.title = this.state.parentState.title;
78640
78705
  this.state.parentState = this.state.parentState.parentState;
78706
+ this.containerRef.el?.scrollTo({ top: 0 });
78641
78707
  }
78642
78708
  get backTitle() {
78643
78709
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -78694,7 +78760,9 @@ class SmallBottomBar extends Component {
78694
78760
  : "inactive";
78695
78761
  }
78696
78762
  get showFxIcon() {
78697
- return this.focus === "inactive" && !this.composerStore.currentContent;
78763
+ return (this.focus === "inactive" &&
78764
+ !this.composerStore.currentContent &&
78765
+ !this.composerStore.placeholder);
78698
78766
  }
78699
78767
  get rect() {
78700
78768
  return this.composerRef.el
@@ -78720,6 +78788,7 @@ class SmallBottomBar extends Component {
78720
78788
  "max-height": `130px`,
78721
78789
  }),
78722
78790
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
78791
+ placeholder: this.composerStore.placeholder,
78723
78792
  };
78724
78793
  }
78725
78794
  get symbols() {
@@ -78782,7 +78851,9 @@ class TopBarComposer extends Component {
78782
78851
  : "inactive";
78783
78852
  }
78784
78853
  get showFxIcon() {
78785
- return this.focus === "inactive" && !this.composerStore.currentContent;
78854
+ return (this.focus === "inactive" &&
78855
+ !this.composerStore.currentContent &&
78856
+ !this.composerStore.placeholder);
78786
78857
  }
78787
78858
  get composerStyle() {
78788
78859
  const style = {
@@ -84801,6 +84872,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
84801
84872
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
84802
84873
 
84803
84874
 
84804
- __info__.version = "18.4.13";
84805
- __info__.date = "2025-10-07T10:00:55.413Z";
84806
- __info__.hash = "d4df70e";
84875
+ __info__.version = "18.4.16";
84876
+ __info__.date = "2025-11-03T12:31:57.153Z";
84877
+ __info__.hash = "1ba569f";