@odoo/o-spreadsheet 19.1.0-alpha.4 → 19.1.0-alpha.5

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.1.0-alpha.4
6
- * @date 2025-10-07T10:03:20.917Z
7
- * @hash b7cfed8
5
+ * @version 19.1.0-alpha.5
6
+ * @date 2025-10-16T06:39:55.925Z
7
+ * @hash 1a0e3d5
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -4451,7 +4451,17 @@
4451
4451
  return toMatrix(data).map((row) => {
4452
4452
  return row.map((cell) => {
4453
4453
  if (typeof cell.value !== "number") {
4454
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
4454
+ let message = "";
4455
+ if (typeof cell === "object") {
4456
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
4457
+ }
4458
+ else if (typeof cell === "string") {
4459
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
4460
+ }
4461
+ else if (typeof cell === "boolean") {
4462
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
4463
+ }
4464
+ throw new EvaluationError(message);
4455
4465
  }
4456
4466
  return cell.value;
4457
4467
  });
@@ -6389,6 +6399,9 @@
6389
6399
  if (content.startsWith("=")) {
6390
6400
  throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
6391
6401
  }
6402
+ if (content.startsWith("'")) {
6403
+ return content.slice(1);
6404
+ }
6392
6405
  if (content === "") {
6393
6406
  return null;
6394
6407
  }
@@ -9338,6 +9351,32 @@
9338
9351
  groups: [],
9339
9352
  });
9340
9353
  }
9354
+ function togglePivotCollapse(position, env) {
9355
+ const pivotCell = env.model.getters.getPivotCellFromPosition(position);
9356
+ const pivotId = env.model.getters.getPivotIdFromPosition(position);
9357
+ if (!pivotId || pivotCell.type !== "HEADER") {
9358
+ return;
9359
+ }
9360
+ const definition = env.model.getters.getPivotCoreDefinition(pivotId);
9361
+ const collapsedDomains = definition.collapsedDomains?.[pivotCell.dimension]
9362
+ ? [...definition.collapsedDomains[pivotCell.dimension]]
9363
+ : [];
9364
+ const index = collapsedDomains.findIndex((domain) => deepEquals(domain, pivotCell.domain));
9365
+ if (index !== -1) {
9366
+ collapsedDomains.splice(index, 1);
9367
+ }
9368
+ else {
9369
+ collapsedDomains.push(pivotCell.domain);
9370
+ }
9371
+ const newDomains = definition.collapsedDomains
9372
+ ? { ...definition.collapsedDomains }
9373
+ : { COL: [], ROW: [] };
9374
+ newDomains[pivotCell.dimension] = collapsedDomains;
9375
+ env.model.dispatch("UPDATE_PIVOT", {
9376
+ pivotId,
9377
+ pivot: { ...definition, collapsedDomains: newDomains },
9378
+ });
9379
+ }
9341
9380
 
9342
9381
  class CellClipboardHandler extends AbstractCellClipboardHandler {
9343
9382
  isCutAllowed(data) {
@@ -9511,7 +9550,7 @@
9511
9550
  pasteCell(origin, target, clipboardOption) {
9512
9551
  const { sheetId, col, row } = target;
9513
9552
  const targetCell = this.getters.getEvaluatedCell(target);
9514
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9553
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9515
9554
  if (clipboardOption?.pasteOption === "asValue") {
9516
9555
  this.dispatch("UPDATE_CELL", {
9517
9556
  ...target,
@@ -13737,7 +13776,7 @@ stores.inject(MyMetaStore, storeInstance);
13737
13776
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13738
13777
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13739
13778
  }
13740
- 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)));
13779
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13741
13780
  },
13742
13781
  };
13743
13782
  // -----------------------------------------------------------------------------
@@ -13810,7 +13849,7 @@ stores.inject(MyMetaStore, storeInstance);
13810
13849
  if (dataY.length === 0 || dataY[0].length === 0) {
13811
13850
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13812
13851
  }
13813
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13852
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13814
13853
  },
13815
13854
  isExported: true,
13816
13855
  };
@@ -13829,7 +13868,7 @@ stores.inject(MyMetaStore, storeInstance);
13829
13868
  if (dataY.length === 0 || dataY[0].length === 0) {
13830
13869
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13831
13870
  }
13832
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13871
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13833
13872
  for (let i = 0; i < coeffs.length; i++) {
13834
13873
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13835
13874
  }
@@ -14450,7 +14489,7 @@ stores.inject(MyMetaStore, storeInstance);
14450
14489
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
14451
14490
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
14452
14491
  }
14453
- 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));
14492
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14454
14493
  },
14455
14494
  };
14456
14495
  // -----------------------------------------------------------------------------
@@ -22971,6 +23010,10 @@ stores.inject(MyMetaStore, storeInstance);
22971
23010
  }
22972
23011
  const ctx = chart.ctx;
22973
23012
  ctx.save();
23013
+ const { left, top, height, width } = chart.chartArea;
23014
+ ctx.beginPath();
23015
+ ctx.rect(left, top, width, height);
23016
+ ctx.clip();
22974
23017
  ctx.textAlign = "center";
22975
23018
  ctx.textBaseline = "middle";
22976
23019
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -24005,7 +24048,7 @@ stores.inject(MyMetaStore, storeInstance);
24005
24048
  this.chart.update();
24006
24049
  }
24007
24050
  hasChartDataChanged() {
24008
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
24051
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
24009
24052
  }
24010
24053
  enableAnimationInChartData(chartData) {
24011
24054
  return {
@@ -24013,6 +24056,17 @@ stores.inject(MyMetaStore, storeInstance);
24013
24056
  options: { ...chartData.options, animation: { animateRotate: true } },
24014
24057
  };
24015
24058
  }
