@odoo/o-spreadsheet 19.0.4 → 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.4
6
- * @date 2025-09-23T12:37:28.362Z
7
- * @hash 87b774d
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) {
@@ -1383,9 +1383,7 @@
1383
1383
  return newArray;
1384
1384
  }
1385
1385
  function insertItemsAtIndex(array, items, index) {
1386
- const newArray = [...array];
1387
- newArray.splice(index, 0, ...items);
1388
- return newArray;
1386
+ return array.slice(0, index).concat(items).concat(array.slice(index));
1389
1387
  }
1390
1388
  function replaceItemAtIndex(array, newItem, index) {
1391
1389
  const newArray = [...array];
@@ -4516,7 +4514,17 @@
4516
4514
  return toMatrix(data).map((row) => {
4517
4515
  return row.map((cell) => {
4518
4516
  if (typeof cell.value !== "number") {
4519
- 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);
4520
4528
  }
4521
4529
  return cell.value;
4522
4530
  });
@@ -5635,7 +5643,7 @@
5635
5643
  * Replace in place tokens "mm" and "m" that denote minutes in date format with "MM" to avoid confusion with months.
5636
5644
  *
5637
5645
  * As per OpenXML specification, in date formats if a date token "m" or "mm" is followed by a date token "s" or
5638
- * preceded by a data token "h", then it's not a month but an minute.
5646
+ * preceded by a data token "h", then it's not a month but a minute.
5639
5647
  */
5640
5648
  function convertTokensToMinutesInDateFormat(tokens) {
5641
5649
  const dateParts = tokens.filter((token) => token.type === "DATE_PART");
@@ -5678,6 +5686,9 @@
5678
5686
  case "REPEATED_CHAR":
5679
5687
  format += "*" + token.value;
5680
5688
  break;
5689
+ case "DATE_PART":
5690
+ format += token.value === "MM" ? "mm" : token.value; // Convert "MM" back to "mm" for minutes
5691
+ break;
5681
5692
  default:
5682
5693
  format += token.value;
5683
5694
  }
@@ -9528,7 +9539,7 @@
9528
9539
  pasteCell(origin, target, clipboardOption) {
9529
9540
  const { sheetId, col, row } = target;
9530
9541
  const targetCell = this.getters.getEvaluatedCell(target);
9531
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9542
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9532
9543
  if (clipboardOption?.pasteOption === "asValue") {
9533
9544
  this.dispatch("UPDATE_CELL", {
9534
9545
  ...target,
@@ -13901,7 +13912,7 @@ stores.inject(MyMetaStore, storeInstance);
13901
13912
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13902
13913
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13903
13914
  }
13904
- return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "the first argument (known_data_y)")), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
13915
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13905
13916
  },
13906
13917
  };
13907
13918
  // -----------------------------------------------------------------------------
@@ -13974,7 +13985,7 @@ stores.inject(MyMetaStore, storeInstance);
13974
13985
  if (dataY.length === 0 || dataY[0].length === 0) {
13975
13986
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13976
13987
  }
13977
- 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));
13978
13989
  },
13979
13990
  isExported: true,
13980
13991
  };
@@ -13993,7 +14004,7 @@ stores.inject(MyMetaStore, storeInstance);
13993
14004
  if (dataY.length === 0 || dataY[0].length === 0) {
13994
14005
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13995
14006
  }
13996
- 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));
13997
14008
  for (let i = 0; i < coeffs.length; i++) {
13998
14009
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13999
14010
  }
@@ -14614,7 +14625,7 @@ stores.inject(MyMetaStore, storeInstance);
14614
14625
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
14615
14626
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
14616
14627
  }
14617
- return predictLinearValues(toNumberMatrix(knownDataY, "the first argument (known_data_y)"), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
14628
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14618
14629
  },
14619
14630
  };
14620
14631
  // -----------------------------------------------------------------------------
@@ -23135,6 +23146,10 @@ stores.inject(MyMetaStore, storeInstance);
23135
23146
  }
23136
23147
  const ctx = chart.ctx;
23137
23148
  ctx.save();
23149
+ const { left, top, height, width } = chart.chartArea;
23150
+ ctx.beginPath();
23151
+ ctx.rect(left, top, width, height);
23152
+ ctx.clip();
23138
23153
  ctx.textAlign = "center";
23139
23154
  ctx.textBaseline = "middle";
23140
23155
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -24178,7 +24193,7 @@ stores.inject(MyMetaStore, storeInstance);
24178
24193
  this.chart.update();
24179
24194
  }
24180
24195
  hasChartDataChanged() {
24181
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
24196
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
24182
24197
  }
24183
24198
  enableAnimationInChartData(chartData) {
24184
24199
  return {
@@ -24186,6 +24201,17 @@ stores.inject(MyMetaStore, storeInstance);
24186
24201
  options: { ...chartData.options, animation: { animateRotate: true } },
24187
24202
  };
24188
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
+ }
24189
24215
  get animationChartId() {
24190
24216
  return this.props.isFullScreen ? this.props.chartId + "-fullscreen" : this.props.chartId;
24191
24217
  }
@@ -24853,6 +24879,7 @@ stores.inject(MyMetaStore, storeInstance);
24853
24879
  static template = "o-spreadsheet-ScorecardChart";
24854
24880
  static props = {
24855
24881
  chartId: String,
24882
+ isFullScreen: { type: Boolean, optional: true },
24856
24883
  };
24857
24884
  canvas = owl.useRef("chartContainer");
24858
24885
  get runtime() {
@@ -25608,6 +25635,7 @@ stores.inject(MyMetaStore, storeInstance);
25608
25635
  parser: luxonFormat,
25609
25636
  displayFormats,
25610
25637
  unit: timeUnit ?? false,
25638
+ tooltipFormat: luxonFormat,
25611
25639
  };
