@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
  'use strict';
@@ -4515,7 +4515,17 @@ function toNumberMatrix(data, argName) {
4515
4515
  return toMatrix(data).map((row) => {
4516
4516
  return row.map((cell) => {
4517
4517
  if (typeof cell.value !== "number") {
4518
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
4518
+ let message = "";
4519
+ if (typeof cell === "object") {
4520
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
4521
+ }
4522
+ else if (typeof cell === "string") {
4523
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
4524
+ }
4525
+ else if (typeof cell === "boolean") {
4526
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
4527
+ }
4528
+ throw new EvaluationError(message);
4519
4529
  }
4520
4530
  return cell.value;
4521
4531
  });
@@ -9530,7 +9540,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9530
9540
  pasteCell(origin, target, clipboardOption) {
9531
9541
  const { sheetId, col, row } = target;
9532
9542
  const targetCell = this.getters.getEvaluatedCell(target);
9533
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9543
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9534
9544
  if (clipboardOption?.pasteOption === "asValue") {
9535
9545
  this.dispatch("UPDATE_CELL", {
9536
9546
  ...target,
@@ -13903,7 +13913,7 @@ const GROWTH = {
13903
13913
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13904
13914
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13905
13915
  }
13906
- 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)));
13916
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13907
13917
  },
13908
13918
  };
13909
13919
  // -----------------------------------------------------------------------------
@@ -13976,7 +13986,7 @@ const LINEST = {
13976
13986
  if (dataY.length === 0 || dataY[0].length === 0) {
13977
13987
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13978
13988
  }
13979
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13989
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13980
13990
  },
13981
13991
  isExported: true,
13982
13992
  };
@@ -13995,7 +14005,7 @@ const LOGEST = {
13995
14005
  if (dataY.length === 0 || dataY[0].length === 0) {
13996
14006
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13997
14007
  }
13998
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
14008
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13999
14009
  for (let i = 0; i < coeffs.length; i++) {
14000
14010
  coeffs[i][0] = Math.exp(coeffs[i][0]);
14001
14011
  }
@@ -14616,7 +14626,7 @@ const TREND = {
14616
14626
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
14617
14627
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
14618
14628
  }
14619
- 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));
14629
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14620
14630
  },
14621
14631
  };
14622
14632
  // -----------------------------------------------------------------------------
@@ -23137,6 +23147,10 @@ const chartShowValuesPlugin = {
23137
23147
  }
23138
23148
  const ctx = chart.ctx;
23139
23149
  ctx.save();
23150
+ const { left, top, height, width } = chart.chartArea;
23151
+ ctx.beginPath();
23152
+ ctx.rect(left, top, width, height);
23153
+ ctx.clip();
23140
23154
  ctx.textAlign = "center";
23141
23155
  ctx.textBaseline = "middle";
23142
23156
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -24180,7 +24194,7 @@ class ChartJsComponent extends owl.Component {
24180
24194
  this.chart.update();
24181
24195
  }
24182
24196
  hasChartDataChanged() {
24183
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
24197
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
24184
24198
  }
24185
24199
  enableAnimationInChartData(chartData) {
24186
24200
  return {
@@ -24188,6 +24202,17 @@ class ChartJsComponent extends owl.Component {
24188
24202
  options: { ...chartData.options, animation: { animateRotate: true } },
24189
24203
  };
24190
24204
  }
24205
+ getChartDataInRuntime(runtime) {
24206
+ const data = runtime.chartJsConfig.data;
24207
+ return {
24208
+ labels: data.labels,
24209
+ dataset: data.datasets.map((dataset) => ({
24210
+ data: dataset.data,
24211
+ label: dataset.label,
24212
+ tree: dataset.tree,
24213
+ })),
24214
+ };
24215
+ }
24191
24216
  get animationChartId() {
24192
24217
  return this.props.isFullScreen ? this.props.chartId + "-fullscreen" : this.props.chartId;
24193
24218
  }
@@ -25611,6 +25636,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
25611
25636
  parser: luxonFormat,
25612
25637
  displayFormats,
25613
25638
  unit: timeUnit ?? false,
25639
+ tooltipFormat: luxonFormat,
25614
25640
  };
25615
25641
  }