24059
+ getChartDataInRuntime(runtime) {
24060
+ const data = runtime.chartJsConfig.data;
24061
+ return {
24062
+ labels: data.labels,
24063
+ dataset: data.datasets.map((dataset) => ({
24064
+ data: dataset.data,
24065
+ label: dataset.label,
24066
+ tree: dataset.tree,
24067
+ })),
24068
+ };
24069
+ }
24016
24070
  get animationChartId() {
24017
24071
  return this.props.isFullScreen ? this.props.chartId + "-fullscreen" : this.props.chartId;
24018
24072
  }
@@ -25436,6 +25490,7 @@ stores.inject(MyMetaStore, storeInstance);
25436
25490
  parser: luxonFormat,
25437
25491
  displayFormats,
25438
25492
  unit: timeUnit ?? false,
25493
+ tooltipFormat: luxonFormat,
25439
25494
  };
25440
25495
  }
25441
25496
  /**
@@ -26504,6 +26559,7 @@ stores.inject(MyMetaStore, storeInstance);
26504
26559
  };
26505
26560
  Object.assign(scales.x, axis);
26506
26561
  scales.x.ticks.maxTicksLimit = 15;
26562
+ delete scales?.x?.ticks?.callback;
26507
26563
  }
26508
26564
  else if (axisType === "linear") {
26509
26565
  scales.x.type = "linear";
@@ -31582,7 +31638,8 @@ stores.inject(MyMetaStore, storeInstance);
31582
31638
  const menuItemsAndSeparators = [];
31583
31639
  for (let i = 0; i < this.props.menuItems.length; i++) {
31584
31640
  const menuItem = this.props.menuItems[i];
31585
- if (menuItem.isVisible(this.env)) {
31641
+ if (menuItem.isVisible(this.env) &&
31642
+ (!this.isRoot(menuItem) || this.hasVisibleChildren(menuItem))) {
31586
31643
  menuItemsAndSeparators.push(menuItem);
31587
31644
  }
31588
31645
  if (menuItem.separator &&
@@ -31624,6 +31681,9 @@ stores.inject(MyMetaStore, storeInstance);
31624
31681
  isRoot(menu) {
31625
31682
  return !menu.execute;
31626
31683
  }
31684
+ hasVisibleChildren(menu) {
31685
+ return menu.children(this.env).some((child) => child.isVisible(this.env));
31686
+ }
31627
31687
  isEnabled(menu) {
31628
31688
  if (menu.isEnabled(this.env)) {
31629
31689
  return this.env.model.getters.isReadonly() ? menu.isReadonlyAllowed : true;
@@ -32182,7 +32242,7 @@ stores.inject(MyMetaStore, storeInstance);
32182
32242
  }
32183
32243
  openContextMenu(ev) {
32184
32244
  this.menuState.isOpen = true;
32185
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
32245
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32186
32246
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32187
32247
  this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32188
32248
  }
@@ -32214,6 +32274,7 @@ stores.inject(MyMetaStore, storeInstance);
32214
32274
  onFigureDeleted: Function,
32215
32275
  editFigureStyle: { type: Function, optional: true },
32216
32276
  isFullScreen: { type: Boolean, optional: true },
32277
+ openContextMenu: { type: Function, optional: true },
32217
32278
  };
32218
32279
  static components = { ChartDashboardMenu, MenuPopover };
32219
32280
  carouselTabsRef = owl.useRef("carouselTabs");
@@ -32347,6 +32408,12 @@ stores.inject(MyMetaStore, storeInstance);
32347
32408
  get visibleCarouselItems() {
32348
32409
  return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32349
32410
  }
32411
+ openContextMenu(event) {
32412
+ const target = event.currentTarget;
32413
+ if (target) {
32414
+ this.props.openContextMenu?.(getBoundingRectAsPOJO(target));
32415
+ }
32416
+ }
32350
32417
  }
32351
32418
 
32352
32419
  class ChartFigure extends owl.Component {
@@ -32356,6 +32423,7 @@ stores.inject(MyMetaStore, storeInstance);
32356
32423
  onFigureDeleted: Function,
32357
32424
  editFigureStyle: { type: Function, optional: true },
32358
32425
  isFullScreen: { type: Boolean, optional: true },
32426
+ openContextMenu: { type: Function, optional: true },
32359
32427
  };
32360
32428
  static components = { ChartDashboardMenu };
32361
32429
  onDoubleClick() {
@@ -32388,6 +32456,7 @@ stores.inject(MyMetaStore, storeInstance);
32388
32456
  figureUI: Object,
32389
32457
  onFigureDeleted: Function,
32390
32458
  editFigureStyle: { type: Function, optional: true },
32459
+ openContextMenu: { type: Function, optional: true },
32391
32460
  };
32392
32461
  static components = {};
32393
32462
  // ---------------------------------------------------------------------------
@@ -34190,8 +34259,11 @@ stores.inject(MyMetaStore, storeInstance);
34190
34259
  }
34191
34260
  const newSelection = this.contentHelper.getCurrentSelection();
34192
34261
  this.props.composerStore.stopComposerRangeSelection();
34193
- this.props.onComposerContentFocused();
34194
- this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34262
+ const isCurrentlyInactive = this.props.composerStore.editionMode === "inactive";
34263
+ this.props.onComposerContentFocused(newSelection);
34264
+ if (!isCurrentlyInactive) {
34265
+ this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34266
+ }
34195
34267
  this.processTokenAtCursor();
34196
34268
  }
34197
34269
  onDblClick() {
@@ -34686,13 +34758,6 @@ stores.inject(MyMetaStore, storeInstance);
34686
34758
  }
34687
34759
  }
34688
34760
  startEdition(text, selection) {
34689
- if (selection) {
34690
- const content = text || this.getComposerContent(this.getters.getActivePosition());
34691
- const validSelection = this.isSelectionValid(content.length, selection.start, selection.end);
34692
- if (!validSelection) {
34693
- return;
34694
- }
34695
- }
34696
34761
  const { col, row } = this.getters.getActivePosition();
34697
34762
  this.model.dispatch("SELECT_FIGURE", { figureId: null });
34698
34763
  this.model.dispatch("SCROLL_TO_CELL", { col, row });
@@ -34749,7 +34814,7 @@ stores.inject(MyMetaStore, storeInstance);
34749
34814
  // ---------------------------------------------------------------------------
34750
34815
  get currentContent() {
34751
34816
  if (this.editionMode === "inactive") {
34752
- return this.getComposerContent(this.getters.getActivePosition());
34817
+ return this.getComposerContent(this.getters.getActivePosition()).text;
34753
34818
  }
34754
34819
  return this._currentContent;
34755
34820
  }
@@ -34948,8 +35013,9 @@ stores.inject(MyMetaStore, storeInstance);
34948
35013
  this.sheetId = sheetId;
34949
35014
  this.row = row;
34950
35015
  this.editionMode = "editing";
34951
- this.initialContent = this.getComposerContent({ sheetId, col, row });
34952
- this.setContent(str || this.initialContent, selection);
35016
+ const { text, adjustedSelection } = this.getComposerContent({ sheetId, col, row }, selection);
35017
+ this.initialContent = text;
35018
+ this.setContent(str || this.initialContent, adjustedSelection ?? selection);
34953
35019
  this.colorIndexByRange = {};
34954
35020
  const zone = positionToZone({ col: this.col, row: this.row });
34955
35021
  this.captureSelection(zone, col, row);
@@ -35426,7 +35492,7 @@ stores.inject(MyMetaStore, storeInstance);
35426
35492
  constructor(get, args) {
35427
35493
  super(get);
35428
35494
  this.args = args;
35429
- this._currentContent = this.getComposerContent();
35495
+ this._currentContent = this.getComposerContent().text;
35430
35496
  }
35431
35497
  getAutoCompleteProviders() {
35432
35498
  const providersDefinitions = super.getAutoCompleteProviders();
@@ -35463,7 +35529,7 @@ stores.inject(MyMetaStore, storeInstance);
35463
35529
  })
35464
35530
  .join("");
35465
35531
  }
35466
- return localizeContent(content, this.getters.getLocale());
35532
+ return { text: localizeContent(content, this.getters.getLocale()) };
35467
35533
  }
35468
35534
  stopEdition() {
35469
35535
  this._stopEdition();
@@ -38683,6 +38749,74 @@ stores.inject(MyMetaStore, storeInstance);
38683
38749
  return path2D;
38684
38750
  }
38685
38751
 
38752
+ /**
38753
+ * Get the relative path between two files
38754
+ *
38755
+ * Eg.:
38756
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
38757
+ */
38758
+ function getRelativePath(from, to) {
38759
+ const fromPathParts = from.split("/");
38760
+ const toPathParts = to.split("/");
38761
+ let relPath = "";
38762
+ let startIndex = 0;
38763
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
38764
+ if (fromPathParts[i] === toPathParts[i]) {
38765
+ startIndex++;
38766
+ }
38767
+ else {
38768
+ relPath += "../";
38769
+ }
38770
+ }
38771
+ relPath += toPathParts.slice(startIndex).join("/");
38772
+ return relPath;
38773
+ }
38774
+ /**
38775
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
38776
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
38777
+ *
38778
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
38779
+ */
38780
+ function arrayToObject(array, indexOffset = 0) {
38781
+ const obj = {};
38782
+ for (let i = 0; i < array.length; i++) {
38783
+ if (array[i]) {
38784
+ obj[i + indexOffset] = array[i];
38785
+ }
38786
+ }
38787
+ return obj;
38788
+ }
38789
+ /**
38790
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
38791
+ * Replace with characters understandable by JS
38792
+ */
38793
+ function fixXlsxUnicode(str) {
38794
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
38795
+ return String.fromCharCode(parseInt(code, 16));
38796
+ });
38797
+ }
38798
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
38799
+ function getSheetDataHeader(sheetData, dimension, index) {
38800
+ if (dimension === "COL") {
38801
+ if (!sheetData.cols[index]) {
38802
+ sheetData.cols[index] = {};
38803
+ }
38804
+ return sheetData.cols[index];
38805
+ }
38806
+ if (!sheetData.rows[index]) {
38807
+ sheetData.rows[index] = {};
38808
+ }
38809
+ return sheetData.rows[index];
38810
+ }
38811
+ /** Prefix the string by "=" if the string looks like a formula */
38812
+ function prefixFormulaWithEqual(formula) {
38813
+ if (formula[0] === "=") {
38814
+ return formula;
38815
+ }
38816
+ const tokens = tokenize(formula);
38817
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
38818
+ }
38819
+
38686
38820
  /**
38687
38821
  * Map of the different types of conversions warnings and their name in error messages
38688
38822
  */
