@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
  'use strict';
@@ -1384,9 +1384,7 @@ function removeIndexesFromArray(array, indexes) {
1384
1384
  return newArray;
1385
1385
  }
1386
1386
  function insertItemsAtIndex(array, items, index) {
1387
- const newArray = [...array];
1388
- newArray.splice(index, 0, ...items);
1389
- return newArray;
1387
+ return array.slice(0, index).concat(items).concat(array.slice(index));
1390
1388
  }
1391
1389
  function replaceItemAtIndex(array, newItem, index) {
1392
1390
  const newArray = [...array];
@@ -4517,7 +4515,17 @@ function toNumberMatrix(data, argName) {
4517
4515
  return toMatrix(data).map((row) => {
4518
4516
  return row.map((cell) => {
4519
4517
  if (typeof cell.value !== "number") {
4520
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
4518
+ let message = "";
4519
+ if (typeof cell === "object") {
4520
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
4521
+ }
4522
+ else if (typeof cell === "string") {
4523
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
4524
+ }
4525
+ else if (typeof cell === "boolean") {
4526
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
4527
+ }
4528
+ throw new EvaluationError(message);
4521
4529
  }
4522
4530
  return cell.value;
4523
4531
  });
@@ -5636,7 +5644,7 @@ function tokensToTextInternalFormat(tokens) {
5636
5644
  * Replace in place tokens "mm" and "m" that denote minutes in date format with "MM" to avoid confusion with months.
5637
5645
  *
5638
5646
  * As per OpenXML specification, in date formats if a date token "m" or "mm" is followed by a date token "s" or
5639
- * preceded by a data token "h", then it's not a month but an minute.
5647
+ * preceded by a data token "h", then it's not a month but a minute.
5640
5648
  */
5641
5649
  function convertTokensToMinutesInDateFormat(tokens) {
5642
5650
  const dateParts = tokens.filter((token) => token.type === "DATE_PART");
@@ -5679,6 +5687,9 @@ function internalFormatPartToFormat(internalFormat) {
5679
5687
  case "REPEATED_CHAR":
5680
5688
  format += "*" + token.value;
5681
5689
  break;
5690
+ case "DATE_PART":
5691
+ format += token.value === "MM" ? "mm" : token.value; // Convert "MM" back to "mm" for minutes
5692
+ break;
5682
5693
  default:
5683
5694
  format += token.value;
5684
5695
  }
@@ -9529,7 +9540,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9529
9540
  pasteCell(origin, target, clipboardOption) {
9530
9541
  const { sheetId, col, row } = target;
9531
9542
  const targetCell = this.getters.getEvaluatedCell(target);
9532
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9543
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9533
9544
  if (clipboardOption?.pasteOption === "asValue") {
9534
9545
  this.dispatch("UPDATE_CELL", {
9535
9546
  ...target,
@@ -13902,7 +13913,7 @@ const GROWTH = {
13902
13913
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13903
13914
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13904
13915
  }
13905
- return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "the first argument (known_data_y)")), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
13916
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13906
13917
  },
13907
13918
  };
13908
13919
  // -----------------------------------------------------------------------------
@@ -13975,7 +13986,7 @@ const LINEST = {
13975
13986
  if (dataY.length === 0 || dataY[0].length === 0) {
13976
13987
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13977
13988
  }
13978
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13989
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13979
13990
  },
13980
13991
  isExported: true,
13981
13992
  };
@@ -13994,7 +14005,7 @@ const LOGEST = {
13994
14005
  if (dataY.length === 0 || dataY[0].length === 0) {
13995
14006
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13996
14007
  }
13997
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
14008
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13998
14009
  for (let i = 0; i < coeffs.length; i++) {
13999
14010
  coeffs[i][0] = Math.exp(coeffs[i][0]);
14000
14011
  }
@@ -14615,7 +14626,7 @@ const TREND = {
14615
14626
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
14616
14627
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
14617
14628
  }
14618
- return predictLinearValues(toNumberMatrix(knownDataY, "the first argument (known_data_y)"), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
14629
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14619
14630
  },
14620
14631
  };
14621
14632
  // -----------------------------------------------------------------------------
@@ -23136,6 +23147,10 @@ const chartShowValuesPlugin = {
23136
23147
  }
23137
23148
  const ctx = chart.ctx;
23138
23149
  ctx.save();
23150
+ const { left, top, height, width } = chart.chartArea;
23151
+ ctx.beginPath();
23152
+ ctx.rect(left, top, width, height);
23153
+ ctx.clip();
23139
23154
  ctx.textAlign = "center";
23140
23155
  ctx.textBaseline = "middle";
23141
23156
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -24179,7 +24194,7 @@ class ChartJsComponent extends owl.Component {
24179
24194
  this.chart.update();
24180
24195
  }
24181
24196
  hasChartDataChanged() {
24182
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
24197
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
24183
24198
  }
24184
24199
  enableAnimationInChartData(chartData) {
24185
24200
  return {
@@ -24187,6 +24202,17 @@ class ChartJsComponent extends owl.Component {
24187
24202
  options: { ...chartData.options, animation: { animateRotate: true } },
24188
24203
  };
24189
24204
  }
24205
+ getChartDataInRuntime(runtime) {
24206
+ const data = runtime.chartJsConfig.data;
24207
+ return {
24208
+ labels: data.labels,
24209
+ dataset: data.datasets.map((dataset) => ({
24210
+ data: dataset.data,
24211
+ label: dataset.label,
24212
+ tree: dataset.tree,
24213
+ })),
24214
+ };
24215
+ }
24190
24216
  get animationChartId() {
24191
24217
  return this.props.isFullScreen ? this.props.chartId + "-fullscreen" : this.props.chartId;
24192
24218
  }
@@ -24854,6 +24880,7 @@ class ScorecardChart extends owl.Component {
24854
24880
  static template = "o-spreadsheet-ScorecardChart";
24855
24881
  static props = {
24856
24882
  chartId: String,
24883
+ isFullScreen: { type: Boolean, optional: true },
24857
24884
  };
24858
24885
  canvas = owl.useRef("chartContainer");
24859
24886
  get runtime() {
@@ -25609,6 +25636,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
25609
25636
  parser: luxonFormat,
25610
25637
  displayFormats,
25611
25638
  unit: timeUnit ?? false,
25639
+ tooltipFormat: luxonFormat,
25612
25640
  };
25613
25641
  }
25614
25642
  /**
@@ -26677,6 +26705,7 @@ function getLineChartScales(definition, args) {
26677
26705
  };
26678
26706
  Object.assign(scales.x, axis);
26679
26707
  scales.x.ticks.maxTicksLimit = 15;
26708
+ delete scales?.x?.ticks?.callback;
26680
26709
  }
26681
26710
  else if (axisType === "linear") {
26682
26711
  scales.x.type = "linear";
@@ -27617,26 +27646,6 @@ function createBarChartRuntime(chart, getters) {
27617
27646
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
27618
27647
  }
27619
27648
 
27620
- class FullScreenChartStore extends SpreadsheetStore {
27621
- mutators = ["toggleFullScreenChart"];
27622
- fullScreenFigure = undefined;
27623
- toggleFullScreenChart(figureId) {
27624
- if (this.fullScreenFigure?.id === figureId) {
27625
- this.fullScreenFigure = undefined;
27626
- }
27627
- else {
27628
- this.makeFullScreen(figureId);
27629
- }
27630
- }
27631
- makeFullScreen(figureId) {
27632
- const sheetId = this.getters.getActiveSheetId();
27633
- const figure = this.getters.getFigure(sheetId, figureId);
27634
- if (figure) {
27635
- this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
27636
- }
27637
- }
27638
- }
27639
-
27640
27649
  const TREND_LINE_AXES_IDS = [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID];
27641
27650
  const ZOOMABLE_AXIS_IDS = ["x", ...TREND_LINE_AXES_IDS];
27642
27651
  class ZoomableChartStore extends SpreadsheetStore {
@@ -27808,7 +27817,6 @@ chartJsExtensionRegistry.add("zoomWindowPlugin", {
27808
27817
  class ZoomableChartJsComponent extends ChartJsComponent {
27809
27818
  static template = "o-spreadsheet-ZoomableChartJsComponent";
27810
27819
  store;
27811
- fullScreenChartStore;
27812
27820
  masterChartCanvas = owl.useRef("masterChartCanvas");
27813
27821
  masterChart;
27814
27822
  mode;
@@ -27819,7 +27827,6 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27819
27827
  removeEventListeners = () => { };
27820
27828
  setup() {
27821
27829
  this.store = useStore(ZoomableChartStore);
27822
- this.fullScreenChartStore = useStore(FullScreenChartStore);
27823
27830
  super.setup();
27824
27831
  }
27825
27832
  unmount() {
@@ -27834,12 +27841,8 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27834
27841
  `;
27835
27842
  }
27836
27843
  get sliceable() {
27837
- if (this.env.isDashboard()) {
27838
- const fullScreenFigureId = this.fullScreenChartStore.fullScreenFigure?.id;
27839
- const chartFigureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
27840
- if (fullScreenFigureId === chartFigureId) {
27841
- return true;
27842
- }
27844
+ if (this.props.isFullScreen) {
27845
+ return true;
27843
27846
  }
27844
27847
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27845
27848
  return ("zoomable" in definition && definition?.zoomable) ?? false;
@@ -27902,9 +27905,6 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27902
27905
  const xMax = Math.max(...xValues);
27903
27906
  return { xMin, xMax };
27904
27907
  }
27905
- get shouldAnimate() {
27906
- return this.env.model.getters.isDashboard() && !this.sliceable;
27907
- }
27908
27908
  createChart(chartRuntime) {
27909
27909
  const chartData = chartRuntime.chartJsConfig;
27910
27910
  this.isBarChart = chartData.type === "bar";
@@ -27923,6 +27923,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27923
27923
  const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
27924
27924
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27925
27925
  this.resetAxesLimits();
27926
+ if (this.chart?.options) {
27927
+ this.chart.options.animation = false;
27928
+ }
27926
27929
  }
27927
27930
  updateChartJs(chartRuntime) {
27928
27931
  const chartData = chartRuntime.chartJsConfig;
@@ -27956,6 +27959,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27956
27959
  }
27957
27960
  }
27958
27961
  this.resetAxesLimits();
27962
+ if (this.chart?.options) {
27963
+ this.chart.options.animation = false;
27964
+ }
27959
27965
  }
27960
27966
  resetAxesLimits() {
27961
27967
  if (!this.chart) {
@@ -28053,7 +28059,7 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28053
28059
  onPointerDownInMasterChart(ev) {
28054
28060
  this.removeEventListeners();
28055
28061
  const position = ev.offsetX;
28056
- if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
28062
+ if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
28057
28063
  return;
28058
28064
  }
28059
28065
  const { left, right, top, bottom } = this.masterChart.chartArea;
@@ -31611,6 +31617,26 @@ function getCarouselItemTitle(getters, item) {
31611
31617
  return matchedChart.displayName;
31612
31618
  }
31613
31619
 
31620
+ class FullScreenFigureStore extends SpreadsheetStore {
31621
+ mutators = ["toggleFullScreenFigure"];
31622
+ fullScreenFigure = undefined;
31623
+ toggleFullScreenFigure(figureId) {
31624
+ if (this.fullScreenFigure?.id === figureId) {
31625
+ this.fullScreenFigure = undefined;
31626
+ }
31627
+ else {
31628
+ this.makeFullScreen(figureId);
31629
+ }
31630
+ }
31631
+ makeFullScreen(figureId) {
31632
+ const sheetId = this.getters.getActiveSheetId();
31633
+ const figure = this.getters.getFigure(sheetId, figureId);
31634
+ if (figure) {
31635
+ this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
31636
+ }
31637
+ }
31638
+ }
31639
+
31614
31640
  /**
31615
31641
  * Repeatedly calls a callback function with a time delay between calls.
31616
31642
  */
@@ -32348,12 +32374,13 @@ class MenuPopover extends owl.Component {
32348
32374
  class ChartDashboardMenu extends owl.Component {
32349
32375
  static template = "o-spreadsheet-ChartDashboardMenu";
32350
32376
  static components = { MenuPopover };
32351
- static props = { chartId: String };
32377
+ static props = { chartId: String, hasFullScreenButton: { type: Boolean, optional: true } };
32378
+ static defaultProps = { hasFullScreenButton: true };
32352
32379
  fullScreenFigureStore;
32353
32380
  menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
32354
32381
  setup() {
32355
32382
  super.setup();
32356
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
32383
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32357
32384
  }
32358
32385
  getMenuItems() {
32359
32386
  return [this.fullScreenMenuItem].filter(isDefined);
@@ -32364,11 +32391,14 @@ class ChartDashboardMenu extends owl.Component {
32364
32391
  }
32365
32392
  openContextMenu(ev) {
32366
32393
  this.menuState.isOpen = true;
32367
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
32394
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32368
32395
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32369
32396
  this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32370
32397
  }
32371
32398
  get fullScreenMenuItem() {
32399
+ if (!this.props.hasFullScreenButton) {
32400
+ return undefined;
32401
+ }
32372
32402
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
32373
32403
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32374
32404
  if (definition.type === "scorecard") {
@@ -32380,7 +32410,7 @@ class ChartDashboardMenu extends owl.Component {
32380
32410
  label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
32381
32411
  class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
32382
32412
  onClick: () => {
32383
- this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32413
+ this.fullScreenFigureStore.toggleFullScreenFigure(figureId);
32384
32414
  },
32385
32415
  };
32386
32416
  }
@@ -32392,6 +32422,8 @@ class CarouselFigure extends owl.Component {
32392
32422
  figureUI: Object,
32393
32423
  onFigureDeleted: Function,
32394
32424
  editFigureStyle: { type: Function, optional: true },
32425
+ isFullScreen: { type: Boolean, optional: true },
32426
+ openContextMenu: { type: Function, optional: true },
32395
32427
  };
32396
32428
  static components = { ChartDashboardMenu, MenuPopover };
32397
32429
  carouselTabsRef = owl.useRef("carouselTabs");
@@ -32399,8 +32431,10 @@ class CarouselFigure extends owl.Component {
32399
32431
  menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
32400
32432
  hiddenItems = [];
32401
32433
  animationStore;
32434
+ fullScreenFigureStore;
32402
32435
  setup() {
32403
32436
  this.animationStore = useStore(ChartAnimationStore);
32437
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32404
32438
  owl.useEffect(() => {
32405
32439
  if (this.selectedCarouselItem?.type === "carouselDataView") {
32406
32440
  this.props.editFigureStyle?.({ "pointer-events": "none" });
@@ -32447,7 +32481,8 @@ class CarouselFigure extends owl.Component {
32447
32481
  item,
32448
32482
  });
32449
32483
  if (item.type === "chart") {
32450
- this.animationStore?.enableAnimationForChart(item.chartId);
32484
+ const animationChartId = item.chartId + (this.props.isFullScreen ? "-fullscreen" : "");
32485
+ this.animationStore?.enableAnimationForChart(animationChartId);
32451
32486
  }
32452
32487
  }
32453
32488
  get headerStyle() {
@@ -32511,6 +32546,23 @@ class CarouselFigure extends owl.Component {
32511
32546
  this.menuState.anchorRect = rect;
32512
32547
  this.menuState.menuItems = createActions(menuItems);
32513
32548
  }
32549
+ toggleFullScreen() {
32550
+ if (this.selectedCarouselItem?.type === "chart") {
32551
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.props.figureUI.id);
32552
+ }
32553
+ }
32554
+ get fullScreenButtonTitle() {
32555
+ return this.props.isFullScreen ? _t("Exit Full Screen") : _t("Full Screen");
32556
+ }
32557
+ get visibleCarouselItems() {
32558
+ return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32559
+ }
32560
+ openContextMenu(event) {
32561
+ const target = event.currentTarget;
32562
+ if (target) {
32563
+ this.props.openContextMenu?.(getBoundingRectAsPOJO(target));
32564
+ }
32565
+ }
32514
32566
  }
32515
32567
 
32516
32568
  class ChartFigure extends owl.Component {
@@ -32519,6 +32571,8 @@ class ChartFigure extends owl.Component {
32519
32571
  figureUI: Object,
32520
32572
  onFigureDeleted: Function,
32521
32573
  editFigureStyle: { type: Function, optional: true },
32574
+ isFullScreen: { type: Boolean, optional: true },
32575
+ openContextMenu: { type: Function, optional: true },
32522
32576
  };
32523
32577
  static components = { ChartDashboardMenu };
32524
32578
  onDoubleClick() {
@@ -32551,6 +32605,7 @@ class ImageFigure extends owl.Component {
32551
32605
  figureUI: Object,
32552
32606
  onFigureDeleted: Function,
32553
32607
  editFigureStyle: { type: Function, optional: true },
32608
+ openContextMenu: { type: Function, optional: true },
32554
32609
  };
32555
32610
  static components = {};
32556
32611
  // ---------------------------------------------------------------------------
@@ -34602,8 +34657,11 @@ class Composer extends owl.Component {
34602
34657
  }
34603
34658
  const newSelection = this.contentHelper.getCurrentSelection();
34604
34659
  this.props.composerStore.stopComposerRangeSelection();
34605
- this.props.onComposerContentFocused();
34606
- this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34660
+ const isCurrentlyInactive = this.props.composerStore.editionMode === "inactive";
34661
+ this.props.onComposerContentFocused(newSelection);
34662
+ if (!isCurrentlyInactive) {
34663
+ this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34664
+ }
34607
34665
  this.processTokenAtCursor();
34608
34666
  }
34609
34667
  onDblClick() {
@@ -35098,13 +35156,6 @@ class AbstractComposerStore extends SpreadsheetStore {
35098
35156
  }
35099
35157
  }
35100
35158
  startEdition(text, selection) {
35101
- if (selection) {
35102
- const content = text || this.getComposerContent(this.getters.getActivePosition());
35103
- const validSelection = this.isSelectionValid(content.length, selection.start, selection.end);
35104
- if (!validSelection) {
35105
- return;
35106
- }
35107
- }
35108
35159
  const { col, row } = this.getters.getActivePosition();
35109
35160
  this.model.dispatch("SELECT_FIGURE", { figureId: null });
35110
35161
  this.model.dispatch("SCROLL_TO_CELL", { col, row });
@@ -35161,7 +35212,7 @@ class AbstractComposerStore extends SpreadsheetStore {
35161
35212
  // ---------------------------------------------------------------------------
35162
35213
  get currentContent() {
35163
35214
  if (this.editionMode === "inactive") {
35164
- return this.getComposerContent(this.getters.getActivePosition());
35215
+ return this.getComposerContent(this.getters.getActivePosition()).text;
35165
35216
  }
35166
35217
  return this._currentContent;
35167
35218
  }
@@ -35360,8 +35411,9 @@ class AbstractComposerStore extends SpreadsheetStore {
35360
35411
  this.sheetId = sheetId;
35361
35412
  this.row = row;
35362
35413
  this.editionMode = "editing";
35363
- this.initialContent = this.getComposerContent({ sheetId, col, row });
35364
- this.setContent(str || this.initialContent, selection);
35414
+ const { text, adjustedSelection } = this.getComposerContent({ sheetId, col, row }, selection);
35415
+ this.initialContent = text;
35416
+ this.setContent(str || this.initialContent, adjustedSelection ?? selection);
35365
35417
  this.colorIndexByRange = {};
35366
35418
  const zone = positionToZone({ col: this.col, row: this.row });
35367
35419
  this.captureSelection(zone, col, row);
@@ -35838,7 +35890,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35838
35890
  constructor(get, args) {
35839
35891
  super(get);
35840
35892
  this.args = args;
35841
- this._currentContent = this.getComposerContent();
35893
+ this._currentContent = this.getComposerContent().text;
35842
35894
  }
35843
35895
  getAutoCompleteProviders() {
35844
35896
  const providersDefinitions = super.getAutoCompleteProviders();
@@ -35875,7 +35927,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35875
35927
  })
35876
35928
  .join("");
35877
35929
  }
35878
- return localizeContent(content, this.getters.getLocale());
35930
+ return { text: localizeContent(content, this.getters.getLocale()) };
35879
35931
  }
35880
35932
  stopEdition() {
35881
35933
  this._stopEdition();
@@ -39553,6 +39605,74 @@ function getPath2D(svgPath) {
39553
39605
  return path2D;
39554
39606
  }
39555
39607
 
39608
+ /**
39609
+ * Get the relative path between two files
39610
+ *
39611
+ * Eg.:
39612
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
39613
+ */
39614
+ function getRelativePath(from, to) {
39615
+ const fromPathParts = from.split("/");
39616
+ const toPathParts = to.split("/");
39617
+ let relPath = "";
39618
+ let startIndex = 0;
39619
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
39620
+ if (fromPathParts[i] === toPathParts[i]) {
39621
+ startIndex++;
39622
+ }
39623
+ else {
39624
+ relPath += "../";
39625
+ }
39626
+ }
39627
+ relPath += toPathParts.slice(startIndex).join("/");
39628
+ return relPath;
39629
+ }
39630
+ /**
39631
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
39632
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
39633
+ *
39634
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
39635
+ */
39636
+ function arrayToObject(array, indexOffset = 0) {
39637
+ const obj = {};
39638
+ for (let i = 0; i < array.length; i++) {
39639
+ if (array[i]) {
39640
+ obj[i + indexOffset] = array[i];
39641
+ }
39642
+ }
39643
+ return obj;
39644
+ }
39645
+ /**
39646
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
39647
+ * Replace with characters understandable by JS
39648
+ */
39649
+ function fixXlsxUnicode(str) {
39650
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
39651
+ return String.fromCharCode(parseInt(code, 16));
39652
+ });
39653
+ }
39654
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
39655
+ function getSheetDataHeader(sheetData, dimension, index) {
39656
+ if (dimension === "COL") {
39657
+ if (!sheetData.cols[index]) {
39658
+ sheetData.cols[index] = {};
39659
+ }
39660
+ return sheetData.cols[index];
39661
+ }
39662
+ if (!sheetData.rows[index]) {
39663
+ sheetData.rows[index] = {};
39664
+ }
39665
+ return sheetData.rows[index];
39666
+ }
39667
+ /** Prefix the string by "=" if the string looks like a formula */
39668
+ function prefixFormulaWithEqual(formula) {
39669
+ if (formula[0] === "=") {
39670
+ return formula;
39671
+ }
39672
+ const tokens = tokenize(formula);
39673
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
39674
+ }
39675
+
39556
39676
  /**
39557
39677
  * Map of the different types of conversions warnings and their name in error messages
39558
39678
  */
@@ -40075,66 +40195,6 @@ function hexaToInt(hex) {
40075
40195
  */
40076
40196
  const DEFAULT_SYSTEM_COLOR = "FF000000";
40077
40197
 
40078
- /**
40079
- * Get the relative path between two files
40080
- *
40081
- * Eg.:
40082
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
40083
- */
40084
- function getRelativePath(from, to) {
40085
- const fromPathParts = from.split("/");
40086
- const toPathParts = to.split("/");
40087
- let relPath = "";
40088
- let startIndex = 0;
40089
- for (let i = 0; i < fromPathParts.length - 1; i++) {
40090
- if (fromPathParts[i] === toPathParts[i]) {
40091
- startIndex++;
40092
- }
40093
- else {
40094
- relPath += "../";
40095
- }
40096
- }
40097
- relPath += toPathParts.slice(startIndex).join("/");
40098
- return relPath;
40099
- }
40100
- /**
40101
- * Convert an array of element into an object where the objects keys were the elements position in the array.
40102
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
40103
- *
40104
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
40105
- */
40106
- function arrayToObject(array, indexOffset = 0) {
40107
- const obj = {};
40108
- for (let i = 0; i < array.length; i++) {
40109
- if (array[i]) {
40110
- obj[i + indexOffset] = array[i];
40111
- }
40112
- }
40113
- return obj;
40114
- }
40115
- /**
40116
- * In xlsx we can have string with unicode characters with the format _x00fa_.
40117
- * Replace with characters understandable by JS
40118
- */
40119
- function fixXlsxUnicode(str) {
40120
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
40121
- return String.fromCharCode(parseInt(code, 16));
40122
- });
40123
- }
40124
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
40125
- function getSheetDataHeader(sheetData, dimension, index) {
40126
- if (dimension === "COL") {
40127
- if (!sheetData.cols[index]) {
40128
- sheetData.cols[index] = {};
40129
- }
40130
- return sheetData.cols[index];
40131
- }
40132
- if (!sheetData.rows[index]) {
40133
- sheetData.rows[index] = {};
40134
- }
40135
- return sheetData.rows[index];
40136
- }
40137
-
40138
40198
  const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/m|\s|-|\/|\.|:)+$/i;
40139
40199
  /**
40140
40200
  * Convert excel format to o_spreadsheet format
@@ -40349,9 +40409,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
40349
40409
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
40350
40410
  continue;
40351
40411
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
40352
- values.push(prefixFormula(rule.formula[0]));
40412
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
40353
40413
  if (rule.formula.length === 2) {
40354
- values.push(prefixFormula(rule.formula[1]));
40414
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
40355
40415
  }
40356
40416
  break;
40357
40417
  }
@@ -40509,11 +40569,6 @@ function convertIcons(xlsxIconSet, index) {
40509
40569
  ? ICON_SETS[iconSet].neutral
40510
40570
  : ICON_SETS[iconSet].good;
40511
40571
  }
40512
- /** Prefix the string by "=" if the string looks like a formula */
40513
- function prefixFormula(formula) {
40514
- const tokens = tokenize(formula);
40515
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
40516
- }
40517
40572
  // ---------------------------------------------------------------------------
40518
40573
  // Warnings
40519
40574
  // ---------------------------------------------------------------------------
@@ -40989,7 +41044,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
40989
41044
  dvRules.push(decimalRule);
40990
41045
  break;
40991
41046
  case "list":
40992
- const listRule = convertListrule(dvId++, dv);
41047
+ const listRule = convertListRule(dvId++, dv);
40993
41048
  dvRules.push(listRule);
40994
41049
  break;
40995
41050
  case "date":
@@ -41009,9 +41064,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
41009
41064
  return dvRules;
41010
41065
  }
41011
41066
  function convertDecimalRule(id, dv) {
41012
- const values = [dv.formula1.toString()];
41067
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41013
41068
  if (dv.formula2) {
41014
- values.push(dv.formula2.toString());
41069
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41015
41070
  }
41016
41071
  return {
41017
41072
  id: id.toString(),
@@ -41023,7 +41078,7 @@ function convertDecimalRule(id, dv) {
41023
41078
  },
41024
41079
  };
41025
41080
  }
41026
- function convertListrule(id, dv) {
41081
+ function convertListRule(id, dv) {
41027
41082
  const formula1 = dv.formula1.toString();
41028
41083
  const isRangeRule = rangeReference.test(formula1);
41029
41084
  return {
@@ -41039,9 +41094,9 @@ function convertListrule(id, dv) {
41039
41094
  }
41040
41095
  function convertDateRule(id, dv) {
41041
41096
  let criterion;
41042
- const values = [dv.formula1.toString()];
41097
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41043
41098
  if (dv.formula2) {
41044
- values.push(dv.formula2.toString());
41099
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41045
41100
  criterion = {
41046
41101
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
41047
41102
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -41068,7 +41123,7 @@ function convertCustomRule(id, dv) {
41068
41123
  isBlocking: dv.errorStyle !== "warning",
41069
41124
  criterion: {
41070
41125
  type: "customFormula",
41071
- values: [`=${dv.formula1.toString()}`],
41126
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
41072
41127
  },
41073
41128
  };
41074
41129
  }
@@ -49732,39 +49787,63 @@ class CellComposerStore extends AbstractComposerStore {
49732
49787
  this.model.dispatch("AUTOFILL_TABLE_COLUMN", { ...this.currentEditedCell });
49733
49788
  this.setContent("");
49734
49789
  }
49735
- getComposerContent(position) {
49790
+ getComposerContent(position, selection) {
49736
49791
  const locale = this.getters.getLocale();
49737
49792
  const cell = this.getters.getCell(position);
49738
49793
  if (cell?.isFormula) {
49739
49794
  const prettifiedContent = this.getPrettifiedFormula(cell);
49740
- return localizeFormula(prettifiedContent, locale);
49795
+ // when a formula is prettified (multi lines, indented), adapt the cursor position
49796
+ // to take into account line breaks and tabs
49797
+ function adjustCursorIndex(targetIndex) {
49798
+ let adjustedIndex = 0;
49799
+ let originalIndex = 0;
49800
+ while (originalIndex < targetIndex) {
49801
+ adjustedIndex++;
49802
+ const char = prettifiedContent[adjustedIndex];
49803
+ if (char !== "\n" && char !== "\t") {
49804
+ originalIndex++;
49805
+ }
49806
+ }
49807
+ return adjustedIndex;
49808
+ }
49809
+ let adjustedSelection = selection;
49810
+ if (selection) {
49811
+ adjustedSelection = {
49812
+ start: adjustCursorIndex(selection.start),
49813
+ end: adjustCursorIndex(selection.end),
49814
+ };
49815
+ }
49816
+ return {
49817
+ text: localizeFormula(prettifiedContent, locale),
49818
+ adjustedSelection,
49819
+ };
49741
49820
  }
49742
49821
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49743
49822
  if (spreader) {
49744
- return "";
49823
+ return { text: "" };
49745
49824
  }
49746
49825
  const { format, value, type, formattedValue } = this.getters.getEvaluatedCell(position);
49747
49826
  switch (type) {
49748
49827
  case CellValueType.empty:
49749
- return "";
49828
+ return { text: "" };
49750
49829
  case CellValueType.text:
49751
49830
  case CellValueType.error:
49752
- return value;
49831
+ return { text: value };
49753
49832
  case CellValueType.boolean:
49754
- return formattedValue;
49833
+ return { text: formattedValue };
49755
49834
  case CellValueType.number:
49756
49835
  if (format && isDateTimeFormat(format)) {
49757
49836
  if (parseDateTime(formattedValue, locale) !== null) {
49758
49837
  // formatted string can be parsed again
49759
- return formattedValue;
49838
+ return { text: formattedValue };
49760
49839
  }
49761
49840
  // display a simplified and parsable string otherwise
49762
49841
  const timeFormat = Number.isInteger(value)
49763
49842
  ? locale.dateFormat
49764
49843
  : getDateTimeFormat(locale);
49765
- return formatValue(value, { locale, format: timeFormat });
49844
+ return { text: formatValue(value, { locale, format: timeFormat }) };
49766
49845
  }
49767
- return this.numberComposerContent(value, format, locale);
49846
+ return { text: this.numberComposerContent(value, format, locale) };
49768
49847
  }
49769
49848
  }
49770
49849
  getPrettifiedFormula(cell) {
@@ -49933,8 +50012,9 @@ class GridComposer extends owl.Component {
49933
50012
  },
49934
50013
  focus: this.focus,
49935
50014
  isDefaultFocus: true,
49936
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
50015
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
49937
50016
  focusMode: "contentFocus",
50017
+ selection,
49938
50018
  }),
49939
50019
  onComposerCellFocused: (content) => this.composerFocusStore.focusComposer(this.composerInterface, {
49940
50020
  focusMode: "cellFocus",
@@ -57340,12 +57420,13 @@ class DataValidationEditor extends owl.Component {
57340
57420
  onCloseSidePanel: { type: Function, optional: true },
57341
57421
  };
57342
57422
  state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
57423
+ editingSheetId;
57343
57424
  setup() {
57425
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
57344
57426
  if (this.props.rule) {
57345
- const sheetId = this.env.model.getters.getActiveSheetId();
57346
57427
  this.state.rule = {
57347
57428
  ...this.props.rule,
57348
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
57429
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
57349
57430
  };
57350
57431
  this.state.rule.criterion.type = this.props.rule.criterion.type;
57351
57432
  }
@@ -57379,7 +57460,6 @@ class DataValidationEditor extends owl.Component {
57379
57460
  const locale = this.env.model.getters.getLocale();
57380
57461
  const criterion = rule.criterion;
57381
57462
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
57382
- const sheetId = this.env.model.getters.getActiveSheetId();
57383
57463
  const values = criterion.values
57384
57464
  .slice(0, criterionEvaluator.numberOfValues(criterion))
57385
57465
  .map((value) => value?.trim())
@@ -57387,8 +57467,8 @@ class DataValidationEditor extends owl.Component {
57387
57467
  .map((value) => canonicalizeContent(value, locale));
57388
57468
  rule.criterion = { ...criterion, values };
57389
57469
  return {
57390
- sheetId,
57391
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
57470
+ sheetId: this.editingSheetId,
57471
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
57392
57472
  rule,
57393
57473
  };
57394
57474
  }
@@ -57915,6 +57995,7 @@ css /* scss */ `
57915
57995
  .o-button {
57916
57996
  height: 19px;
57917
57997
  width: 19px;
57998
+ box-sizing: content-box;
57918
57999
  .o-icon {
57919
58000
  height: 14px;
57920
58001
  width: 14px;
@@ -58660,7 +58741,7 @@ class PivotMeasureEditor extends owl.Component {
58660
58741
  return undefined;
58661
58742
  }
58662
58743
  get isCalculatedMeasureInvalid() {
58663
- return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
58744
+ return compile(this.props.measure.computedBy?.formula ?? "").isBadExpression;
58664
58745
  }
58665
58746
  }
58666
58747
 
@@ -61538,16 +61619,16 @@ class ClickableCellSortIcon extends owl.Component {
61538
61619
  }
61539
61620
  }
61540
61621
 
61541
- class FullScreenChart extends owl.Component {
61542
- static template = "o-spreadsheet-FullScreenChart";
61622
+ class FullScreenFigure extends owl.Component {
61623
+ static template = "o-spreadsheet-FullScreenFigure";
61543
61624
  static props = {};
61544
- static components = { ChartDashboardMenu };
61545
- fullScreenChartStore;
61546
- ref = owl.useRef("fullScreenChart");
61625
+ static components = { ChartFigure };
61626
+ fullScreenFigureStore;
61627
+ ref = owl.useRef("fullScreenFigure");
61547
61628
  spreadsheetRect = useSpreadsheetRect();
61548
61629
  figureRegistry = figureRegistry;
61549
61630
  setup() {
61550
- this.fullScreenChartStore = useStore(FullScreenChartStore);
61631
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
61551
61632
  const animationStore = useStore(ChartAnimationStore);
61552
61633
  let lastFigureId = undefined;
61553
61634
  owl.onWillUpdateProps(() => {
@@ -61559,7 +61640,7 @@ class FullScreenChart extends owl.Component {
61559
61640
  owl.useEffect((el) => el?.focus(), () => [this.ref.el]);
61560
61641
  }
61561
61642
  get figureUI() {
61562
- return this.fullScreenChartStore.fullScreenFigure;
61643
+ return this.fullScreenFigureStore.fullScreenFigure;
61563
61644
  }
61564
61645
  get chartId() {
61565
61646
  if (!this.figureUI)
@@ -61568,7 +61649,7 @@ class FullScreenChart extends owl.Component {
61568
61649
  }
61569
61650
  exitFullScreen() {
61570
61651
  if (this.figureUI) {
61571
- this.fullScreenChartStore.toggleFullScreenChart(this.figureUI.id);
61652
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.figureUI.id);
61572
61653
  }
61573
61654
  }
61574
61655
  onKeyDown(ev) {
@@ -61576,15 +61657,10 @@ class FullScreenChart extends owl.Component {
61576
61657
  this.exitFullScreen();
61577
61658
  }
61578
61659
  }
61579
- get chartComponent() {
61580
- if (!this.chartId)
61660
+ get figureComponent() {
61661
+ if (!this.figureUI)
61581
61662
  return undefined;
61582
- const type = this.env.model.getters.getChartType(this.chartId);
61583
- const component = chartComponentRegistry.get(type);
61584
- if (!component) {
61585
- throw new Error(`Component is not defined for type ${type}`);
61586
- }
61587
- return component;
61663
+ return figureRegistry.get(this.figureUI.tag).Component;
61588
61664
  }
61589
61665
  }
61590
61666
 
@@ -64324,11 +64400,11 @@ class HeaderSizePlugin extends CorePlugin {
64324
64400
  break;
64325
64401
  }
64326
64402
  case "ADD_COLUMNS_ROWS": {
64327
- const sizes = [...this.sizes[cmd.sheetId][cmd.dimension]];
64403
+ const sizes = this.sizes[cmd.sheetId][cmd.dimension];
64328
64404
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
64329
64405
  const baseSize = sizes[cmd.base];
64330
- sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
64331
- this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
64406
+ const newSizes = insertItemsAtIndex(sizes, Array(cmd.quantity).fill(baseSize), addIndex);
64407
+ this.history.update("sizes", cmd.sheetId, cmd.dimension, newSizes);
64332
64408
  break;
64333
64409
  }
64334
64410
  case "RESIZE_COLUMNS_ROWS":
@@ -64479,9 +64555,8 @@ class HeaderVisibilityPlugin extends CorePlugin {
64479
64555
  break;
64480
64556
  }
64481
64557
  case "ADD_COLUMNS_ROWS": {
64482
- const hiddenHeaders = [...this.hiddenHeaders[cmd.sheetId][cmd.dimension]];
64483
64558
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
64484
- hiddenHeaders.splice(addIndex, 0, ...Array(cmd.quantity).fill(false));
64559
+ const hiddenHeaders = insertItemsAtIndex([...this.hiddenHeaders[cmd.sheetId][cmd.dimension]], Array(cmd.quantity).fill(false), addIndex);
64485
64560
  this.history.update("hiddenHeaders", cmd.sheetId, cmd.dimension, hiddenHeaders);
64486
64561
  break;
64487
64562
  }
@@ -68665,12 +68740,12 @@ class SpreadsheetRTree {
68665
68740
  this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
68666
68741
  }
68667
68742
  rtreeItemComparer(left, right) {
68668
- return (left.data === right.data &&
68669
- left.boundingBox.sheetId === right.boundingBox.sheetId &&
68743
+ return (left.boundingBox.sheetId === right.boundingBox.sheetId &&
68670
68744
  left.boundingBox?.zone.left === right.boundingBox.zone.left &&
68671
68745
  left.boundingBox?.zone.top === right.boundingBox.zone.top &&
68672
68746
  left.boundingBox?.zone.right === right.boundingBox.zone.right &&
68673
- left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
68747
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom &&
68748
+ deepEquals(left.data, right.data));
68674
68749
  }
68675
68750
  }
68676
68751
  /**
@@ -68743,7 +68818,7 @@ class FormulaDependencyGraph {
68743
68818
  * in the correct order they should be evaluated.
68744
68819
  * This is called a topological ordering (excluding cycles)
68745
68820
  */
68746
- getCellsDependingOn(ranges) {
68821
+ getCellsDependingOn(ranges, ignore) {
68747
68822
  const visited = this.createEmptyPositionSet();
68748
68823
  const queue = Array.from(ranges).reverse();
68749
68824
  while (queue.length > 0) {
@@ -68758,7 +68833,7 @@ class FormulaDependencyGraph {
68758
68833
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
68759
68834
  const nextInQueue = {};
68760
68835
  for (const position of impactedPositions) {
68761
- if (!visited.has(position)) {
68836
+ if (!visited.has(position) && !ignore.has(position)) {
68762
68837
  if (!nextInQueue[position.sheetId]) {
68763
68838
  nextInQueue[position.sheetId] = [];
68764
68839
  }
@@ -69316,7 +69391,7 @@ class Evaluator {
69316
69391
  }
69317
69392
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
69318
69393
  // the result matrix is split in 2 zones to exclude the array formula position
69319
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
69394
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
69320
69395
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
69321
69396
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
69322
69397
  }
@@ -69434,7 +69509,7 @@ class Evaluator {
69434
69509
  for (const sheetId in zonesBySheetIds) {
69435
69510
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
69436
69511
  }
69437
- return this.formulaDependencies().getCellsDependingOn(ranges);
69512
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
69438
69513
  }
69439
69514
  }
69440
69515
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -70945,7 +71020,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
70945
71020
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
70946
71021
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
70947
71022
  if (!parentSpreadingCell) {
70948
- return false;
71023
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
71024
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
70949
71025
  }
70950
71026
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
70951
71027
  return true;
@@ -82117,6 +82193,7 @@ class RibbonMenu extends owl.Component {
82117
82193
  static components = { Menu };
82118
82194
  rootItems = topbarMenuRegistry.getMenuItems();
82119
82195
  menuRef = owl.useRef("menu");
82196
+ containerRef = owl.useRef("container");
82120
82197
  state = owl.useState({
82121
82198
  menuItems: this.rootItems,
82122
82199
  title: _t("Menu Bar"),
@@ -82124,6 +82201,7 @@ class RibbonMenu extends owl.Component {
82124
82201
  });
82125
82202
  setup() {
82126
82203
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
82204
+ owl.onMounted(this.updateShadows);
82127
82205
  }
82128
82206
  onExternalClick(ev) {
82129
82207
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -82136,6 +82214,7 @@ class RibbonMenu extends owl.Component {
82136
82214
  this.state.parentState = { ...this.state };
82137
82215
  this.state.menuItems = children;
82138
82216
  this.state.title = menu.name(this.env);
82217
+ this.containerRef.el?.scrollTo({ top: 0 });
82139
82218
  }
82140
82219
  else {
82141
82220
  this.state.menuItems = this.rootItems;
@@ -82157,6 +82236,19 @@ class RibbonMenu extends owl.Component {
82157
82236
  height: `${this.props.height}px`,
82158
82237
  });
82159
82238
  }
82239
+ updateShadows() {
82240
+ if (!this.containerRef.el) {
82241
+ return;
82242
+ }
82243
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
82244
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
82245
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
82246
+ this.containerRef.el.classList.add("scroll-bottom");
82247
+ }
82248
+ if (this.containerRef.el.scrollTop > 0) {
82249
+ this.containerRef.el.classList.add("scroll-top");
82250
+ }
82251
+ }
82160
82252
  onClickBack() {
82161
82253
  if (!this.state.parentState) {
82162
82254
  this.props.onClose();
@@ -82165,6 +82257,7 @@ class RibbonMenu extends owl.Component {
82165
82257
  this.state.menuItems = this.state.parentState.menuItems;
82166
82258
  this.state.title = this.state.parentState.title;
82167
82259
  this.state.parentState = this.state.parentState.parentState;
82260
+ this.containerRef.el?.scrollTo({ top: 0 });
82168
82261
  }
82169
82262
  get backTitle() {
82170
82263
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -82220,6 +82313,11 @@ class SmallBottomBar extends owl.Component {
82220
82313
  ? this.composerFocusStore.focusMode
82221
82314
  : "inactive";
82222
82315
  }
82316
+ get showFxIcon() {
82317
+ return (this.focus === "inactive" &&
82318
+ !this.composerStore.currentContent &&
82319
+ !this.composerStore.placeholder);
82320
+ }
82223
82321
  get rect() {
82224
82322
  return this.composerRef.el
82225
82323
  ? getBoundingRectAsPOJO(this.composerRef.el)
@@ -82235,8 +82333,9 @@ class SmallBottomBar extends owl.Component {
82235
82333
  },
82236
82334
  focus: this.focus,
82237
82335
  composerStore: this.composerStore,
82238
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
82336
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
82239
82337
  focusMode: "contentFocus",
82338
+ selection,
82240
82339
  }),
82241
82340
  isDefaultFocus: false,
82242
82341
  inputStyle: cssPropertiesToCss({
@@ -82244,6 +82343,7 @@ class SmallBottomBar extends owl.Component {
82244
82343
  "max-height": `130px`,
82245
82344
  }),
82246
82345
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82346
+ placeholder: this.composerStore.placeholder,
82247
82347
  };
82248
82348
  }
82249
82349
  get symbols() {
@@ -82262,12 +82362,6 @@ class SmallBottomBar extends owl.Component {
82262
82362
  }
82263
82363
 
82264
82364
  const COMPOSER_MAX_HEIGHT = 300;
82265
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
82266
- const FX_SVG = /*xml*/ `
82267
- <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
82268
- <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'/>
82269
- </svg>
82270
- `;
82271
82365
  css /* scss */ `
82272
82366
  .o-topbar-composer-container {
82273
82367
  height: ${DESKTOP_TOPBAR_TOOLBAR_HEIGHT}px;
@@ -82279,14 +82373,6 @@ css /* scss */ `
82279
82373
  margin-bottom: -1px;
82280
82374
  border: 1px solid;
82281
82375
  font-family: ${DEFAULT_FONT};
82282
-
82283
- /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
82284
- .o-composer:empty:not(:focus):not(.active)::before,
82285
- &.o-topbar-composer-readonly .o-composer:empty::before {
82286
- content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
82287
- position: relative;
82288
- top: 20%;
82289
- }
82290
82376
  }
82291
82377
 
82292
82378
  .user-select-text {
@@ -82319,6 +82405,11 @@ class TopBarComposer extends owl.Component {
82319
82405
  ? this.composerFocusStore.focusMode
82320
82406
  : "inactive";
82321
82407
  }
82408
+ get showFxIcon() {
82409
+ return (this.focus === "inactive" &&
82410
+ !this.composerStore.currentContent &&
82411
+ !this.composerStore.placeholder);
82412
+ }
82322
82413
  get composerStyle() {
82323
82414
  const style = {
82324
82415
  padding: "5px 0px 5px 8px",
@@ -83670,7 +83761,7 @@ class Spreadsheet extends owl.Component {
83670
83761
  SidePanels,
83671
83762
  SpreadsheetDashboard,
83672
83763
  HeaderGroupContainer,
83673
- FullScreenChart,
83764
+ FullScreenFigure,
83674
83765
  };
83675
83766
  sidePanel;
83676
83767
  spreadsheetRef = owl.useRef("spreadsheet");
@@ -88394,6 +88485,7 @@ const components = {
88394
88485
  Grid,
88395
88486
  GridOverlay,
88396
88487
  ScorecardChart,
88488
+ GaugeChartComponent,
88397
88489
  LineConfigPanel,
88398
88490
  BarConfigPanel,
88399
88491
  PieChartDesignPanel,
@@ -88432,7 +88524,7 @@ const components = {
88432
88524
  RadioSelection,
88433
88525
  GeoChartRegionSelectSection,
88434
88526
  ChartDashboardMenu,
88435
- FullScreenChart,
88527
+ FullScreenFigure,
88436
88528
  };
88437
88529
  const hooks = {
88438
88530
  useDragAndDropListItems,
@@ -88532,6 +88624,6 @@ exports.tokenColors = tokenColors;
88532
88624
  exports.tokenize = tokenize;
88533
88625
 
88534
88626
 
88535
- __info__.version = "19.0.4";
88536
- __info__.date = "2025-09-23T12:37:28.362Z";
88537
- __info__.hash = "87b774d";
88627
+ __info__.version = "19.0.6";
88628
+ __info__.date = "2025-10-16T06:39:36.282Z";
88629
+ __info__.hash = "0d4315a";