25616
25642
  /**
@@ -26679,6 +26705,7 @@ function getLineChartScales(definition, args) {
26679
26705
  };
26680
26706
  Object.assign(scales.x, axis);
26681
26707
  scales.x.ticks.maxTicksLimit = 15;
26708
+ delete scales?.x?.ticks?.callback;
26682
26709
  }
26683
26710
  else if (axisType === "linear") {
26684
26711
  scales.x.type = "linear";
@@ -32364,7 +32391,7 @@ class ChartDashboardMenu extends owl.Component {
32364
32391
  }
32365
32392
  openContextMenu(ev) {
32366
32393
  this.menuState.isOpen = true;
32367
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
32394
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32368
32395
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32369
32396
  this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32370
32397
  }
@@ -32396,6 +32423,7 @@ class CarouselFigure extends owl.Component {
32396
32423
  onFigureDeleted: Function,
32397
32424
  editFigureStyle: { type: Function, optional: true },
32398
32425
  isFullScreen: { type: Boolean, optional: true },
32426
+ openContextMenu: { type: Function, optional: true },
32399
32427
  };
32400
32428
  static components = { ChartDashboardMenu, MenuPopover };
32401
32429
  carouselTabsRef = owl.useRef("carouselTabs");
@@ -32529,6 +32557,12 @@ class CarouselFigure extends owl.Component {
32529
32557
  get visibleCarouselItems() {
32530
32558
  return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32531
32559
  }
32560
+ openContextMenu(event) {
32561
+ const target = event.currentTarget;
32562
+ if (target) {
32563
+ this.props.openContextMenu?.(getBoundingRectAsPOJO(target));
32564
+ }
32565
+ }
32532
32566
  }
32533
32567
 
32534
32568
  class ChartFigure extends owl.Component {
@@ -32538,6 +32572,7 @@ class ChartFigure extends owl.Component {
32538
32572
  onFigureDeleted: Function,
32539
32573
  editFigureStyle: { type: Function, optional: true },
32540
32574
  isFullScreen: { type: Boolean, optional: true },
32575
+ openContextMenu: { type: Function, optional: true },
32541
32576
  };
32542
32577
  static components = { ChartDashboardMenu };
32543
32578
  onDoubleClick() {
@@ -32570,6 +32605,7 @@ class ImageFigure extends owl.Component {
32570
32605
  figureUI: Object,
32571
32606
  onFigureDeleted: Function,
32572
32607
  editFigureStyle: { type: Function, optional: true },
32608
+ openContextMenu: { type: Function, optional: true },
32573
32609
  };
32574
32610
  static components = {};
32575
32611
  // ---------------------------------------------------------------------------
@@ -34621,8 +34657,11 @@ class Composer extends owl.Component {
34621
34657
  }
34622
34658
  const newSelection = this.contentHelper.getCurrentSelection();
34623
34659
  this.props.composerStore.stopComposerRangeSelection();
34624
- this.props.onComposerContentFocused();
34625
- this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34660
+ const isCurrentlyInactive = this.props.composerStore.editionMode === "inactive";
34661
+ this.props.onComposerContentFocused(newSelection);
34662
+ if (!isCurrentlyInactive) {
34663
+ this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34664
+ }
34626
34665
  this.processTokenAtCursor();
34627
34666
  }
34628
34667
  onDblClick() {
@@ -35117,13 +35156,6 @@ class AbstractComposerStore extends SpreadsheetStore {
35117
35156
  }
35118
35157
  }
35119
35158
  startEdition(text, selection) {
35120
- if (selection) {
35121
- const content = text || this.getComposerContent(this.getters.getActivePosition());
35122
- const validSelection = this.isSelectionValid(content.length, selection.start, selection.end);
35123
- if (!validSelection) {
35124
- return;
35125
- }
35126
- }
35127
35159
  const { col, row } = this.getters.getActivePosition();
35128
35160
  this.model.dispatch("SELECT_FIGURE", { figureId: null });
35129
35161
  this.model.dispatch("SCROLL_TO_CELL", { col, row });
@@ -35180,7 +35212,7 @@ class AbstractComposerStore extends SpreadsheetStore {
35180
35212
  // ---------------------------------------------------------------------------
35181
35213
  get currentContent() {
35182
35214
  if (this.editionMode === "inactive") {
35183
- return this.getComposerContent(this.getters.getActivePosition());
35215
+ return this.getComposerContent(this.getters.getActivePosition()).text;
35184
35216
  }
35185
35217
  return this._currentContent;
35186
35218
  }
@@ -35379,8 +35411,9 @@ class AbstractComposerStore extends SpreadsheetStore {
35379
35411
  this.sheetId = sheetId;
35380
35412
  this.row = row;
35381
35413
  this.editionMode = "editing";
35382
- this.initialContent = this.getComposerContent({ sheetId, col, row });
35383
- this.setContent(str || this.initialContent, selection);
35414
+ const { text, adjustedSelection } = this.getComposerContent({ sheetId, col, row }, selection);
35415
+ this.initialContent = text;
35416
+ this.setContent(str || this.initialContent, adjustedSelection ?? selection);
35384
35417
  this.colorIndexByRange = {};
35385
35418
  const zone = positionToZone({ col: this.col, row: this.row });
35386
35419
  this.captureSelection(zone, col, row);
@@ -35857,7 +35890,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35857
35890
  constructor(get, args) {
35858
35891
  super(get);
35859
35892
  this.args = args;
35860
- this._currentContent = this.getComposerContent();
35893
+ this._currentContent = this.getComposerContent().text;
35861
35894
  }
35862
35895
  getAutoCompleteProviders() {
35863
35896
  const providersDefinitions = super.getAutoCompleteProviders();
@@ -35894,7 +35927,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35894
35927
  })
35895
35928
  .join("");
35896
35929
  }
35897
- return localizeContent(content, this.getters.getLocale());
35930
+ return { text: localizeContent(content, this.getters.getLocale()) };
35898
35931
  }
35899
35932
  stopEdition() {
35900
35933
  this._stopEdition();
@@ -39572,6 +39605,74 @@ function getPath2D(svgPath) {
39572
39605
  return path2D;
39573
39606
  }
39574
39607
 
39608
+ /**
39609
+ * Get the relative path between two files
39610
+ *
39611
+ * Eg.:
39612
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
39613
+ */
39614
+ function getRelativePath(from, to) {
39615
+ const fromPathParts = from.split("/");
39616
+ const toPathParts = to.split("/");
39617
+ let relPath = "";
39618
+ let startIndex = 0;
39619
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
39620
+ if (fromPathParts[i] === toPathParts[i]) {
39621
+ startIndex++;
39622
+ }
39623
+ else {
39624
+ relPath += "../";
39625
+ }
39626
+ }
39627
+ relPath += toPathParts.slice(startIndex).join("/");
39628
+ return relPath;
39629
+ }
39630
+ /**
39631
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
39632
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
39633
+ *
39634
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
39635
+ */
39636
+ function arrayToObject(array, indexOffset = 0) {
39637
+ const obj = {};
39638
+ for (let i = 0; i < array.length; i++) {
39639
+ if (array[i]) {
39640
+ obj[i + indexOffset] = array[i];
39641
+ }
39642
+ }
39643
+ return obj;
39644
+ }
39645
+ /**
39646
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
39647
+ * Replace with characters understandable by JS
39648
+ */
39649
+ function fixXlsxUnicode(str) {
39650
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
39651
+ return String.fromCharCode(parseInt(code, 16));
39652
+ });
39653
+ }
39654
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
39655
+ function getSheetDataHeader(sheetData, dimension, index) {
39656
+ if (dimension === "COL") {
39657
+ if (!sheetData.cols[index]) {
39658
+ sheetData.cols[index] = {};
39659
+ }
39660
+ return sheetData.cols[index];
39661
+ }
39662
+ if (!sheetData.rows[index]) {
39663
+ sheetData.rows[index] = {};
39664
+ }
39665
+ return sheetData.rows[index];
39666
+ }
39667
+ /** Prefix the string by "=" if the string looks like a formula */
39668
+ function prefixFormulaWithEqual(formula) {
39669
+ if (formula[0] === "=") {
39670
+ return formula;
39671
+ }
39672
+ const tokens = tokenize(formula);
39673
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
39674
+ }
39675
+
39575
39676
  /**
39576
39677
  * Map of the different types of conversions warnings and their name in error messages
39577
39678
  */