@@ -39205,66 +39339,6 @@ stores.inject(MyMetaStore, storeInstance);
39205
39339
  */
39206
39340
  const DEFAULT_SYSTEM_COLOR = "FF000000";
39207
39341
 
39208
- /**
39209
- * Get the relative path between two files
39210
- *
39211
- * Eg.:
39212
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
39213
- */
39214
- function getRelativePath(from, to) {
39215
- const fromPathParts = from.split("/");
39216
- const toPathParts = to.split("/");
39217
- let relPath = "";
39218
- let startIndex = 0;
39219
- for (let i = 0; i < fromPathParts.length - 1; i++) {
39220
- if (fromPathParts[i] === toPathParts[i]) {
39221
- startIndex++;
39222
- }
39223
- else {
39224
- relPath += "../";
39225
- }
39226
- }
39227
- relPath += toPathParts.slice(startIndex).join("/");
39228
- return relPath;
39229
- }
39230
- /**
39231
- * Convert an array of element into an object where the objects keys were the elements position in the array.
39232
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
39233
- *
39234
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
39235
- */
39236
- function arrayToObject(array, indexOffset = 0) {
39237
- const obj = {};
39238
- for (let i = 0; i < array.length; i++) {
39239
- if (array[i]) {
39240
- obj[i + indexOffset] = array[i];
39241
- }
39242
- }
39243
- return obj;
39244
- }
39245
- /**
39246
- * In xlsx we can have string with unicode characters with the format _x00fa_.
39247
- * Replace with characters understandable by JS
39248
- */
39249
- function fixXlsxUnicode(str) {
39250
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
39251
- return String.fromCharCode(parseInt(code, 16));
39252
- });
39253
- }
39254
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
39255
- function getSheetDataHeader(sheetData, dimension, index) {
39256
- if (dimension === "COL") {
39257
- if (!sheetData.cols[index]) {
39258
- sheetData.cols[index] = {};
39259
- }
39260
- return sheetData.cols[index];
39261
- }
39262
- if (!sheetData.rows[index]) {
39263
- sheetData.rows[index] = {};
39264
- }
39265
- return sheetData.rows[index];
39266
- }
39267
-
39268
39342
  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;
