@odoo/o-spreadsheet 19.0.5 → 19.0.6

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 19.0.5
6
- * @date 2025-10-07T10:04:06.400Z
7
- * @hash 86fc442
5
+ * @version 19.0.6
6
+ * @date 2025-10-16T06:39:36.282Z
7
+ * @hash 0d4315a
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';
@@ -4513,7 +4513,17 @@ function toNumberMatrix(data, argName) {
4513
4513
  return toMatrix(data).map((row) => {
4514
4514
  return row.map((cell) => {
4515
4515
  if (typeof cell.value !== "number") {
4516
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
4516
+ let message = "";
4517
+ if (typeof cell === "object") {
4518
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
4519
+ }
4520
+ else if (typeof cell === "string") {
4521
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
4522
+ }
4523
+ else if (typeof cell === "boolean") {
4524
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
4525
+ }
4526
+ throw new EvaluationError(message);
4517
4527
  }
4518
4528
  return cell.value;
4519
4529
  });
@@ -9528,7 +9538,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9528
9538
  pasteCell(origin, target, clipboardOption) {
9529
9539
  const { sheetId, col, row } = target;
9530
9540
  const targetCell = this.getters.getEvaluatedCell(target);
9531
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9541
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9532
9542
  if (clipboardOption?.pasteOption === "asValue") {
9533
9543
  this.dispatch("UPDATE_CELL", {
9534
9544
  ...target,
@@ -13901,7 +13911,7 @@ const GROWTH = {
13901
13911
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13902
13912
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13903
13913
  }
13904
- 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)));
13914
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13905
13915
  },
13906
13916
  };
13907
13917
  // -----------------------------------------------------------------------------
@@ -13974,7 +13984,7 @@ const LINEST = {
13974
13984
  if (dataY.length === 0 || dataY[0].length === 0) {
13975
13985
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13976
13986
  }
13977
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13987
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13978
13988
  },
13979
13989
  isExported: true,
13980
13990
  };
@@ -13993,7 +14003,7 @@ const LOGEST = {
13993
14003
  if (dataY.length === 0 || dataY[0].length === 0) {
13994
14004
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13995
14005
  }
13996
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
14006
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13997
14007
  for (let i = 0; i < coeffs.length; i++) {
13998
14008
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13999
14009
  }
@@ -14614,7 +14624,7 @@ const TREND = {
14614
14624
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
14615
14625
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
14616
14626
  }
14617
- 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));
14627
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14618
14628
  },
14619
14629
  };
14620
14630
  // -----------------------------------------------------------------------------
@@ -23135,6 +23145,10 @@ const chartShowValuesPlugin = {
23135
23145
  }
23136
23146
  const ctx = chart.ctx;
23137
23147
  ctx.save();
23148
+ const { left, top, height, width } = chart.chartArea;
23149
+ ctx.beginPath();
23150
+ ctx.rect(left, top, width, height);
23151
+ ctx.clip();
23138
23152
  ctx.textAlign = "center";
23139
23153
  ctx.textBaseline = "middle";
23140
23154
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -24178,7 +24192,7 @@ class ChartJsComponent extends Component {
24178
24192
  this.chart.update();
24179
24193
  }
24180
24194
  hasChartDataChanged() {
24181
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
24195
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
24182
24196
  }
24183
24197
  enableAnimationInChartData(chartData) {
24184
24198
  return {
@@ -24186,6 +24200,17 @@ class ChartJsComponent extends Component {
24186
24200
  options: { ...chartData.options, animation: { animateRotate: true } },
24187
24201
  };
24188
24202
  }
24203
+ getChartDataInRuntime(runtime) {
24204
+ const data = runtime.chartJsConfig.data;
24205
+ return {
24206
+ labels: data.labels,
24207
+ dataset: data.datasets.map((dataset) => ({
24208
+ data: dataset.data,
24209
+ label: dataset.label,
24210
+ tree: dataset.tree,
24211
+ })),
24212
+ };
24213
+ }
24189
24214
  get animationChartId() {
24190
24215
  return this.props.isFullScreen ? this.props.chartId + "-fullscreen" : this.props.chartId;
24191
24216
  }
@@ -25609,6 +25634,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
25609
25634
  parser: luxonFormat,
25610
25635
  displayFormats,
25611
25636
  unit: timeUnit ?? false,
25637
+ tooltipFormat: luxonFormat,
25612
25638
  };