@@ -40094,66 +40195,6 @@ function hexaToInt(hex) {
40094
40195
  */
40095
40196
  const DEFAULT_SYSTEM_COLOR = "FF000000";
40096
40197
 
40097
- /**
40098
- * Get the relative path between two files
40099
- *
40100
- * Eg.:
40101
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
40102
- */
40103
- function getRelativePath(from, to) {
40104
- const fromPathParts = from.split("/");
40105
- const toPathParts = to.split("/");
40106
- let relPath = "";
40107
- let startIndex = 0;
40108
- for (let i = 0; i < fromPathParts.length - 1; i++) {
40109
- if (fromPathParts[i] === toPathParts[i]) {
40110
- startIndex++;
40111
- }
40112
- else {
40113
- relPath += "../";
40114
- }
40115
- }
40116
- relPath += toPathParts.slice(startIndex).join("/");
40117
- return relPath;
40118
- }
40119
- /**
40120
- * Convert an array of element into an object where the objects keys were the elements position in the array.
40121
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
40122
- *
40123
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
40124
- */
40125
- function arrayToObject(array, indexOffset = 0) {
40126
- const obj = {};
40127
- for (let i = 0; i < array.length; i++) {
40128
- if (array[i]) {
40129
- obj[i + indexOffset] = array[i];
40130
- }
40131
- }
40132
- return obj;
40133
- }
40134
- /**
40135
- * In xlsx we can have string with unicode characters with the format _x00fa_.
40136
- * Replace with characters understandable by JS
40137
- */
40138
- function fixXlsxUnicode(str) {
40139
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
40140
- return String.fromCharCode(parseInt(code, 16));
40141
- });
40142
- }
40143
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
40144
- function getSheetDataHeader(sheetData, dimension, index) {
40145
- if (dimension === "COL") {
40146
- if (!sheetData.cols[index]) {
40147
- sheetData.cols[index] = {};
40148
- }
40149
- return sheetData.cols[index];
40150
- }
40151
- if (!sheetData.rows[index]) {
40152
- sheetData.rows[index] = {};
40153
- }
40154
- return sheetData.rows[index];
40155
- }
40156
-
40157
40198
  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;