39269
39343
  /**
39270
39344
  * Convert excel format to o_spreadsheet format
@@ -39479,9 +39553,9 @@ stores.inject(MyMetaStore, storeInstance);
39479
39553
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
39480
39554
  continue;
39481
39555
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
39482
- values.push(prefixFormula(rule.formula[0]));
39556
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
39483
39557
  if (rule.formula.length === 2) {
39484
- values.push(prefixFormula(rule.formula[1]));
39558
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
39485
39559
  }
39486
39560
  break;
39487
39561
  }
@@ -39639,11 +39713,6 @@ stores.inject(MyMetaStore, storeInstance);
39639
39713
  ? ICON_SETS[iconSet].neutral
39640
39714
  : ICON_SETS[iconSet].good;
39641
39715
  }
39642
- /** Prefix the string by "=" if the string looks like a formula */
39643
- function prefixFormula(formula) {
39644
- const tokens = tokenize(formula);
39645
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
39646
- }
39647
39716
  // ---------------------------------------------------------------------------
39648
39717
  // Warnings
39649
39718
  // ---------------------------------------------------------------------------
@@ -40119,7 +40188,7 @@ stores.inject(MyMetaStore, storeInstance);
40119
40188
  dvRules.push(decimalRule);
40120
40189
  break;
40121
40190
  case "list":
40122
- const listRule = convertListrule(dvId++, dv);
40191
+ const listRule = convertListRule(dvId++, dv);
40123
40192
  dvRules.push(listRule);
40124
40193
  break;
40125
40194
  case "date":
@@ -40139,9 +40208,9 @@ stores.inject(MyMetaStore, storeInstance);
40139
40208
  return dvRules;
40140
40209
  }
40141
40210
  function convertDecimalRule(id, dv) {
40142
- const values = [dv.formula1.toString()];
40211
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
40143
40212
  if (dv.formula2) {
40144
- values.push(dv.formula2.toString());
40213
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
40145
40214
  }
40146
40215
  return {
40147
40216
  id: id.toString(),
@@ -40153,7 +40222,7 @@ stores.inject(MyMetaStore, storeInstance);
40153
40222
  },
40154
40223
  };
40155
40224
  }
40156
- function convertListrule(id, dv) {
40225
+ function convertListRule(id, dv) {
40157
40226
  const formula1 = dv.formula1.toString();
40158
40227
  const isRangeRule = rangeReference.test(formula1);
40159
40228
  return {
@@ -40169,9 +40238,9 @@ stores.inject(MyMetaStore, storeInstance);
40169
40238
  }
40170
40239
  function convertDateRule(id, dv) {
40171
40240
  let criterion;
40172
- const values = [dv.formula1.toString()];
40241
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
40173
40242
  if (dv.formula2) {
40174
- values.push(dv.formula2.toString());
40243
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
40175
40244
  criterion = {
40176
40245
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
40177
40246
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -40198,7 +40267,7 @@ stores.inject(MyMetaStore, storeInstance);
40198
40267
  isBlocking: dv.errorStyle !== "warning",
40199
40268
  criterion: {
40200
40269
  type: "customFormula",
40201
- values: [`=${dv.formula1.toString()}`],
40270
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
40202
40271
  },
40203
40272
  };
40204
40273
  }
@@ -45972,6 +46041,18 @@ stores.inject(MyMetaStore, storeInstance);
45972
46041
  get numberOfCells() {
45973
46042
  return this.rows.length * this.getNumberOfDataColumns();
45974
46043
  }
46044
+ getColumnDomainsAtDepth(depth) {
46045
+ if (depth < 0 || depth >= this.columns.length - 1) {
46046
+ return [];
46047
+ }
46048
+ return this.columns[depth].map((col) => this.getDomain(col)).filter((d) => d.length);
46049
+ }
46050
+ getRowDomainsAtDepth(depth) {
46051
+ if (depth < 0 || depth > this.maxIndent) {
46052
+ return [];
46053
+ }
46054
+ return this.rows.filter((row) => row.indent === depth + 1).map((row) => this.getDomain(row));
46055
+ }
45975
46056
  }
45976
46057
  const EMPTY_PIVOT_CELL = { type: "EMPTY" };
45977
46058
 
@@ -46964,6 +47045,109 @@ stores.inject(MyMetaStore, storeInstance);
46964
47045
  return areFieldValuesInGroups(definition, values, field, pivot.getFields());
46965
47046
  },
46966
47047
  };