25613
25639
  }
25614
25640
  /**
@@ -26677,6 +26703,7 @@ function getLineChartScales(definition, args) {
26677
26703
  };
26678
26704
  Object.assign(scales.x, axis);
26679
26705
  scales.x.ticks.maxTicksLimit = 15;
26706
+ delete scales?.x?.ticks?.callback;
26680
26707
  }
26681
26708
  else if (axisType === "linear") {
26682
26709
  scales.x.type = "linear";
@@ -32362,7 +32389,7 @@ class ChartDashboardMenu extends Component {
32362
32389
  }
32363
32390
  openContextMenu(ev) {
32364
32391
  this.menuState.isOpen = true;
32365
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
32392
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32366
32393
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32367
32394
  this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32368
32395
  }
@@ -32394,6 +32421,7 @@ class CarouselFigure extends Component {
32394
32421
  onFigureDeleted: Function,
32395
32422
  editFigureStyle: { type: Function, optional: true },
32396
32423
  isFullScreen: { type: Boolean, optional: true },
32424
+ openContextMenu: { type: Function, optional: true },
32397
32425
  };
32398
32426
  static components = { ChartDashboardMenu, MenuPopover };
32399
32427
  carouselTabsRef = useRef("carouselTabs");
@@ -32527,6 +32555,12 @@ class CarouselFigure extends Component {
32527
32555
  get visibleCarouselItems() {
32528
32556
  return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32529
32557
  }
32558
+ openContextMenu(event) {
32559
+ const target = event.currentTarget;
32560
+ if (target) {
32561
+ this.props.openContextMenu?.(getBoundingRectAsPOJO(target));
32562
+ }
32563
+ }
32530
32564
  }
32531
32565
 
32532
32566
  class ChartFigure extends Component {
@@ -32536,6 +32570,7 @@ class ChartFigure extends Component {
32536
32570
  onFigureDeleted: Function,
32537
32571
  editFigureStyle: { type: Function, optional: true },
32538
32572
  isFullScreen: { type: Boolean, optional: true },
32573
+ openContextMenu: { type: Function, optional: true },
32539
32574
  };
32540
32575
  static components = { ChartDashboardMenu };
32541
32576
  onDoubleClick() {
@@ -32568,6 +32603,7 @@ class ImageFigure extends Component {
32568
32603
  figureUI: Object,
32569
32604
  onFigureDeleted: Function,
32570
32605
  editFigureStyle: { type: Function, optional: true },
32606
+ openContextMenu: { type: Function, optional: true },
32571
32607
  };
32572
32608
  static components = {};
32573
32609
  // ---------------------------------------------------------------------------
@@ -34619,8 +34655,11 @@ class Composer extends Component {
34619
34655
  }
34620
34656
  const newSelection = this.contentHelper.getCurrentSelection();
34621
34657
  this.props.composerStore.stopComposerRangeSelection();
34622
- this.props.onComposerContentFocused();
34623
- this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34658
+ const isCurrentlyInactive = this.props.composerStore.editionMode === "inactive";
34659
+ this.props.onComposerContentFocused(newSelection);
34660
+ if (!isCurrentlyInactive) {
34661
+ this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34662
+ }
34624
34663
  this.processTokenAtCursor();
34625
34664
  }
34626
34665
  onDblClick() {
@@ -35115,13 +35154,6 @@ class AbstractComposerStore extends SpreadsheetStore {
35115
35154
  }
35116
35155
  }
35117
35156
  startEdition(text, selection) {
35118
- if (selection) {
35119
- const content = text || this.getComposerContent(this.getters.getActivePosition());
35120
- const validSelection = this.isSelectionValid(content.length, selection.start, selection.end);
35121
- if (!validSelection) {
35122
- return;
35123
- }
35124
- }
35125
35157
  const { col, row } = this.getters.getActivePosition();
35126
35158
  this.model.dispatch("SELECT_FIGURE", { figureId: null });
35127
35159
  this.model.dispatch("SCROLL_TO_CELL", { col, row });
@@ -35178,7 +35210,7 @@ class AbstractComposerStore extends SpreadsheetStore {
35178
35210
  // ---------------------------------------------------------------------------
35179
35211
  get currentContent() {
35180
35212
  if (this.editionMode === "inactive") {
35181
- return this.getComposerContent(this.getters.getActivePosition());
35213
+ return this.getComposerContent(this.getters.getActivePosition()).text;
35182
35214
  }
35183
35215
  return this._currentContent;
35184
35216
  }
@@ -35377,8 +35409,9 @@ class AbstractComposerStore extends SpreadsheetStore {
35377
35409
  this.sheetId = sheetId;
35378
35410
  this.row = row;
35379
35411
  this.editionMode = "editing";
35380
- this.initialContent = this.getComposerContent({ sheetId, col, row });
35381
- this.setContent(str || this.initialContent, selection);
35412
+ const { text, adjustedSelection } = this.getComposerContent({ sheetId, col, row }, selection);
35413
+ this.initialContent = text;
35414
+ this.setContent(str || this.initialContent, adjustedSelection ?? selection);
35382
35415
  this.colorIndexByRange = {};
35383
35416
  const zone = positionToZone({ col: this.col, row: this.row });
35384
35417
  this.captureSelection(zone, col, row);
@@ -35855,7 +35888,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35855
35888
  constructor(get, args) {
35856
35889
  super(get);
35857
35890
  this.args = args;
35858
- this._currentContent = this.getComposerContent();
35891
+ this._currentContent = this.getComposerContent().text;
35859
35892
  }
35860
35893
  getAutoCompleteProviders() {
35861
35894
  const providersDefinitions = super.getAutoCompleteProviders();
@@ -35892,7 +35925,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35892
35925
  })
35893
35926
  .join("");
35894
35927
  }
35895
- return localizeContent(content, this.getters.getLocale());
35928
+ return { text: localizeContent(content, this.getters.getLocale()) };
35896
35929
  }
35897
35930
  stopEdition() {
35898
35931
  this._stopEdition();
@@ -39570,6 +39603,74 @@ function getPath2D(svgPath) {
39570
39603
  return path2D;
39571
39604
  }
39572
39605
 
39606
+ /**
39607
+ * Get the relative path between two files
39608
+ *
39609
+ * Eg.:
39610
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
39611
+ */
39612
+ function getRelativePath(from, to) {
39613
+ const fromPathParts = from.split("/");
39614
+ const toPathParts = to.split("/");
39615
+ let relPath = "";
39616
+ let startIndex = 0;
39617
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
39618
+ if (fromPathParts[i] === toPathParts[i]) {
39619
+ startIndex++;
39620
+ }
39621
+ else {
39622
+ relPath += "../";
39623
+ }
39624
+ }
39625
+ relPath += toPathParts.slice(startIndex).join("/");
39626
+ return relPath;
39627
+ }
39628
+ /**
39629
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
39630
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
39631
+ *
39632
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
39633
+ */
39634
+ function arrayToObject(array, indexOffset = 0) {
39635
+ const obj = {};
39636
+ for (let i = 0; i < array.length; i++) {
39637
+ if (array[i]) {
39638
+ obj[i + indexOffset] = array[i];
39639
+ }
39640
+ }
39641
+ return obj;
39642
+ }
39643
+ /**
39644
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
39645
+ * Replace with characters understandable by JS
39646
+ */
39647
+ function fixXlsxUnicode(str) {
39648
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
39649
+ return String.fromCharCode(parseInt(code, 16));
39650
+ });
39651
+ }
39652
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
39653
+ function getSheetDataHeader(sheetData, dimension, index) {
39654
+ if (dimension === "COL") {
39655
+ if (!sheetData.cols[index]) {
39656
+ sheetData.cols[index] = {};
39657
+ }
39658
+ return sheetData.cols[index];
39659
+ }
39660
+ if (!sheetData.rows[index]) {
39661
+ sheetData.rows[index] = {};
39662
+ }
39663
+ return sheetData.rows[index];
39664
+ }
39665
+ /** Prefix the string by "=" if the string looks like a formula */
39666
+ function prefixFormulaWithEqual(formula) {
39667
+ if (formula[0] === "=") {
39668
+ return formula;
39669
+ }
39670
+ const tokens = tokenize(formula);
39671
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
39672
+ }
39673
+
39573
39674
  /**
39574
39675
  * Map of the different types of conversions warnings and their name in error messages
39575
39676
  */