40158
40199
  /**
40159
40200
  * Convert excel format to o_spreadsheet format
@@ -40368,9 +40409,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
40368
40409
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
40369
40410
  continue;
40370
40411
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
40371
- values.push(prefixFormula(rule.formula[0]));
40412
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
40372
40413
  if (rule.formula.length === 2) {
40373
- values.push(prefixFormula(rule.formula[1]));
40414
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
40374
40415
  }
40375
40416
  break;
40376
40417
  }
@@ -40528,11 +40569,6 @@ function convertIcons(xlsxIconSet, index) {
40528
40569
  ? ICON_SETS[iconSet].neutral
40529
40570
  : ICON_SETS[iconSet].good;
40530
40571
  }
40531
- /** Prefix the string by "=" if the string looks like a formula */
40532
- function prefixFormula(formula) {
40533
- const tokens = tokenize(formula);
40534
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
40535
- }
40536
40572
  // ---------------------------------------------------------------------------
40537
40573
  // Warnings
40538
40574
  // ---------------------------------------------------------------------------
@@ -41008,7 +41044,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
41008
41044
  dvRules.push(decimalRule);
41009
41045
  break;
41010
41046
  case "list":
41011
- const listRule = convertListrule(dvId++, dv);
41047
+ const listRule = convertListRule(dvId++, dv);
41012
41048
  dvRules.push(listRule);
41013
41049
  break;
41014
41050
  case "date":
@@ -41028,9 +41064,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
41028
41064
  return dvRules;
41029
41065
  }
41030
41066
  function convertDecimalRule(id, dv) {
41031
- const values = [dv.formula1.toString()];
41067
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41032
41068
  if (dv.formula2) {
41033
- values.push(dv.formula2.toString());
41069
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41034
41070
  }
41035
41071
  return {
41036
41072
  id: id.toString(),
@@ -41042,7 +41078,7 @@ function convertDecimalRule(id, dv) {
41042
41078
  },
41043
41079
  };
41044
41080
  }