47048
+ const toggleCollapsePivotGroupAction = {
47049
+ name: (env) => {
47050
+ const position = env.model.getters.getActivePosition();
47051
+ const pivotCellState = getPivotCellCollapseState(env.model.getters, position);
47052
+ if (pivotCellState.isPivotGroup) {
47053
+ return pivotCellState.isCollapsed ? _t("Expand") : _t("Collapse");
47054
+ }
47055
+ return "";
47056
+ },
47057
+ execute(env) {
47058
+ const position = env.model.getters.getActivePosition();
47059
+ togglePivotCollapse(position, env);
47060
+ },
47061
+ isVisible: (env) => {
47062
+ const position = env.model.getters.getActivePosition();
47063
+ const pivotCellState = getPivotCellCollapseState(env.model.getters, position);
47064
+ return pivotCellState.isPivotGroup;
47065
+ },
47066
+ };
47067
+ const collapseAllPivotGroupAction = {
47068
+ name: _t("Collapse all"),
47069
+ execute(env) {
47070
+ const position = env.model.getters.getActivePosition();
47071
+ const pivotCellState = getPivotCellCollapseState(env.model.getters, position);
47072
+ if (!pivotCellState.isPivotGroup) {
47073
+ return;
47074
+ }
47075
+ const { pivotCell, pivotId, siblingDomains } = pivotCellState;
47076
+ const definition = deepCopy(env.model.getters.getPivotCoreDefinition(pivotId));
47077
+ definition.collapsedDomains = definition.collapsedDomains || { COL: [], ROW: [] };
47078
+ const newCollapsed = [
47079
+ ...(definition.collapsedDomains[pivotCell.dimension] || []),
47080
+ ...siblingDomains,
47081
+ ];
47082
+ const filteredCollapsed = newCollapsed.filter((domain, index) => index === newCollapsed.findIndex((d) => deepEquals(d, domain)));
47083
+ definition.collapsedDomains[pivotCell.dimension] = filteredCollapsed;
47084
+ env.model.dispatch("UPDATE_PIVOT", { pivotId, pivot: definition });
47085
+ },
47086
+ isVisible: (env) => {
47087
+ const position = env.model.getters.getActivePosition();
47088
+ const pivotCellState = getPivotCellCollapseState(env.model.getters, position);
47089
+ if (!pivotCellState.isPivotGroup) {
47090
+ return false;
47091
+ }
47092
+ const { pivotCell, pivotId, siblingDomains } = pivotCellState;
47093
+ const definition = env.model.getters.getPivotCoreDefinition(pivotId);
47094
+ return !siblingDomains.every((domain) => (definition.collapsedDomains?.[pivotCell.dimension] || []).some((d) => deepEquals(d, domain)));
47095
+ },
47096
+ };
47097
+ const expandAllPivotGroupAction = {
47098
+ name: _t("Expand all"),
47099
+ execute(env) {
47100
+ const position = env.model.getters.getActivePosition();
47101
+ const pivotCellState = getPivotCellCollapseState(env.model.getters, position);
47102
+ if (!pivotCellState.isPivotGroup) {
47103
+ return;
47104
+ }
47105
+ const { pivotCell, pivotId, siblingDomains } = pivotCellState;
47106
+ const definition = deepCopy(env.model.getters.getPivotCoreDefinition(pivotId));
47107
+ definition.collapsedDomains = definition.collapsedDomains || { COL: [], ROW: [] };
47108
+ const domains = definition.collapsedDomains[pivotCell.dimension] || [];
47109
+ const filteredDomains = domains.filter((domain) => !siblingDomains.find((d) => deepEquals(d, domain)));
47110
+ definition.collapsedDomains[pivotCell.dimension] = filteredDomains;
47111
+ env.model.dispatch("UPDATE_PIVOT", { pivotId, pivot: definition });
47112
+ },
47113
+ isVisible: (env) => {
47114
+ const position = env.model.getters.getActivePosition();
47115
+ const pivotCellState = getPivotCellCollapseState(env.model.getters, position);
47116
+ if (!pivotCellState.isPivotGroup) {
47117
+ return false;
47118
+ }
47119
+ const { pivotCell, pivotId, siblingDomains } = pivotCellState;
47120
+ const definition = env.model.getters.getPivotCoreDefinition(pivotId);
47121
+ const collapsedDomains = definition.collapsedDomains?.[pivotCell.dimension] || [];
47122
+ return collapsedDomains.some((domain) => siblingDomains.some((d) => deepEquals(d, domain)));
47123
+ },
47124
+ };
47125
+ function getPivotCellCollapseState(getters, position) {
47126
+ if (!getters.isSpillPivotFormula(position)) {
47127
+ return { isPivotGroup: false };
47128
+ }
47129
+ const pivotCell = getters.getPivotCellFromPosition(position);
47130
+ const pivotId = getters.getPivotIdFromPosition(position);
47131
+ if (pivotCell.type !== "HEADER" || !pivotId || !pivotCell.domain.length) {
47132
+ return { isPivotGroup: false };
47133
+ }
47134
+ const definition = getters.getPivotCoreDefinition(pivotId);
47135
+ const isDashboard = getters.isDashboard();
47136
+ const fields = pivotCell.dimension === "COL" ? definition.columns : definition.rows;
47137
+ const hasIcon = !isDashboard && pivotCell.domain.length !== fields.length;
47138
+ if (!hasIcon) {
47139
+ return { isPivotGroup: false };
47140
+ }
47141
+ const domains = definition.collapsedDomains?.[pivotCell.dimension] ?? [];
47142
+ const isCollapsed = domains.some((domain) => deepEquals(domain, pivotCell.domain));
47143
+ const pivot = getters.getPivot(pivotId);
47144
+ const table = pivot.getExpandedTableStructure();
47145
+ const depth = pivotCell.domain.length - 1;
47146
+ const siblingDomains = pivotCell.dimension === "ROW"
47147
+ ? table.getRowDomainsAtDepth(depth)
47148
+ : table.getColumnDomainsAtDepth(depth);
47149
+ return { isPivotGroup: true, isCollapsed, pivotCell, pivotId, siblingDomains };
47150
+ }
46967
47151
  function canSortPivot(getters, position) {
46968
47152
  const pivotId = getters.getPivotIdFromPosition(position);
46969
47153
  if (!pivotId || !getters.isExistingPivot(pivotId) || !getters.isSpillPivotFormula(position)) {
@@ -47299,6 +47483,23 @@ stores.inject(MyMetaStore, storeInstance);
47299
47483
  sequence: 155,
47300
47484
  icon: "o-spreadsheet-Icon.MINUS_IN_BOX",
47301
47485
  ...ungroupPivotHeadersAction,
47486
+ })
47487
+ .add("collapse_pivot", {
47488
+ sequence: 156,
47489
+ name: _t("Expand/Collapse"),
47490
+ icon: "o-spreadsheet-Icon.COLLAPSE",
47491
+ })
47492
+ .addChild("toggle_collapse_pivot_cell", ["collapse_pivot"], {
47493
+ sequence: 10,
47494
+ ...toggleCollapsePivotGroupAction,
47495
+ })
47496
+ .addChild("collapse_all_pivot", ["collapse_pivot"], {
47497
+ sequence: 20,
47498
+ ...collapseAllPivotGroupAction,
47499
+ })
47500
+ .addChild("expand_all_pivot", ["collapse_pivot"], {
47501
+ sequence: 30,
47502
+ ...expandAllPivotGroupAction,
47302
47503
  })