@@ -40092,66 +40193,6 @@ function hexaToInt(hex) {
40092
40193
  */
40093
40194
  const DEFAULT_SYSTEM_COLOR = "FF000000";
40094
40195
 
40095
- /**
40096
- * Get the relative path between two files
40097
- *
40098
- * Eg.:
40099
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
40100
- */
40101
- function getRelativePath(from, to) {
40102
- const fromPathParts = from.split("/");
40103
- const toPathParts = to.split("/");
40104
- let relPath = "";
40105
- let startIndex = 0;
40106
- for (let i = 0; i < fromPathParts.length - 1; i++) {
40107
- if (fromPathParts[i] === toPathParts[i]) {
40108
- startIndex++;
40109
- }
40110
- else {
40111
- relPath += "../";
40112
- }
40113
- }
40114
- relPath += toPathParts.slice(startIndex).join("/");
40115
- return relPath;
40116
- }
40117
- /**
40118
- * Convert an array of element into an object where the objects keys were the elements position in the array.
40119
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
40120
- *
40121
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
40122
- */
40123
- function arrayToObject(array, indexOffset = 0) {
40124
- const obj = {};
40125
- for (let i = 0; i < array.length; i++) {
40126
- if (array[i]) {
40127
- obj[i + indexOffset] = array[i];
40128
- }
40129
- }
40130
- return obj;
40131
- }
40132
- /**
40133
- * In xlsx we can have string with unicode characters with the format _x00fa_.
40134
- * Replace with characters understandable by JS
40135
- */
40136
- function fixXlsxUnicode(str) {
40137
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
40138
- return String.fromCharCode(parseInt(code, 16));
40139
- });
40140
- }
40141
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
40142
- function getSheetDataHeader(sheetData, dimension, index) {
40143
- if (dimension === "COL") {
40144
- if (!sheetData.cols[index]) {
40145
- sheetData.cols[index] = {};
40146
- }
40147
- return sheetData.cols[index];
40148
- }
40149
- if (!sheetData.rows[index]) {
40150
- sheetData.rows[index] = {};
40151
- }
40152
- return sheetData.rows[index];
40153
- }
40154
-
40155
40196
  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;