41045
- function convertListrule(id, dv) {
41081
+ function convertListRule(id, dv) {
41046
41082
  const formula1 = dv.formula1.toString();
41047
41083
  const isRangeRule = rangeReference.test(formula1);
41048
41084
  return {
@@ -41058,9 +41094,9 @@ function convertListrule(id, dv) {
41058
41094
  }
41059
41095
  function convertDateRule(id, dv) {
41060
41096
  let criterion;
41061
- const values = [dv.formula1.toString()];
41097
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41062
41098
  if (dv.formula2) {
41063
- values.push(dv.formula2.toString());
41099
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41064
41100
  criterion = {
41065
41101
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
41066
41102
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -41087,7 +41123,7 @@ function convertCustomRule(id, dv) {
41087
41123
  isBlocking: dv.errorStyle !== "warning",
41088
41124
  criterion: {
41089
41125
  type: "customFormula",
41090
- values: [`=${dv.formula1.toString()}`],
41126
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
41091
41127
  },
41092
41128
  };
41093
41129
  }
@@ -49751,39 +49787,63 @@ class CellComposerStore extends AbstractComposerStore {
49751
49787
  this.model.dispatch("AUTOFILL_TABLE_COLUMN", { ...this.currentEditedCell });
49752
49788
  this.setContent("");
49753
49789
  }
49754
- getComposerContent(position) {
49790
+ getComposerContent(position, selection) {
49755
49791
  const locale = this.getters.getLocale();
49756
49792
  const cell = this.getters.getCell(position);
49757
49793
  if (cell?.isFormula) {
49758
49794
  const prettifiedContent = this.getPrettifiedFormula(cell);
49759
- return localizeFormula(prettifiedContent, locale);
49795
+ // when a formula is prettified (multi lines, indented), adapt the cursor position
49796
+ // to take into account line breaks and tabs
49797
+ function adjustCursorIndex(targetIndex) {
49798
+ let adjustedIndex = 0;
49799
+ let originalIndex = 0;
49800
+ while (originalIndex < targetIndex) {
49801
+ adjustedIndex++;
49802
+ const char = prettifiedContent[adjustedIndex];
49803
+ if (char !== "\n" && char !== "\t") {
49804
+ originalIndex++;
49805
+ }
49806
+ }
49807
+ return adjustedIndex;
49808
+ }
49809
+ let adjustedSelection = selection;
49810
+ if (selection) {
49811
+ adjustedSelection = {
49812
+ start: adjustCursorIndex(selection.start),
49813
+ end: adjustCursorIndex(selection.end),
49814
+ };
49815
+ }
49816
+ return {
49817
+ text: localizeFormula(prettifiedContent, locale),
49818
+ adjustedSelection,
49819
+ };
49760
49820
  }
49761
49821
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49762
49822
  if (spreader) {
49763
- return "";
49823
+ return { text: "" };
49764
49824
  }
49765
49825
  const { format, value, type, formattedValue } = this.getters.getEvaluatedCell(position);
49766
49826
  switch (type) {
49767
49827
  case CellValueType.empty:
49768
- return "";
49828
+ return { text: "" };
49769
49829
  case CellValueType.text:
49770
49830
  case CellValueType.error:
49771
- return value;
49831
+ return { text: value };
49772
49832
  case CellValueType.boolean:
49773
- return formattedValue;
49833
+ return { text: formattedValue };
49774
49834
  case CellValueType.number:
49775
49835
  if (format && isDateTimeFormat(format)) {
49776
49836
  if (parseDateTime(formattedValue, locale) !== null) {
49777
49837
  // formatted string can be parsed again
49778
- return formattedValue;
49838
+ return { text: formattedValue };
49779
49839
  }
49780
49840
  // display a simplified and parsable string otherwise
49781
49841
  const timeFormat = Number.isInteger(value)
49782
49842
  ? locale.dateFormat
49783
49843
  : getDateTimeFormat(locale);
49784
- return formatValue(value, { locale, format: timeFormat });
49844
+ return { text: formatValue(value, { locale, format: timeFormat }) };
49785
49845
  }
49786
- return this.numberComposerContent(value, format, locale);
49846
+ return { text: this.numberComposerContent(value, format, locale) };
49787
49847
  }
49788
49848
  }
49789
49849
  getPrettifiedFormula(cell) {
@@ -49952,8 +50012,9 @@ class GridComposer extends owl.Component {
49952
50012
  },
49953
50013
  focus: this.focus,
49954
50014
  isDefaultFocus: true,
49955
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
50015
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
49956
50016
  focusMode: "contentFocus",
50017
+ selection,
49957
50018
  }),
49958
50019
  onComposerCellFocused: (content) => this.composerFocusStore.focusComposer(this.composerInterface, {
49959
50020
  focusMode: "cellFocus",
@@ -57359,12 +57420,13 @@ class DataValidationEditor extends owl.Component {
57359
57420
  onCloseSidePanel: { type: Function, optional: true },
57360
57421
  };
57361
57422
  state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
57423
+ editingSheetId;
57362
57424
  setup() {
57425
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
57363
57426
  if (this.props.rule) {
57364
- const sheetId = this.env.model.getters.getActiveSheetId();
57365
57427
  this.state.rule = {
57366
57428
  ...this.props.rule,
57367
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
57429
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
57368
57430
  };
57369
57431
  this.state.rule.criterion.type = this.props.rule.criterion.type;
57370
57432
  }
@@ -57398,7 +57460,6 @@ class DataValidationEditor extends owl.Component {
57398
57460
  const locale = this.env.model.getters.getLocale();
57399
57461
  const criterion = rule.criterion;
57400
57462
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
57401
- const sheetId = this.env.model.getters.getActiveSheetId();
57402
57463
  const values = criterion.values
57403
57464
  .slice(0, criterionEvaluator.numberOfValues(criterion))
57404
57465
  .map((value) => value?.trim())
@@ -57406,8 +57467,8 @@ class DataValidationEditor extends owl.Component {
57406
57467
  .map((value) => canonicalizeContent(value, locale));
57407
57468
  rule.criterion = { ...criterion, values };
57408
57469
  return {
57409
- sheetId,
57410
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
57470
+ sheetId: this.editingSheetId,
57471
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
57411
57472
  rule,
57412
57473
  };
57413
57474
  }
@@ -57934,6 +57995,7 @@ css /* scss */ `
57934
57995
  .o-button {
57935
57996
  height: 19px;
57936
57997
  width: 19px;
57998
+ box-sizing: content-box;
57937
57999
  .o-icon {
57938
58000
  height: 14px;
57939
58001
  width: 14px;
@@ -68756,7 +68818,7 @@ class FormulaDependencyGraph {
68756
68818
  * in the correct order they should be evaluated.
68757
68819
  * This is called a topological ordering (excluding cycles)
68758
68820
  */
68759
- getCellsDependingOn(ranges) {
68821
+ getCellsDependingOn(ranges, ignore) {
68760
68822
  const visited = this.createEmptyPositionSet();
68761
68823
  const queue = Array.from(ranges).reverse();
68762
68824
  while (queue.length > 0) {
@@ -68771,7 +68833,7 @@ class FormulaDependencyGraph {
68771
68833
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
68772
68834
  const nextInQueue = {};
68773
68835
  for (const position of impactedPositions) {
68774
- if (!visited.has(position)) {
68836
+ if (!visited.has(position) && !ignore.has(position)) {
68775
68837
  if (!nextInQueue[position.sheetId]) {
68776
68838
  nextInQueue[position.sheetId] = [];
68777
68839
  }
@@ -69329,7 +69391,7 @@ class Evaluator {
69329
69391
  }
69330
69392
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
69331
69393
  // the result matrix is split in 2 zones to exclude the array formula position
69332
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
69394
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
69333
69395
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
69334
69396
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
69335
69397
  }
@@ -69447,7 +69509,7 @@ class Evaluator {
69447
69509
  for (const sheetId in zonesBySheetIds) {
69448
69510
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
69449
69511
  }
69450
- return this.formulaDependencies().getCellsDependingOn(ranges);
69512
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
69451
69513
  }
69452
69514
  }
69453
69515
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -70958,7 +71020,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
70958
71020
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
70959
71021
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
70960
71022
  if (!parentSpreadingCell) {
70961
- return false;
71023
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
71024
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
70962
71025
  }
70963
71026
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
70964
71027
  return true;
@@ -82130,6 +82193,7 @@ class RibbonMenu extends owl.Component {
82130
82193
  static components = { Menu };
82131
82194
  rootItems = topbarMenuRegistry.getMenuItems();
82132
82195
  menuRef = owl.useRef("menu");
82196
+ containerRef = owl.useRef("container");
82133
82197
  state = owl.useState({
82134
82198
  menuItems: this.rootItems,
82135
82199
  title: _t("Menu Bar"),
@@ -82137,6 +82201,7 @@ class RibbonMenu extends owl.Component {
82137
82201
  });
82138
82202
  setup() {
82139
82203
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
82204
+ owl.onMounted(this.updateShadows);
82140
82205
  }
82141
82206
  onExternalClick(ev) {
82142
82207
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -82149,6 +82214,7 @@ class RibbonMenu extends owl.Component {
82149
82214
  this.state.parentState = { ...this.state };
82150
82215
  this.state.menuItems = children;
82151
82216
  this.state.title = menu.name(this.env);
82217
+ this.containerRef.el?.scrollTo({ top: 0 });
82152
82218
  }
82153
82219
  else {
82154
82220
  this.state.menuItems = this.rootItems;
@@ -82170,6 +82236,19 @@ class RibbonMenu extends owl.Component {
82170
82236
  height: `${this.props.height}px`,
82171
82237
  });
82172
82238
  }
82239
+ updateShadows() {
82240
+ if (!this.containerRef.el) {
82241
+ return;
82242
+ }
82243
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
82244
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
82245
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
82246
+ this.containerRef.el.classList.add("scroll-bottom");
82247
+ }
82248
+ if (this.containerRef.el.scrollTop > 0) {
82249
+ this.containerRef.el.classList.add("scroll-top");
82250
+ }
82251
+ }
82173
82252
  onClickBack() {
82174
82253
  if (!this.state.parentState) {
82175
82254
  this.props.onClose();
@@ -82178,6 +82257,7 @@ class RibbonMenu extends owl.Component {
82178
82257
  this.state.menuItems = this.state.parentState.menuItems;
82179
82258
  this.state.title = this.state.parentState.title;
82180
82259
  this.state.parentState = this.state.parentState.parentState;
82260
+ this.containerRef.el?.scrollTo({ top: 0 });
82181
82261
  }
82182
82262
  get backTitle() {
82183
82263
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -82234,7 +82314,9 @@ class SmallBottomBar extends owl.Component {
82234
82314
  : "inactive";
82235
82315
  }
82236
82316
  get showFxIcon() {
82237
- return this.focus === "inactive" && !this.composerStore.currentContent;
82317
+ return (this.focus === "inactive" &&
82318
+ !this.composerStore.currentContent &&
82319
+ !this.composerStore.placeholder);
82238
82320
  }
82239
82321
  get rect() {
82240
82322
  return this.composerRef.el
@@ -82251,8 +82333,9 @@ class SmallBottomBar extends owl.Component {
82251
82333
  },
82252
82334
  focus: this.focus,
82253
82335
  composerStore: this.composerStore,
82254
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
82336
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
82255
82337
  focusMode: "contentFocus",
82338
+ selection,
82256
82339
  }),
82257
82340
  isDefaultFocus: false,
82258
82341
  inputStyle: cssPropertiesToCss({
@@ -82260,6 +82343,7 @@ class SmallBottomBar extends owl.Component {
82260
82343
  "max-height": `130px`,
82261
82344
  }),
82262
82345
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82346
+ placeholder: this.composerStore.placeholder,
82263
82347
  };
82264
82348
  }
82265
82349
  get symbols() {
@@ -82322,7 +82406,9 @@ class TopBarComposer extends owl.Component {
82322
82406
  : "inactive";
82323
82407
  }
82324
82408
  get showFxIcon() {
82325
- return this.focus === "inactive" && !this.composerStore.currentContent;
82409
+ return (this.focus === "inactive" &&
82410
+ !this.composerStore.currentContent &&
82411
+ !this.composerStore.placeholder);
82326
82412
  }
82327
82413
  get composerStyle() {
82328
82414
  const style = {
@@ -88538,6 +88624,6 @@ exports.tokenColors = tokenColors;
88538
88624
  exports.tokenize = tokenize;
88539
88625
 
88540
88626
 
88541
- __info__.version = "19.0.5";
88542
- __info__.date = "2025-10-07T10:04:06.400Z";
88543
- __info__.hash = "86fc442";
88627
+ __info__.version = "19.0.6";
88628
+ __info__.date = "2025-10-16T06:39:36.282Z";
88629
+ __info__.hash = "0d4315a";