25612
25640
  }
25613
25641
  /**
@@ -26676,6 +26704,7 @@ stores.inject(MyMetaStore, storeInstance);
26676
26704
  };
26677
26705
  Object.assign(scales.x, axis);
26678
26706
  scales.x.ticks.maxTicksLimit = 15;
26707
+ delete scales?.x?.ticks?.callback;
26679
26708
  }
26680
26709
  else if (axisType === "linear") {
26681
26710
  scales.x.type = "linear";
@@ -27616,26 +27645,6 @@ stores.inject(MyMetaStore, storeInstance);
27616
27645
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
27617
27646
  }
27618
27647
 
27619
- class FullScreenChartStore extends SpreadsheetStore {
27620
- mutators = ["toggleFullScreenChart"];
27621
- fullScreenFigure = undefined;
27622
- toggleFullScreenChart(figureId) {
27623
- if (this.fullScreenFigure?.id === figureId) {
27624
- this.fullScreenFigure = undefined;
27625
- }
27626
- else {
27627
- this.makeFullScreen(figureId);
27628
- }
27629
- }
27630
- makeFullScreen(figureId) {
27631
- const sheetId = this.getters.getActiveSheetId();
27632
- const figure = this.getters.getFigure(sheetId, figureId);
27633
- if (figure) {
27634
- this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
27635
- }
27636
- }
27637
- }
27638
-
27639
27648
  const TREND_LINE_AXES_IDS = [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID];
27640
27649
  const ZOOMABLE_AXIS_IDS = ["x", ...TREND_LINE_AXES_IDS];
27641
27650
  class ZoomableChartStore extends SpreadsheetStore {
@@ -27807,7 +27816,6 @@ stores.inject(MyMetaStore, storeInstance);
27807
27816
  class ZoomableChartJsComponent extends ChartJsComponent {
27808
27817
  static template = "o-spreadsheet-ZoomableChartJsComponent";
27809
27818
  store;
27810
- fullScreenChartStore;
27811
27819
  masterChartCanvas = owl.useRef("masterChartCanvas");
27812
27820
  masterChart;
27813
27821
  mode;
@@ -27818,7 +27826,6 @@ stores.inject(MyMetaStore, storeInstance);
27818
27826
  removeEventListeners = () => { };
27819
27827
  setup() {
27820
27828
  this.store = useStore(ZoomableChartStore);
27821
- this.fullScreenChartStore = useStore(FullScreenChartStore);
27822
27829
  super.setup();
27823
27830
  }
27824
27831
  unmount() {
@@ -27833,12 +27840,8 @@ stores.inject(MyMetaStore, storeInstance);
27833
27840
  `;
27834
27841
  }
27835
27842
  get sliceable() {
27836
- if (this.env.isDashboard()) {
27837
- const fullScreenFigureId = this.fullScreenChartStore.fullScreenFigure?.id;
27838
- const chartFigureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
27839
- if (fullScreenFigureId === chartFigureId) {
27840
- return true;
27841
- }
27843
+ if (this.props.isFullScreen) {
27844
+ return true;
27842
27845
  }
27843
27846
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27844
27847
  return ("zoomable" in definition && definition?.zoomable) ?? false;
@@ -27901,9 +27904,6 @@ stores.inject(MyMetaStore, storeInstance);
27901
27904
  const xMax = Math.max(...xValues);
27902
27905
  return { xMin, xMax };
27903
27906
  }
27904
- get shouldAnimate() {
27905
- return this.env.model.getters.isDashboard() && !this.sliceable;
27906
- }
27907
27907
  createChart(chartRuntime) {
27908
27908
  const chartData = chartRuntime.chartJsConfig;
27909
27909
  this.isBarChart = chartData.type === "bar";
@@ -27922,6 +27922,9 @@ stores.inject(MyMetaStore, storeInstance);
27922
27922
  const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
27923
27923
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27924
27924
  this.resetAxesLimits();
27925
+ if (this.chart?.options) {
27926
+ this.chart.options.animation = false;
27927
+ }
27925
27928
  }
27926
27929
  updateChartJs(chartRuntime) {
27927
27930
  const chartData = chartRuntime.chartJsConfig;
@@ -27955,6 +27958,9 @@ stores.inject(MyMetaStore, storeInstance);
27955
27958
  }
27956
27959
  }
27957
27960
  this.resetAxesLimits();
27961
+ if (this.chart?.options) {
27962
+ this.chart.options.animation = false;
27963
+ }
27958
27964
  }
27959
27965
  resetAxesLimits() {
27960
27966
  if (!this.chart) {
@@ -28052,7 +28058,7 @@ stores.inject(MyMetaStore, storeInstance);
28052
28058
  onPointerDownInMasterChart(ev) {
28053
28059
  this.removeEventListeners();
28054
28060
  const position = ev.offsetX;
28055
- if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
28061
+ if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
28056
28062
  return;
28057
28063
  }
28058
28064
  const { left, right, top, bottom } = this.masterChart.chartArea;
@@ -31610,6 +31616,26 @@ stores.inject(MyMetaStore, storeInstance);
31610
31616
  return matchedChart.displayName;
31611
31617
  }
31612
31618
 
31619
+ class FullScreenFigureStore extends SpreadsheetStore {
31620
+ mutators = ["toggleFullScreenFigure"];
31621
+ fullScreenFigure = undefined;
31622
+ toggleFullScreenFigure(figureId) {
31623
+ if (this.fullScreenFigure?.id === figureId) {
31624
+ this.fullScreenFigure = undefined;
31625
+ }
31626
+ else {
31627
+ this.makeFullScreen(figureId);
31628
+ }
31629
+ }
31630
+ makeFullScreen(figureId) {
31631
+ const sheetId = this.getters.getActiveSheetId();
31632
+ const figure = this.getters.getFigure(sheetId, figureId);
31633
+ if (figure) {
31634
+ this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
31635
+ }
31636
+ }
31637
+ }
31638
+
31613
31639
  /**
31614
31640
  * Repeatedly calls a callback function with a time delay between calls.
31615
31641
  */