40156
40197
  /**
40157
40198
  * Convert excel format to o_spreadsheet format
@@ -40366,9 +40407,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
40366
40407
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
40367
40408
  continue;
40368
40409
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
40369
- values.push(prefixFormula(rule.formula[0]));
40410
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
40370
40411
  if (rule.formula.length === 2) {
40371
- values.push(prefixFormula(rule.formula[1]));
40412
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
40372
40413
  }
40373
40414
  break;
40374
40415
  }
@@ -40526,11 +40567,6 @@ function convertIcons(xlsxIconSet, index) {
40526
40567
  ? ICON_SETS[iconSet].neutral
40527
40568
  : ICON_SETS[iconSet].good;
40528
40569
  }
40529
- /** Prefix the string by "=" if the string looks like a formula */
40530
- function prefixFormula(formula) {
40531
- const tokens = tokenize(formula);
40532
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
40533
- }
40534
40570
  // ---------------------------------------------------------------------------
40535
40571
  // Warnings
40536
40572
  // ---------------------------------------------------------------------------
@@ -41006,7 +41042,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
41006
41042
  dvRules.push(decimalRule);
41007
41043
  break;
41008
41044
  case "list":
41009
- const listRule = convertListrule(dvId++, dv);
41045
+ const listRule = convertListRule(dvId++, dv);
41010
41046
  dvRules.push(listRule);
41011
41047
  break;
41012
41048
  case "date":
@@ -41026,9 +41062,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
41026
41062
  return dvRules;
41027
41063
  }
41028
41064
  function convertDecimalRule(id, dv) {
41029
- const values = [dv.formula1.toString()];
41065
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41030
41066
  if (dv.formula2) {
41031
- values.push(dv.formula2.toString());
41067
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41032
41068
  }
41033
41069
  return {
41034
41070
  id: id.toString(),
@@ -41040,7 +41076,7 @@ function convertDecimalRule(id, dv) {
41040
41076
  },
41041
41077
  };
41042
41078
  }
