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