@@ -32347,12 +32373,13 @@ stores.inject(MyMetaStore, storeInstance);
32347
32373
  class ChartDashboardMenu extends owl.Component {
32348
32374
  static template = "o-spreadsheet-ChartDashboardMenu";
32349
32375
  static components = { MenuPopover };
32350
- static props = { chartId: String };
32376
+ static props = { chartId: String, hasFullScreenButton: { type: Boolean, optional: true } };
32377
+ static defaultProps = { hasFullScreenButton: true };
32351
32378
  fullScreenFigureStore;
32352
32379
  menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
32353
32380
  setup() {
32354
32381
  super.setup();
32355
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
32382
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32356
32383
  }
32357
32384
  getMenuItems() {
32358
32385
  return [this.fullScreenMenuItem].filter(isDefined);
@@ -32363,11 +32390,14 @@ 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
  }
32370
32397
  get fullScreenMenuItem() {
32398
+ if (!this.props.hasFullScreenButton) {
32399
+ return undefined;
32400
+ }
32371
32401
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
32372
32402
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32373
32403
  if (definition.type === "scorecard") {
@@ -32379,7 +32409,7 @@ stores.inject(MyMetaStore, storeInstance);
32379
32409
  label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
32380
32410
  class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
32381
32411
  onClick: () => {
32382
- this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32412
+ this.fullScreenFigureStore.toggleFullScreenFigure(figureId);
32383
32413
  },
32384
32414
  };
32385
32415
  }
@@ -32391,6 +32421,8 @@ stores.inject(MyMetaStore, storeInstance);
32391
32421
  figureUI: Object,
32392
32422
  onFigureDeleted: Function,
32393
32423
  editFigureStyle: { type: Function, optional: true },
32424
+ isFullScreen: { type: Boolean, optional: true },
32425
+ openContextMenu: { type: Function, optional: true },
32394
32426
  };
32395
32427
  static components = { ChartDashboardMenu, MenuPopover };
32396
32428
  carouselTabsRef = owl.useRef("carouselTabs");
@@ -32398,8 +32430,10 @@ stores.inject(MyMetaStore, storeInstance);
32398
32430
  menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
32399
32431
  hiddenItems = [];
32400
32432
  animationStore;
32433
+ fullScreenFigureStore;
32401
32434
  setup() {
32402
32435
  this.animationStore = useStore(ChartAnimationStore);
32436
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32403
32437
  owl.useEffect(() => {
32404
32438
  if (this.selectedCarouselItem?.type === "carouselDataView") {
32405
32439
  this.props.editFigureStyle?.({ "pointer-events": "none" });
@@ -32446,7 +32480,8 @@ stores.inject(MyMetaStore, storeInstance);
32446
32480
  item,
32447
32481
  });
32448
32482
  if (item.type === "chart") {
32449
- this.animationStore?.enableAnimationForChart(item.chartId);
32483
+ const animationChartId = item.chartId + (this.props.isFullScreen ? "-fullscreen" : "");
32484
+ this.animationStore?.enableAnimationForChart(animationChartId);
32450
32485
  }
32451
32486
  }
32452
32487
  get headerStyle() {
@@ -32510,6 +32545,23 @@ stores.inject(MyMetaStore, storeInstance);
32510
32545
  this.menuState.anchorRect = rect;
32511
32546
  this.menuState.menuItems = createActions(menuItems);
32512
32547
  }
32548
+ toggleFullScreen() {
32549
+ if (this.selectedCarouselItem?.type === "chart") {
32550
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.props.figureUI.id);
32551
+ }
32552
+ }
32553
+ get fullScreenButtonTitle() {
32554
+ return this.props.isFullScreen ? _t("Exit Full Screen") : _t("Full Screen");
32555
+ }
32556
+ get visibleCarouselItems() {
32557
+ return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32558
+ }
32559
+ openContextMenu(event) {
32560
+ const target = event.currentTarget;
32561
+ if (target) {
32562
+ this.props.openContextMenu?.(getBoundingRectAsPOJO(target));
32563
+ }
32564
+ }
32513
32565
  }
32514
32566
 
32515
32567
  class ChartFigure extends owl.Component {
@@ -32518,6 +32570,8 @@ stores.inject(MyMetaStore, storeInstance);
32518
32570
  figureUI: Object,
32519
32571
  onFigureDeleted: Function,
32520
32572
  editFigureStyle: { type: Function, optional: true },
32573
+ isFullScreen: { type: Boolean, optional: true },
32574
+ openContextMenu: { type: Function, optional: true },
32521
32575
  };
32522
32576
  static components = { ChartDashboardMenu };
32523
32577
  onDoubleClick() {
@@ -32550,6 +32604,7 @@ stores.inject(MyMetaStore, storeInstance);
32550
32604
  figureUI: Object,
32551
32605
  onFigureDeleted: Function,
32552
32606
  editFigureStyle: { type: Function, optional: true },
32607
+ openContextMenu: { type: Function, optional: true },
32553
32608
  };
32554
32609
  static components = {};
32555
32610
  // ---------------------------------------------------------------------------
@@ -34601,8 +34656,11 @@ stores.inject(MyMetaStore, storeInstance);
34601
34656
  }
34602
34657
  const newSelection = this.contentHelper.getCurrentSelection();
34603
34658
  this.props.composerStore.stopComposerRangeSelection();