47303
47504
  .add("pivot_sorting", {
47304
47505
  name: _t("Sort pivot"),
@@ -47923,6 +48124,7 @@ stores.inject(MyMetaStore, storeInstance);
47923
48124
  execute: (env) => {
47924
48125
  env.model.dispatch("UNFOLD_ALL_HEADER_GROUPS", { sheetId, dimension });
47925
48126
  },
48127
+ icon: "o-spreadsheet-Icon.EXPAND",
47926
48128
  },
47927
48129
  {
47928
48130
  id: "fold_all",
@@ -47930,6 +48132,7 @@ stores.inject(MyMetaStore, storeInstance);
47930
48132
  execute: (env) => {
47931
48133
  env.model.dispatch("FOLD_ALL_HEADER_GROUPS", { sheetId, dimension });
47932
48134
  },
48135
+ icon: "o-spreadsheet-Icon.COLLAPSE",
47933
48136
  },
47934
48137
  ]);
47935
48138
  }
@@ -47951,6 +48154,11 @@ stores.inject(MyMetaStore, storeInstance);
47951
48154
  const sheetId = env.model.getters.getActiveSheetId();
47952
48155
  interactiveToggleGroup(env, sheetId, dimension, start, end);
47953
48156
  },
48157
+ icon: (env) => {
48158
+ const sheetId = env.model.getters.getActiveSheetId();
48159
+ const groupIsFolded = env.model.getters.isGroupFolded(sheetId, dimension, start, end);
48160
+ return groupIsFolded ? "o-spreadsheet-Icon.EXPAND" : "o-spreadsheet-Icon.COLLAPSE";
48161
+ },
47954
48162
  },
47955
48163
  {
47956
48164
  id: "remove_group",
@@ -47959,6 +48167,7 @@ stores.inject(MyMetaStore, storeInstance);
47959
48167
  const sheetId = env.model.getters.getActiveSheetId();
47960
48168
  env.model.dispatch("UNGROUP_HEADERS", { sheetId, dimension, start, end });
47961
48169
  },
48170
+ icon: "o-spreadsheet-Icon.TRASH",
47962
48171
  separator: true,
47963
48172
  },
47964
48173
  ]);
@@ -48821,39 +49030,66 @@ stores.inject(MyMetaStore, storeInstance);
48821
49030
  this.model.dispatch("AUTOFILL_TABLE_COLUMN", { ...this.currentEditedCell });
48822
49031
  this.setContent("");
48823
49032
  }
48824
- getComposerContent(position) {
49033
+ getComposerContent(position, selection) {
48825
49034
  const locale = this.getters.getLocale();
48826
49035
  const cell = this.getters.getCell(position);
48827
49036
  if (cell?.isFormula) {
48828
49037
  const prettifiedContent = this.getPrettifiedFormula(cell);
48829
- return localizeFormula(prettifiedContent, locale);
49038
+ // when a formula is prettified (multi lines, indented), adapt the cursor position
49039
+ // to take into account line breaks and tabs
49040
+ function adjustCursorIndex(targetIndex) {
49041
+ let adjustedIndex = 0;
49042
+ let originalIndex = 0;
49043
+ while (originalIndex < targetIndex) {
49044
+ adjustedIndex++;
49045
+ const char = prettifiedContent[adjustedIndex];
49046
+ if (char !== "\n" && char !== "\t") {
49047
+ originalIndex++;
49048
+ }
49049
+ }
49050
+ return adjustedIndex;
49051
+ }
49052
+ let adjustedSelection = selection;
49053
+ if (selection) {
49054
+ adjustedSelection = {
49055
+ start: adjustCursorIndex(selection.start),
49056
+ end: adjustCursorIndex(selection.end),
49057
+ };
49058
+ }
49059
+ return {
49060
+ text: localizeFormula(prettifiedContent, locale),
49061
+ adjustedSelection,
49062
+ };
48830
49063
  }
48831
49064
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
48832
49065
  if (spreader) {
48833
- return "";
49066
+ return { text: "" };
49067
+ }
49068
+ if (cell?.content.startsWith("'")) {
49069
+ return { text: cell.content };
48834
49070
  }
48835
49071
  const { format, value, type, formattedValue } = this.getters.getEvaluatedCell(position);
48836
49072
  switch (type) {
48837
49073
  case CellValueType.empty:
48838
- return "";
49074
+ return { text: "" };
48839
49075
  case CellValueType.text:
48840
49076
  case CellValueType.error:
48841
- return value;
49077
+ return { text: value };
48842
49078
  case CellValueType.boolean:
48843
- return formattedValue;
49079
+ return { text: formattedValue };
48844
49080
  case CellValueType.number:
48845
49081
  if (format && isDateTimeFormat(format)) {
48846
49082
  if (parseDateTime(formattedValue, locale) !== null) {
48847
49083
  // formatted string can be parsed again
48848
- return formattedValue;
49084
+ return { text: formattedValue };
48849
49085
  }
48850
49086
  // display a simplified and parsable string otherwise
48851
49087
  const timeFormat = Number.isInteger(value)
48852
49088
  ? locale.dateFormat
48853
49089
  : getDateTimeFormat(locale);
48854
- return formatValue(value, { locale, format: timeFormat });
49090
+ return { text: formatValue(value, { locale, format: timeFormat }) };
48855
49091
  }
48856
- return this.numberComposerContent(value, format, locale);
49092
+ return { text: this.numberComposerContent(value, format, locale) };
48857
49093
  }
48858
49094
  }
48859
49095
  getPrettifiedFormula(cell) {
@@ -49000,8 +49236,9 @@ stores.inject(MyMetaStore, storeInstance);
49000
49236
  },
49001
49237
  focus: this.focus,
49002
49238
  isDefaultFocus: true,
49003
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
49239
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
49004
49240
  focusMode: "contentFocus",
49241
+ selection,
49005
49242
  }),