41043
- function convertListrule(id, dv) {
41079
+ function convertListRule(id, dv) {
41044
41080
  const formula1 = dv.formula1.toString();
41045
41081
  const isRangeRule = rangeReference.test(formula1);
41046
41082
  return {
@@ -41056,9 +41092,9 @@ function convertListrule(id, dv) {
41056
41092
  }
41057
41093
  function convertDateRule(id, dv) {
41058
41094
  let criterion;
41059
- const values = [dv.formula1.toString()];
41095
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41060
41096
  if (dv.formula2) {
41061
- values.push(dv.formula2.toString());
41097
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41062
41098
  criterion = {
41063
41099
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
41064
41100
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -41085,7 +41121,7 @@ function convertCustomRule(id, dv) {
41085
41121
  isBlocking: dv.errorStyle !== "warning",
41086
41122
  criterion: {
41087
41123
  type: "customFormula",
41088
- values: [`=${dv.formula1.toString()}`],
41124
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
41089
41125
  },
41090
41126
  };
41091
41127
  }
@@ -49749,39 +49785,63 @@ class CellComposerStore extends AbstractComposerStore {
49749
49785
  this.model.dispatch("AUTOFILL_TABLE_COLUMN", { ...this.currentEditedCell });
49750
49786
  this.setContent("");
49751
49787
  }
49752
- getComposerContent(position) {
49788
+ getComposerContent(position, selection) {
49753
49789
  const locale = this.getters.getLocale();
49754
49790
  const cell = this.getters.getCell(position);
49755
49791
  if (cell?.isFormula) {
49756
49792
  const prettifiedContent = this.getPrettifiedFormula(cell);
49757
- return localizeFormula(prettifiedContent, locale);
49793
+ // when a formula is prettified (multi lines, indented), adapt the cursor position
49794
+ // to take into account line breaks and tabs
49795
+ function adjustCursorIndex(targetIndex) {
49796
+ let adjustedIndex = 0;
49797
+ let originalIndex = 0;
49798
+ while (originalIndex < targetIndex) {
49799
+ adjustedIndex++;
49800
+ const char = prettifiedContent[adjustedIndex];
49801
+ if (char !== "\n" && char !== "\t") {
49802
+ originalIndex++;
49803
+ }
49804
+ }
49805
+ return adjustedIndex;
49806
+ }
49807
+ let adjustedSelection = selection;
49808
+ if (selection) {
49809
+ adjustedSelection = {
49810
+ start: adjustCursorIndex(selection.start),
49811
+ end: adjustCursorIndex(selection.end),
49812
+ };
49813
+ }
49814
+ return {
49815
+ text: localizeFormula(prettifiedContent, locale),
49816
+ adjustedSelection,
49817
+ };
49758
49818
  }
49759
49819
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49760
49820
  if (spreader) {
49761
- return "";
49821
+ return { text: "" };
49762
49822
  }
49763
49823
  const { format, value, type, formattedValue } = this.getters.getEvaluatedCell(position);
49764
49824
  switch (type) {
49765
49825
  case CellValueType.empty:
49766
- return "";
49826
+ return { text: "" };
49767
49827
  case CellValueType.text:
49768
49828
  case CellValueType.error:
49769
- return value;
49829
+ return { text: value };
49770
49830
  case CellValueType.boolean:
49771
- return formattedValue;
49831
+ return { text: formattedValue };
49772
49832
  case CellValueType.number:
49773
49833
  if (format && isDateTimeFormat(format)) {
49774
49834
  if (parseDateTime(formattedValue, locale) !== null) {
49775
49835
  // formatted string can be parsed again
49776
- return formattedValue;
49836
+ return { text: formattedValue };
49777
49837
  }
49778
49838
  // display a simplified and parsable string otherwise
49779
49839
  const timeFormat = Number.isInteger(value)
49780
49840
  ? locale.dateFormat
49781
49841
  : getDateTimeFormat(locale);
49782
- return formatValue(value, { locale, format: timeFormat });
49842
+ return { text: formatValue(value, { locale, format: timeFormat }) };
49783
49843
  }
49784
- return this.numberComposerContent(value, format, locale);
49844
+ return { text: this.numberComposerContent(value, format, locale) };
49785
49845
  }
49786
49846
  }
49787
49847
  getPrettifiedFormula(cell) {
@@ -49950,8 +50010,9 @@ class GridComposer extends Component {
49950
50010
  },
49951
50011
  focus: this.focus,
49952
50012
  isDefaultFocus: true,
49953
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
50013
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
49954
50014
  focusMode: "contentFocus",
50015
+ selection,
49955
50016
  }),
49956
50017
  onComposerCellFocused: (content) => this.composerFocusStore.focusComposer(this.composerInterface, {
49957
50018
  focusMode: "cellFocus",
@@ -57357,12 +57418,13 @@ class DataValidationEditor extends Component {
57357
57418
  onCloseSidePanel: { type: Function, optional: true },
57358
57419
  };
57359
57420
  state = useState({ rule: this.defaultDataValidationRule, errors: [] });
57421
+ editingSheetId;
57360
57422
  setup() {
57423
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
57361
57424
  if (this.props.rule) {
57362
- const sheetId = this.env.model.getters.getActiveSheetId();
57363
57425
  this.state.rule = {
57364
57426
  ...this.props.rule,
57365
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
57427
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
57366
57428
  };
57367
57429
  this.state.rule.criterion.type = this.props.rule.criterion.type;
57368
57430
  }
@@ -57396,7 +57458,6 @@ class DataValidationEditor extends Component {
57396
57458
  const locale = this.env.model.getters.getLocale();
57397
57459
  const criterion = rule.criterion;
57398
57460
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
57399
- const sheetId = this.env.model.getters.getActiveSheetId();
57400
57461
  const values = criterion.values
57401
57462
  .slice(0, criterionEvaluator.numberOfValues(criterion))
57402
57463
  .map((value) => value?.trim())
@@ -57404,8 +57465,8 @@ class DataValidationEditor extends Component {
57404
57465
  .map((value) => canonicalizeContent(value, locale));
57405
57466
  rule.criterion = { ...criterion, values };
57406
57467
  return {
57407
- sheetId,
57408
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
57468
+ sheetId: this.editingSheetId,
57469
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
57409
57470
  rule,
57410
57471
  };
57411
57472
  }
@@ -57932,6 +57993,7 @@ css /* scss */ `
57932
57993
  .o-button {
57933
57994
  height: 19px;
57934
57995
  width: 19px;
57996
+ box-sizing: content-box;
57935
57997
  .o-icon {
57936
57998
  height: 14px;
57937
57999
  width: 14px;
@@ -68754,7 +68816,7 @@ class FormulaDependencyGraph {
68754
68816
  * in the correct order they should be evaluated.
68755
68817
  * This is called a topological ordering (excluding cycles)
68756
68818
  */
68757
- getCellsDependingOn(ranges) {
68819
+ getCellsDependingOn(ranges, ignore) {
68758
68820
  const visited = this.createEmptyPositionSet();
68759
68821
  const queue = Array.from(ranges).reverse();
68760
68822
  while (queue.length > 0) {
@@ -68769,7 +68831,7 @@ class FormulaDependencyGraph {
68769
68831
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
68770
68832
  const nextInQueue = {};
68771
68833
  for (const position of impactedPositions) {
68772
- if (!visited.has(position)) {
68834
+ if (!visited.has(position) && !ignore.has(position)) {
68773
68835
  if (!nextInQueue[position.sheetId]) {
68774
68836
  nextInQueue[position.sheetId] = [];
68775
68837
  }
@@ -69327,7 +69389,7 @@ class Evaluator {
69327
69389
  }
69328
69390
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
69329
69391
  // the result matrix is split in 2 zones to exclude the array formula position
69330
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
69392
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
69331
69393
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
69332
69394
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
69333
69395
  }
@@ -69445,7 +69507,7 @@ class Evaluator {
69445
69507
  for (const sheetId in zonesBySheetIds) {
69446
69508
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
69447
69509
  }
69448
- return this.formulaDependencies().getCellsDependingOn(ranges);
69510
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
69449
69511
  }
69450
69512
  }
69451
69513
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -70956,7 +71018,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
70956
71018
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
70957
71019
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
70958
71020
  if (!parentSpreadingCell) {
70959
- return false;
71021
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
71022
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
70960
71023
  }
70961
71024
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
70962
71025
  return true;
@@ -82128,6 +82191,7 @@ class RibbonMenu extends Component {
82128
82191
  static components = { Menu };
82129
82192
  rootItems = topbarMenuRegistry.getMenuItems();
82130
82193
  menuRef = useRef("menu");
82194
+ containerRef = useRef("container");
82131
82195
  state = useState({
82132
82196
  menuItems: this.rootItems,
82133
82197
  title: _t("Menu Bar"),
@@ -82135,6 +82199,7 @@ class RibbonMenu extends Component {
82135
82199
  });
82136
82200
  setup() {
82137
82201
  useExternalListener(window, "click", this.onExternalClick, { capture: true });
82202
+ onMounted(this.updateShadows);
82138
82203
  }
82139
82204
  onExternalClick(ev) {
82140
82205
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -82147,6 +82212,7 @@ class RibbonMenu extends Component {
82147
82212
  this.state.parentState = { ...this.state };
82148
82213
  this.state.menuItems = children;
82149
82214
  this.state.title = menu.name(this.env);
82215
+ this.containerRef.el?.scrollTo({ top: 0 });
82150
82216
  }
82151
82217
  else {
82152
82218
  this.state.menuItems = this.rootItems;
@@ -82168,6 +82234,19 @@ class RibbonMenu extends Component {
82168
82234
  height: `${this.props.height}px`,
82169
82235
  });
82170
82236
  }
82237
+ updateShadows() {
82238
+ if (!this.containerRef.el) {
82239
+ return;
82240
+ }
82241
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
82242
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
82243
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
82244
+ this.containerRef.el.classList.add("scroll-bottom");
82245
+ }
82246
+ if (this.containerRef.el.scrollTop > 0) {
82247
+ this.containerRef.el.classList.add("scroll-top");
82248
+ }
82249
+ }
82171
82250
  onClickBack() {
82172
82251
  if (!this.state.parentState) {
82173
82252
  this.props.onClose();
@@ -82176,6 +82255,7 @@ class RibbonMenu extends Component {
82176
82255
  this.state.menuItems = this.state.parentState.menuItems;
82177
82256
  this.state.title = this.state.parentState.title;
82178
82257
  this.state.parentState = this.state.parentState.parentState;
82258
+ this.containerRef.el?.scrollTo({ top: 0 });
82179
82259
  }
82180
82260
  get backTitle() {
82181
82261
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -82232,7 +82312,9 @@ class SmallBottomBar extends Component {
82232
82312
  : "inactive";
82233
82313
  }
82234
82314
  get showFxIcon() {
82235
- return this.focus === "inactive" && !this.composerStore.currentContent;
82315
+ return (this.focus === "inactive" &&
82316
+ !this.composerStore.currentContent &&
82317
+ !this.composerStore.placeholder);
82236
82318
  }
82237
82319
  get rect() {
82238
82320
  return this.composerRef.el
@@ -82249,8 +82331,9 @@ class SmallBottomBar extends Component {
82249
82331
  },
82250
82332
  focus: this.focus,
82251
82333
  composerStore: this.composerStore,
82252
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
82334
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
82253
82335
  focusMode: "contentFocus",
82336
+ selection,
82254
82337
  }),
82255
82338
  isDefaultFocus: false,
82256
82339
  inputStyle: cssPropertiesToCss({
@@ -82258,6 +82341,7 @@ class SmallBottomBar extends Component {
82258
82341
  "max-height": `130px`,
82259
82342
  }),
82260
82343
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82344
+ placeholder: this.composerStore.placeholder,
82261
82345
  };
82262
82346
  }
82263
82347
  get symbols() {
@@ -82320,7 +82404,9 @@ class TopBarComposer extends Component {
82320
82404
  : "inactive";
82321
82405
  }
82322
82406
  get showFxIcon() {
82323
- return this.focus === "inactive" && !this.composerStore.currentContent;
82407
+ return (this.focus === "inactive" &&
82408
+ !this.composerStore.currentContent &&
82409
+ !this.composerStore.placeholder);
82324
82410
  }
82325
82411
  get composerStyle() {
82326
82412
  const style = {
@@ -88486,6 +88572,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
88486
88572
  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, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
88487
88573
 
88488
88574
 
88489
- __info__.version = "19.0.5";
88490
- __info__.date = "2025-10-07T10:04:06.400Z";
88491
- __info__.hash = "86fc442";
88575
+ __info__.version = "19.0.6";
88576
+ __info__.date = "2025-10-16T06:39:36.282Z";
88577
+ __info__.hash = "0d4315a";