34604
- this.props.onComposerContentFocused();
34605
- 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
+ }
34606
34664
  this.processTokenAtCursor();
34607
34665
  }
34608
34666
  onDblClick() {
@@ -35097,13 +35155,6 @@ stores.inject(MyMetaStore, storeInstance);
35097
35155
  }
35098
35156
  }
35099
35157
  startEdition(text, selection) {
35100
- if (selection) {
35101
- const content = text || this.getComposerContent(this.getters.getActivePosition());
35102
- const validSelection = this.isSelectionValid(content.length, selection.start, selection.end);
35103
- if (!validSelection) {
35104
- return;
35105
- }
35106
- }
35107
35158
  const { col, row } = this.getters.getActivePosition();
35108
35159
  this.model.dispatch("SELECT_FIGURE", { figureId: null });
35109
35160
  this.model.dispatch("SCROLL_TO_CELL", { col, row });
@@ -35160,7 +35211,7 @@ stores.inject(MyMetaStore, storeInstance);
35160
35211
  // ---------------------------------------------------------------------------
35161
35212
  get currentContent() {
35162
35213
  if (this.editionMode === "inactive") {
35163
- return this.getComposerContent(this.getters.getActivePosition());
35214
+ return this.getComposerContent(this.getters.getActivePosition()).text;
35164
35215
  }
35165
35216
  return this._currentContent;
35166
35217
  }
@@ -35359,8 +35410,9 @@ stores.inject(MyMetaStore, storeInstance);
35359
35410
  this.sheetId = sheetId;
35360
35411
  this.row = row;
35361
35412
  this.editionMode = "editing";
35362
- this.initialContent = this.getComposerContent({ sheetId, col, row });
35363
- 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);
35364
35416
  this.colorIndexByRange = {};
35365
35417
  const zone = positionToZone({ col: this.col, row: this.row });
35366
35418
  this.captureSelection(zone, col, row);
@@ -35837,7 +35889,7 @@ stores.inject(MyMetaStore, storeInstance);
35837
35889
  constructor(get, args) {
35838
35890
  super(get);
35839
35891
  this.args = args;
35840
- this._currentContent = this.getComposerContent();
35892
+ this._currentContent = this.getComposerContent().text;
35841
35893
  }
35842
35894
  getAutoCompleteProviders() {
35843
35895
  const providersDefinitions = super.getAutoCompleteProviders();
@@ -35874,7 +35926,7 @@ stores.inject(MyMetaStore, storeInstance);
35874
35926
  })
35875
35927
  .join("");
35876
35928
  }
35877
- return localizeContent(content, this.getters.getLocale());
35929
+ return { text: localizeContent(content, this.getters.getLocale()) };
35878
35930
  }
35879
35931
  stopEdition() {
35880
35932
  this._stopEdition();
@@ -39552,6 +39604,74 @@ stores.inject(MyMetaStore, storeInstance);
39552
39604
  return path2D;
39553
39605
  }
39554
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
+
39555
39675
  /**
39556
39676
  * Map of the different types of conversions warnings and their name in error messages
39557
39677
  */
@@ -40074,66 +40194,6 @@ stores.inject(MyMetaStore, storeInstance);
40074
40194
  */
40075
40195
  const DEFAULT_SYSTEM_COLOR = "FF000000";
40076
40196
 
40077
- /**
40078
- * Get the relative path between two files
40079
- *
40080
- * Eg.:
40081
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
40082
- */
40083
- function getRelativePath(from, to) {
40084
- const fromPathParts = from.split("/");
40085
- const toPathParts = to.split("/");
40086
- let relPath = "";
40087
- let startIndex = 0;
40088
- for (let i = 0; i < fromPathParts.length - 1; i++) {
40089
- if (fromPathParts[i] === toPathParts[i]) {
40090
- startIndex++;
40091
- }
40092
- else {
40093
- relPath += "../";
40094
- }
40095
- }
40096
- relPath += toPathParts.slice(startIndex).join("/");
40097
- return relPath;
40098
- }
40099
- /**
40100
- * Convert an array of element into an object where the objects keys were the elements position in the array.
40101
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
40102
- *
40103
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
40104
- */
40105
- function arrayToObject(array, indexOffset = 0) {
40106
- const obj = {};
40107
- for (let i = 0; i < array.length; i++) {
40108
- if (array[i]) {
40109
- obj[i + indexOffset] = array[i];
40110
- }
40111
- }
40112
- return obj;
40113
- }
40114
- /**
40115
- * In xlsx we can have string with unicode characters with the format _x00fa_.
40116
- * Replace with characters understandable by JS
40117
- */
40118
- function fixXlsxUnicode(str) {
40119
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
40120
- return String.fromCharCode(parseInt(code, 16));
40121
- });
40122
- }
40123
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
40124
- function getSheetDataHeader(sheetData, dimension, index) {
40125
- if (dimension === "COL") {
40126
- if (!sheetData.cols[index]) {
40127
- sheetData.cols[index] = {};
40128
- }
40129
- return sheetData.cols[index];
40130
- }
40131
- if (!sheetData.rows[index]) {
40132
- sheetData.rows[index] = {};
40133
- }
40134
- return sheetData.rows[index];
40135
- }
40136
-
40137
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;
40138
40198
  /**
40139
40199
  * Convert excel format to o_spreadsheet format
@@ -40348,9 +40408,9 @@ stores.inject(MyMetaStore, storeInstance);
40348
40408
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
40349
40409
  continue;
40350
40410
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
40351
- values.push(prefixFormula(rule.formula[0]));
40411
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
40352
40412
  if (rule.formula.length === 2) {
40353
- values.push(prefixFormula(rule.formula[1]));
40413
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
40354
40414
  }
40355
40415
  break;
40356
40416
  }
@@ -40508,11 +40568,6 @@ stores.inject(MyMetaStore, storeInstance);
40508
40568
  ? ICON_SETS[iconSet].neutral
40509
40569
  : ICON_SETS[iconSet].good;
40510
40570
  }
40511
- /** Prefix the string by "=" if the string looks like a formula */
40512
- function prefixFormula(formula) {
40513
- const tokens = tokenize(formula);
40514
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
40515
- }
40516
40571
  // ---------------------------------------------------------------------------