49006
49243
  onComposerCellFocused: (content) => this.composerFocusStore.focusComposer(this.composerInterface, {
49007
49244
  focusMode: "cellFocus",
@@ -55689,12 +55926,13 @@ stores.inject(MyMetaStore, storeInstance);
55689
55926
  onCloseSidePanel: { type: Function, optional: true },
55690
55927
  };
55691
55928
  state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
55929
+ editingSheetId;
55692
55930
  setup() {
55931
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
55693
55932
  if (this.props.rule) {
55694
- const sheetId = this.env.model.getters.getActiveSheetId();
55695
55933
  this.state.rule = {
55696
55934
  ...this.props.rule,
55697
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
55935
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
55698
55936
  };
55699
55937
  this.state.rule.criterion.type = this.props.rule.criterion.type;
55700
55938
  }
@@ -55728,7 +55966,6 @@ stores.inject(MyMetaStore, storeInstance);
55728
55966
  const locale = this.env.model.getters.getLocale();
55729
55967
  const criterion = rule.criterion;
55730
55968
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
55731
- const sheetId = this.env.model.getters.getActiveSheetId();
55732
55969
  const values = criterion.values
55733
55970
  .slice(0, criterionEvaluator.numberOfValues(criterion))
55734
55971
  .map((value) => value?.trim())
@@ -55736,8 +55973,8 @@ stores.inject(MyMetaStore, storeInstance);
55736
55973
  .map((value) => canonicalizeContent(value, locale));
55737
55974
  rule.criterion = { ...criterion, values };
55738
55975
  return {
55739
- sheetId,
55740
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
55976
+ sheetId: this.editingSheetId,
55977
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
55741
55978
  rule,
55742
55979
  };
55743
55980
  }
@@ -60847,7 +61084,7 @@ stores.inject(MyMetaStore, storeInstance);
60847
61084
  (typeof parsedValue === "number"
60848
61085
  ? detectDateFormat(content, locale) || detectNumberFormat(content)
60849
61086
  : undefined);
60850
- if (!isTextFormat(format) && !isEvaluationError(content)) {
61087
+ if (!isTextFormat(format) && !content.startsWith("'") && !isEvaluationError(content)) {
60851
61088
  content = toString(parsedValue);
60852
61089
  }
60853
61090
  return {
@@ -66745,7 +66982,7 @@ stores.inject(MyMetaStore, storeInstance);
66745
66982
  * in the correct order they should be evaluated.
66746
66983
  * This is called a topological ordering (excluding cycles)
66747
66984
  */
66748
- getCellsDependingOn(ranges) {
66985
+ getCellsDependingOn(ranges, ignore) {
66749
66986
  const visited = this.createEmptyPositionSet();
66750
66987
  const queue = Array.from(ranges).reverse();
66751
66988
  while (queue.length > 0) {
@@ -66760,7 +66997,7 @@ stores.inject(MyMetaStore, storeInstance);
66760
66997
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
66761
66998
  const nextInQueue = {};
66762
66999
  for (const position of impactedPositions) {
66763
- if (!visited.has(position)) {
67000
+ if (!visited.has(position) && !ignore.has(position)) {
66764
67001
  if (!nextInQueue[position.sheetId]) {
66765
67002
  nextInQueue[position.sheetId] = [];
66766
67003
  }
@@ -67318,7 +67555,7 @@ stores.inject(MyMetaStore, storeInstance);
67318
67555
  }
67319
67556
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
67320
67557
  // the result matrix is split in 2 zones to exclude the array formula position
67321
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
67558
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
67322
67559
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
67323
67560
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
67324
67561
  }
@@ -67436,7 +67673,7 @@ stores.inject(MyMetaStore, storeInstance);
67436
67673
  for (const sheetId in zonesBySheetIds) {
67437
67674
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
67438
67675
  }
67439
- return this.formulaDependencies().getCellsDependingOn(ranges);
67676
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
67440
67677
  }
67441
67678
  }
67442
67679
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -68703,32 +68940,6 @@ stores.inject(MyMetaStore, storeInstance);
68703
68940
  }
68704
68941
  return undefined;
68705
68942
  });
68706
- function togglePivotCollapse(position, env) {
68707
- const pivotCell = env.model.getters.getPivotCellFromPosition(position);
68708
- const pivotId = env.model.getters.getPivotIdFromPosition(position);
68709
- if (!pivotId || pivotCell.type !== "HEADER") {
68710
- return;
68711
- }
68712
- const definition = env.model.getters.getPivotCoreDefinition(pivotId);
68713
- const collapsedDomains = definition.collapsedDomains?.[pivotCell.dimension]
68714
- ? [...definition.collapsedDomains[pivotCell.dimension]]
68715
- : [];
68716
- const index = collapsedDomains.findIndex((domain) => deepEquals(domain, pivotCell.domain));
68717
- if (index !== -1) {
68718
- collapsedDomains.splice(index, 1);
68719
- }
68720
- else {
68721
- collapsedDomains.push(pivotCell.domain);
68722
- }
68723
- const newDomains = definition.collapsedDomains
68724
- ? { ...definition.collapsedDomains }
68725
- : { COL: [], ROW: [] };
68726
- newDomains[pivotCell.dimension] = collapsedDomains;
68727
- env.model.dispatch("UPDATE_PIVOT", {
68728
- pivotId,
68729
- pivot: { ...definition, collapsedDomains: newDomains },
68730
- });
68731
- }
68732
68943
  iconsOnCellRegistry.add("pivot_dashboard_sorting", (getters, position) => {
68733
68944
  if (!getters.isDashboard()) {
68734
68945
  return undefined;
@@ -68947,7 +69158,8 @@ stores.inject(MyMetaStore, storeInstance);
68947
69158
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
68948
69159
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
68949
69160
  if (!parentSpreadingCell) {
68950
- return false;
69161
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
69162
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
68951
69163
  }
68952
69164
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
68953
69165
  return true;
@@ -79813,6 +80025,7 @@ stores.inject(MyMetaStore, storeInstance);
79813
80025
  static components = { Menu };
79814
80026
  rootItems = topbarMenuRegistry.getMenuItems();
79815
80027
  menuRef = owl.useRef("menu");
80028
+ containerRef = owl.useRef("container");
79816
80029
  state = owl.useState({
79817
80030
  menuItems: this.rootItems,
79818
80031
  title: _t("Menu Bar"),
@@ -79820,6 +80033,7 @@ stores.inject(MyMetaStore, storeInstance);
79820
80033
  });
79821
80034
  setup() {
79822
80035
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
80036
+ owl.onMounted(this.updateShadows);
79823
80037
  }
79824
80038
  onExternalClick(ev) {
79825
80039
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -79832,6 +80046,7 @@ stores.inject(MyMetaStore, storeInstance);
79832
80046
  this.state.parentState = { ...this.state };
79833
80047
  this.state.menuItems = children;
79834
80048
  this.state.title = menu.name(this.env);
80049
+ this.containerRef.el?.scrollTo({ top: 0 });
79835
80050
  }
79836
80051
  else {
79837
80052
  this.state.menuItems = this.rootItems;
@@ -79853,6 +80068,19 @@ stores.inject(MyMetaStore, storeInstance);
79853
80068
  height: `${this.props.height}px`,
79854
80069
  });
79855
80070
  }
80071
+ updateShadows() {
80072
+ if (!this.containerRef.el) {
80073
+ return;
80074
+ }
80075
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
80076
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
80077
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
80078
+ this.containerRef.el.classList.add("scroll-bottom");
80079
+ }
80080
+ if (this.containerRef.el.scrollTop > 0) {
80081
+ this.containerRef.el.classList.add("scroll-top");
80082
+ }
80083
+ }
79856
80084
  onClickBack() {
79857
80085
  if (!this.state.parentState) {
79858
80086
  this.props.onClose();
@@ -79861,6 +80089,7 @@ stores.inject(MyMetaStore, storeInstance);
79861
80089
  this.state.menuItems = this.state.parentState.menuItems;
79862
80090
  this.state.title = this.state.parentState.title;
79863
80091
  this.state.parentState = this.state.parentState.parentState;
80092
+ this.containerRef.el?.scrollTo({ top: 0 });
79864
80093
  }
79865
80094
  get backTitle() {
79866
80095
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -79912,7 +80141,9 @@ stores.inject(MyMetaStore, storeInstance);
79912
80141
  : "inactive";
79913
80142
  }
79914
80143
  get showFxIcon() {
79915
- return this.focus === "inactive" && !this.composerStore.currentContent;
80144
+ return (this.focus === "inactive" &&
80145
+ !this.composerStore.currentContent &&
80146
+ !this.composerStore.placeholder);
79916
80147
  }
79917
80148
  get rect() {
79918
80149
  return this.composerRef.el
@@ -79929,8 +80160,9 @@ stores.inject(MyMetaStore, storeInstance);
79929
80160
  },
79930
80161
  focus: this.focus,
79931
80162
  composerStore: this.composerStore,
79932
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
80163
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
79933
80164
  focusMode: "contentFocus",
80165
+ selection,
79934
80166
  }),
79935
80167
  isDefaultFocus: false,
79936
80168
  inputStyle: cssPropertiesToCss({
@@ -79938,6 +80170,7 @@ stores.inject(MyMetaStore, storeInstance);
79938
80170
  "max-height": `130px`,
79939
80171
  }),
79940
80172
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
80173
+ placeholder: this.composerStore.placeholder,
79941
80174
  };
79942
80175
  }
79943
80176
  get symbols() {
@@ -79983,7 +80216,9 @@ stores.inject(MyMetaStore, storeInstance);
79983
80216
  : "inactive";
79984
80217
  }
79985
80218
  get showFxIcon() {
79986
- return this.focus === "inactive" && !this.composerStore.currentContent;
80219
+ return (this.focus === "inactive" &&
80220
+ !this.composerStore.currentContent &&
80221
+ !this.composerStore.placeholder);
79987
80222
  }
79988
80223
  get composerStyle() {
79989
80224
  const style = {
@@ -85785,9 +86020,9 @@ stores.inject(MyMetaStore, storeInstance);
85785
86020
  exports.tokenize = tokenize;
85786
86021
 
85787
86022
 
85788
- __info__.version = "19.1.0-alpha.4";
85789
- __info__.date = "2025-10-07T10:03:20.917Z";
85790
- __info__.hash = "b7cfed8";
86023
+ __info__.version = "19.1.0-alpha.5";
86024
+ __info__.date = "2025-10-16T06:39:55.925Z";
86025
+ __info__.hash = "1a0e3d5";
85791
86026
 
85792
86027
 
85793
86028
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);