40517
40572
  // Warnings
40518
40573
  // ---------------------------------------------------------------------------
@@ -40988,7 +41043,7 @@ stores.inject(MyMetaStore, storeInstance);
40988
41043
  dvRules.push(decimalRule);
40989
41044
  break;
40990
41045
  case "list":
40991
- const listRule = convertListrule(dvId++, dv);
41046
+ const listRule = convertListRule(dvId++, dv);
40992
41047
  dvRules.push(listRule);
40993
41048
  break;
40994
41049
  case "date":
@@ -41008,9 +41063,9 @@ stores.inject(MyMetaStore, storeInstance);
41008
41063
  return dvRules;
41009
41064
  }
41010
41065
  function convertDecimalRule(id, dv) {
41011
- const values = [dv.formula1.toString()];
41066
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41012
41067
  if (dv.formula2) {
41013
- values.push(dv.formula2.toString());
41068
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41014
41069
  }
41015
41070
  return {
41016
41071
  id: id.toString(),
@@ -41022,7 +41077,7 @@ stores.inject(MyMetaStore, storeInstance);
41022
41077
  },
41023
41078
  };
41024
41079
  }
41025
- function convertListrule(id, dv) {
41080
+ function convertListRule(id, dv) {
41026
41081
  const formula1 = dv.formula1.toString();
41027
41082
  const isRangeRule = rangeReference.test(formula1);
41028
41083
  return {
@@ -41038,9 +41093,9 @@ stores.inject(MyMetaStore, storeInstance);
41038
41093
  }
41039
41094
  function convertDateRule(id, dv) {
41040
41095
  let criterion;
41041
- const values = [dv.formula1.toString()];
41096
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41042
41097
  if (dv.formula2) {
41043
- values.push(dv.formula2.toString());
41098
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41044
41099
  criterion = {
41045
41100
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
41046
41101
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -41067,7 +41122,7 @@ stores.inject(MyMetaStore, storeInstance);
41067
41122
  isBlocking: dv.errorStyle !== "warning",
41068
41123
  criterion: {
41069
41124
  type: "customFormula",
41070
- values: [`=${dv.formula1.toString()}`],
41125
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
41071
41126
  },
41072
41127
  };
41073
41128
  }
@@ -49731,39 +49786,63 @@ stores.inject(MyMetaStore, storeInstance);
49731
49786
  this.model.dispatch("AUTOFILL_TABLE_COLUMN", { ...this.currentEditedCell });
49732
49787
  this.setContent("");
49733
49788
  }
49734
- getComposerContent(position) {
49789
+ getComposerContent(position, selection) {
49735
49790
  const locale = this.getters.getLocale();
49736
49791
  const cell = this.getters.getCell(position);
49737
49792
  if (cell?.isFormula) {
49738
49793
  const prettifiedContent = this.getPrettifiedFormula(cell);
49739
- 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
+ };
49740
49819
  }
49741
49820
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49742
49821
  if (spreader) {
49743
- return "";
49822
+ return { text: "" };
49744
49823
  }
49745
49824
  const { format, value, type, formattedValue } = this.getters.getEvaluatedCell(position);
49746
49825
  switch (type) {
49747
49826
  case CellValueType.empty:
49748
- return "";
49827
+ return { text: "" };
49749
49828
  case CellValueType.text:
49750
49829
  case CellValueType.error:
49751
- return value;
49830
+ return { text: value };
49752
49831
  case CellValueType.boolean:
49753
- return formattedValue;
49832
+ return { text: formattedValue };
49754
49833
  case CellValueType.number:
49755
49834
  if (format && isDateTimeFormat(format)) {
49756
49835
  if (parseDateTime(formattedValue, locale) !== null) {
49757
49836
  // formatted string can be parsed again
49758
- return formattedValue;
49837
+ return { text: formattedValue };
49759
49838
  }
49760
49839
  // display a simplified and parsable string otherwise
49761
49840
  const timeFormat = Number.isInteger(value)
49762
49841
  ? locale.dateFormat
49763
49842
  : getDateTimeFormat(locale);
49764
- return formatValue(value, { locale, format: timeFormat });
49843
+ return { text: formatValue(value, { locale, format: timeFormat }) };
49765
49844
  }
49766
- return this.numberComposerContent(value, format, locale);
49845
+ return { text: this.numberComposerContent(value, format, locale) };
49767
49846
  }
49768
49847
  }
49769
49848
  getPrettifiedFormula(cell) {
@@ -49932,8 +50011,9 @@ stores.inject(MyMetaStore, storeInstance);
49932
50011
  },
49933
50012
  focus: this.focus,
49934
50013
  isDefaultFocus: true,
49935
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
50014
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
49936
50015
  focusMode: "contentFocus",
50016
+ selection,
49937
50017
  }),
49938
50018
  onComposerCellFocused: (content) => this.composerFocusStore.focusComposer(this.composerInterface, {
49939
50019
  focusMode: "cellFocus",
@@ -57339,12 +57419,13 @@ stores.inject(MyMetaStore, storeInstance);
57339
57419
  onCloseSidePanel: { type: Function, optional: true },
57340
57420
  };
57341
57421
  state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
57422
+ editingSheetId;
57342
57423
  setup() {
57424
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
57343
57425
  if (this.props.rule) {
57344
- const sheetId = this.env.model.getters.getActiveSheetId();
57345
57426
  this.state.rule = {
57346
57427
  ...this.props.rule,
57347
- 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)),
57348
57429
  };
57349
57430
  this.state.rule.criterion.type = this.props.rule.criterion.type;
57350
57431
  }
@@ -57378,7 +57459,6 @@ stores.inject(MyMetaStore, storeInstance);
57378
57459
  const locale = this.env.model.getters.getLocale();
57379
57460
  const criterion = rule.criterion;
57380
57461
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
57381
- const sheetId = this.env.model.getters.getActiveSheetId();
57382
57462
  const values = criterion.values
57383
57463
  .slice(0, criterionEvaluator.numberOfValues(criterion))
57384
57464
  .map((value) => value?.trim())
@@ -57386,8 +57466,8 @@ stores.inject(MyMetaStore, storeInstance);
57386
57466
  .map((value) => canonicalizeContent(value, locale));
57387
57467
  rule.criterion = { ...criterion, values };
57388
57468
  return {
57389
- sheetId,
57390
- 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)),
57391
57471
  rule,
57392
57472
  };
57393
57473
  }
@@ -57914,6 +57994,7 @@ stores.inject(MyMetaStore, storeInstance);
57914
57994
  .o-button {
57915
57995
  height: 19px;
57916
57996
  width: 19px;
57997
+ box-sizing: content-box;
57917
57998
  .o-icon {
57918
57999
  height: 14px;
57919
58000
  width: 14px;
@@ -58659,7 +58740,7 @@ stores.inject(MyMetaStore, storeInstance);
58659
58740
  return undefined;
58660
58741
  }
58661
58742
  get isCalculatedMeasureInvalid() {
58662
- return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
58743
+ return compile(this.props.measure.computedBy?.formula ?? "").isBadExpression;
58663
58744
  }
58664
58745
  }
58665
58746
 
@@ -61537,16 +61618,16 @@ stores.inject(MyMetaStore, storeInstance);
61537
61618
  }
61538
61619
  }
61539
61620
 
61540
- class FullScreenChart extends owl.Component {
61541
- static template = "o-spreadsheet-FullScreenChart";
61621
+ class FullScreenFigure extends owl.Component {
61622
+ static template = "o-spreadsheet-FullScreenFigure";
61542
61623
  static props = {};
61543
- static components = { ChartDashboardMenu };
61544
- fullScreenChartStore;
61545
- ref = owl.useRef("fullScreenChart");
61624
+ static components = { ChartFigure };
61625
+ fullScreenFigureStore;
61626
+ ref = owl.useRef("fullScreenFigure");
61546
61627
  spreadsheetRect = useSpreadsheetRect();
61547
61628
  figureRegistry = figureRegistry;
61548
61629
  setup() {
61549
- this.fullScreenChartStore = useStore(FullScreenChartStore);
61630
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
61550
61631
  const animationStore = useStore(ChartAnimationStore);
61551
61632
  let lastFigureId = undefined;
61552
61633
  owl.onWillUpdateProps(() => {
@@ -61558,7 +61639,7 @@ stores.inject(MyMetaStore, storeInstance);
61558
61639
  owl.useEffect((el) => el?.focus(), () => [this.ref.el]);
61559
61640
  }
61560
61641
  get figureUI() {
61561
- return this.fullScreenChartStore.fullScreenFigure;
61642
+ return this.fullScreenFigureStore.fullScreenFigure;
61562
61643
  }
61563
61644
  get chartId() {
61564
61645
  if (!this.figureUI)
@@ -61567,7 +61648,7 @@ stores.inject(MyMetaStore, storeInstance);
61567
61648
  }
61568
61649
  exitFullScreen() {
61569
61650
  if (this.figureUI) {
61570
- this.fullScreenChartStore.toggleFullScreenChart(this.figureUI.id);
61651
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.figureUI.id);
61571
61652
  }
61572
61653
  }
61573
61654
  onKeyDown(ev) {
@@ -61575,15 +61656,10 @@ stores.inject(MyMetaStore, storeInstance);
61575
61656
  this.exitFullScreen();
61576
61657
  }
61577
61658
  }
61578
- get chartComponent() {
61579
- if (!this.chartId)
61659
+ get figureComponent() {
61660
+ if (!this.figureUI)
61580
61661
  return undefined;
61581
- const type = this.env.model.getters.getChartType(this.chartId);
61582
- const component = chartComponentRegistry.get(type);
61583
- if (!component) {
61584
- throw new Error(`Component is not defined for type ${type}`);
61585
- }
61586
- return component;
61662
+ return figureRegistry.get(this.figureUI.tag).Component;
61587
61663
  }
61588
61664
  }
61589
61665
 
@@ -64323,11 +64399,11 @@ stores.inject(MyMetaStore, storeInstance);
64323
64399
  break;
64324
64400
  }
64325
64401
  case "ADD_COLUMNS_ROWS": {
64326
- const sizes = [...this.sizes[cmd.sheetId][cmd.dimension]];
64402
+ const sizes = this.sizes[cmd.sheetId][cmd.dimension];
64327
64403
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
64328
64404
  const baseSize = sizes[cmd.base];
64329
- sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
64330
- this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
64405
+ const newSizes = insertItemsAtIndex(sizes, Array(cmd.quantity).fill(baseSize), addIndex);
64406
+ this.history.update("sizes", cmd.sheetId, cmd.dimension, newSizes);
64331
64407
  break;
64332
64408
  }
64333
64409
  case "RESIZE_COLUMNS_ROWS":
@@ -64478,9 +64554,8 @@ stores.inject(MyMetaStore, storeInstance);
64478
64554
  break;
64479
64555
  }
64480
64556
  case "ADD_COLUMNS_ROWS": {
64481
- const hiddenHeaders = [...this.hiddenHeaders[cmd.sheetId][cmd.dimension]];
64482
64557
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
64483
- hiddenHeaders.splice(addIndex, 0, ...Array(cmd.quantity).fill(false));
64558
+ const hiddenHeaders = insertItemsAtIndex([...this.hiddenHeaders[cmd.sheetId][cmd.dimension]], Array(cmd.quantity).fill(false), addIndex);
64484
64559
  this.history.update("hiddenHeaders", cmd.sheetId, cmd.dimension, hiddenHeaders);
64485
64560
  break;
64486
64561
  }
@@ -68664,12 +68739,12 @@ stores.inject(MyMetaStore, storeInstance);
68664
68739
  this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
68665
68740
  }
68666
68741
  rtreeItemComparer(left, right) {
68667
- return (left.data === right.data &&
68668
- left.boundingBox.sheetId === right.boundingBox.sheetId &&
68742
+ return (left.boundingBox.sheetId === right.boundingBox.sheetId &&
68669
68743
  left.boundingBox?.zone.left === right.boundingBox.zone.left &&
68670
68744
  left.boundingBox?.zone.top === right.boundingBox.zone.top &&
68671
68745
  left.boundingBox?.zone.right === right.boundingBox.zone.right &&
68672
- left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
68746
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom &&
68747
+ deepEquals(left.data, right.data));
68673
68748
  }
68674
68749
  }
68675
68750
  /**
@@ -68742,7 +68817,7 @@ stores.inject(MyMetaStore, storeInstance);
68742
68817
  * in the correct order they should be evaluated.
68743
68818
  * This is called a topological ordering (excluding cycles)
68744
68819
  */
68745
- getCellsDependingOn(ranges) {
68820
+ getCellsDependingOn(ranges, ignore) {
68746
68821
  const visited = this.createEmptyPositionSet();
68747
68822
  const queue = Array.from(ranges).reverse();
68748
68823
  while (queue.length > 0) {
@@ -68757,7 +68832,7 @@ stores.inject(MyMetaStore, storeInstance);
68757
68832
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
68758
68833
  const nextInQueue = {};
68759
68834
  for (const position of impactedPositions) {
68760
- if (!visited.has(position)) {
68835
+ if (!visited.has(position) && !ignore.has(position)) {
68761
68836
  if (!nextInQueue[position.sheetId]) {
68762
68837
  nextInQueue[position.sheetId] = [];
68763
68838
  }
@@ -69315,7 +69390,7 @@ stores.inject(MyMetaStore, storeInstance);
69315
69390
  }
69316
69391
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
69317
69392
  // the result matrix is split in 2 zones to exclude the array formula position
69318
- 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);
69319
69394
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
69320
69395
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
69321
69396
  }
@@ -69433,7 +69508,7 @@ stores.inject(MyMetaStore, storeInstance);
69433
69508
  for (const sheetId in zonesBySheetIds) {
69434
69509
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
69435
69510
  }
69436
- return this.formulaDependencies().getCellsDependingOn(ranges);
69511
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
69437
69512
  }
69438
69513
  }
69439
69514
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -70944,7 +71019,8 @@ stores.inject(MyMetaStore, storeInstance);
70944
71019
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
70945
71020
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
70946
71021
  if (!parentSpreadingCell) {
70947
- return false;
71022
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
71023
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
70948
71024
  }
70949
71025
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
70950
71026
  return true;
@@ -82116,6 +82192,7 @@ stores.inject(MyMetaStore, storeInstance);
82116
82192
  static components = { Menu };
82117
82193
  rootItems = topbarMenuRegistry.getMenuItems();
82118
82194
  menuRef = owl.useRef("menu");
82195
+ containerRef = owl.useRef("container");
82119
82196
  state = owl.useState({
82120
82197
  menuItems: this.rootItems,
82121
82198
  title: _t("Menu Bar"),
@@ -82123,6 +82200,7 @@ stores.inject(MyMetaStore, storeInstance);
82123
82200
  });
82124
82201
  setup() {
82125
82202
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
82203
+ owl.onMounted(this.updateShadows);
82126
82204
  }
82127
82205
  onExternalClick(ev) {
82128
82206
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -82135,6 +82213,7 @@ stores.inject(MyMetaStore, storeInstance);
82135
82213
  this.state.parentState = { ...this.state };
82136
82214
  this.state.menuItems = children;
82137
82215
  this.state.title = menu.name(this.env);
82216
+ this.containerRef.el?.scrollTo({ top: 0 });
82138
82217
  }
82139
82218
  else {
82140
82219
  this.state.menuItems = this.rootItems;
@@ -82156,6 +82235,19 @@ stores.inject(MyMetaStore, storeInstance);
82156
82235
  height: `${this.props.height}px`,
82157
82236
  });
82158
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
+ }
82159
82251
  onClickBack() {
82160
82252
  if (!this.state.parentState) {
82161
82253
  this.props.onClose();
@@ -82164,6 +82256,7 @@ stores.inject(MyMetaStore, storeInstance);
82164
82256
  this.state.menuItems = this.state.parentState.menuItems;
82165
82257
  this.state.title = this.state.parentState.title;
82166
82258
  this.state.parentState = this.state.parentState.parentState;
82259
+ this.containerRef.el?.scrollTo({ top: 0 });
82167
82260
  }
82168
82261
  get backTitle() {
82169
82262
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -82219,6 +82312,11 @@ stores.inject(MyMetaStore, storeInstance);
82219
82312
  ? this.composerFocusStore.focusMode
82220
82313
  : "inactive";
82221
82314
  }
82315
+ get showFxIcon() {
82316
+ return (this.focus === "inactive" &&
82317
+ !this.composerStore.currentContent &&
82318
+ !this.composerStore.placeholder);
82319
+ }
82222
82320
  get rect() {
82223
82321
  return this.composerRef.el
82224
82322
  ? getBoundingRectAsPOJO(this.composerRef.el)
@@ -82234,8 +82332,9 @@ stores.inject(MyMetaStore, storeInstance);
82234
82332
  },
82235
82333
  focus: this.focus,
82236
82334
  composerStore: this.composerStore,
82237
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
82335
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
82238
82336
  focusMode: "contentFocus",
82337
+ selection,
82239
82338
  }),
82240
82339
  isDefaultFocus: false,
82241
82340
  inputStyle: cssPropertiesToCss({
@@ -82243,6 +82342,7 @@ stores.inject(MyMetaStore, storeInstance);
82243
82342
  "max-height": `130px`,
82244
82343
  }),
82245
82344
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82345
+ placeholder: this.composerStore.placeholder,
82246
82346
  };
82247
82347
  }
82248
82348
  get symbols() {
@@ -82261,12 +82361,6 @@ stores.inject(MyMetaStore, storeInstance);
82261
82361
  }
82262
82362
 
82263
82363
  const COMPOSER_MAX_HEIGHT = 300;
82264
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
82265
- const FX_SVG = /*xml*/ `
82266
- <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
82267
- <path d='m28 34-4 5v2h10l-6 40c-4 22-6 28-7 30-2 2-3 3-5 3-3 0-7-2-9-4H4c-2 2-4 4-4 7s4 6 8 6 9-2 15-8c8-7 13-17 18-39l7-35 13-1 3-6H49c4-23 7-27 11-27 2 0 5 2 8 6h4c1-1 4-4 4-7 0-2-3-6-9-6-5 0-13 4-20 10-6 7-9 14-11 24h-8zm41 16c4-5 7-7 8-7s2 1 5 9l3 12c-7 11-12 17-16 17l-3-1-2-1c-3 0-6 3-6 7s3 7 7 7c6 0 12-6 22-23l3 10c3 9 6 13 10 13 5 0 11-4 18-15l-3-4c-4 6-7 8-8 8-2 0-4-3-6-10l-5-15 8-10 6-4 3 1 3 2c2 0 6-3 6-7s-2-7-6-7c-6 0-11 5-21 20l-2-6c-3-9-5-14-9-14-5 0-12 6-18 15l3 3z' fill='#BDBDBD'/>
82268
- </svg>
82269
- `;
82270
82364
  css /* scss */ `
82271
82365
  .o-topbar-composer-container {
82272
82366
  height: ${DESKTOP_TOPBAR_TOOLBAR_HEIGHT}px;
@@ -82278,14 +82372,6 @@ stores.inject(MyMetaStore, storeInstance);
82278
82372
  margin-bottom: -1px;
82279
82373
  border: 1px solid;
82280
82374
  font-family: ${DEFAULT_FONT};
82281
-
82282
- /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
82283
- .o-composer:empty:not(:focus):not(.active)::before,
82284
- &.o-topbar-composer-readonly .o-composer:empty::before {
82285
- content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
82286
- position: relative;
82287
- top: 20%;
82288
- }
82289
82375
  }
82290
82376
 
82291
82377
  .user-select-text {
@@ -82318,6 +82404,11 @@ stores.inject(MyMetaStore, storeInstance);
82318
82404
  ? this.composerFocusStore.focusMode
82319
82405
  : "inactive";
82320
82406
  }
82407
+ get showFxIcon() {
82408
+ return (this.focus === "inactive" &&
82409
+ !this.composerStore.currentContent &&
82410
+ !this.composerStore.placeholder);
82411
+ }
82321
82412
  get composerStyle() {
82322
82413
  const style = {
82323
82414
  padding: "5px 0px 5px 8px",
@@ -83669,7 +83760,7 @@ stores.inject(MyMetaStore, storeInstance);
83669
83760
  SidePanels,
83670
83761
  SpreadsheetDashboard,
83671
83762
  HeaderGroupContainer,
83672
- FullScreenChart,
83763
+ FullScreenFigure,
83673
83764
  };
83674
83765
  sidePanel;
83675
83766
  spreadsheetRef = owl.useRef("spreadsheet");
@@ -88393,6 +88484,7 @@ stores.inject(MyMetaStore, storeInstance);
88393
88484
  Grid,
88394
88485
  GridOverlay,
88395
88486
  ScorecardChart,
88487
+ GaugeChartComponent,
88396
88488
  LineConfigPanel,
88397
88489
  BarConfigPanel,
88398
88490
  PieChartDesignPanel,
@@ -88431,7 +88523,7 @@ stores.inject(MyMetaStore, storeInstance);
88431
88523
  RadioSelection,
88432
88524
  GeoChartRegionSelectSection,
88433
88525
  ChartDashboardMenu,
88434
- FullScreenChart,
88526
+ FullScreenFigure,
88435
88527
  };
88436
88528
  const hooks = {
88437
88529
  useDragAndDropListItems,
@@ -88531,9 +88623,9 @@ stores.inject(MyMetaStore, storeInstance);
88531
88623
  exports.tokenize = tokenize;
88532
88624
 
88533
88625
 
88534
- __info__.version = "19.0.4";
88535
- __info__.date = "2025-09-23T12:37:28.362Z";
88536
- __info__.hash = "87b774d";
88626
+ __info__.version = "19.0.6";
88627
+ __info__.date = "2025-10-16T06:39:36.282Z";
88628
+ __info__.hash = "0d4315a";
88537
88629
 
88538
88630
 
88539
88631
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);