@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
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -1382,9 +1382,7 @@ function removeIndexesFromArray(array, indexes) {
1382
1382
  return newArray;
1383
1383
  }
1384
1384
  function insertItemsAtIndex(array, items, index) {
1385
- const newArray = [...array];
1386
- newArray.splice(index, 0, ...items);
1387
- return newArray;
1385
+ return array.slice(0, index).concat(items).concat(array.slice(index));
1388
1386
  }
1389
1387
  function replaceItemAtIndex(array, newItem, index) {
1390
1388
  const newArray = [...array];
@@ -4515,7 +4513,17 @@ function toNumberMatrix(data, argName) {
4515
4513
  return toMatrix(data).map((row) => {
4516
4514
  return row.map((cell) => {
4517
4515
  if (typeof cell.value !== "number") {
4518
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
4516
+ let message = "";
4517
+ if (typeof cell === "object") {
4518
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
4519
+ }
4520
+ else if (typeof cell === "string") {
4521
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
4522
+ }
4523
+ else if (typeof cell === "boolean") {
4524
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
4525
+ }
4526
+ throw new EvaluationError(message);
4519
4527
  }
4520
4528
  return cell.value;
4521
4529
  });
@@ -5634,7 +5642,7 @@ function tokensToTextInternalFormat(tokens) {
5634
5642
  * Replace in place tokens "mm" and "m" that denote minutes in date format with "MM" to avoid confusion with months.
5635
5643
  *
5636
5644
  * As per OpenXML specification, in date formats if a date token "m" or "mm" is followed by a date token "s" or
5637
- * preceded by a data token "h", then it's not a month but an minute.
5645
+ * preceded by a data token "h", then it's not a month but a minute.
5638
5646
  */
5639
5647
  function convertTokensToMinutesInDateFormat(tokens) {
5640
5648
  const dateParts = tokens.filter((token) => token.type === "DATE_PART");
@@ -5677,6 +5685,9 @@ function internalFormatPartToFormat(internalFormat) {
5677
5685
  case "REPEATED_CHAR":
5678
5686
  format += "*" + token.value;
5679
5687
  break;
5688
+ case "DATE_PART":
5689
+ format += token.value === "MM" ? "mm" : token.value; // Convert "MM" back to "mm" for minutes
5690
+ break;
5680
5691
  default:
5681
5692
  format += token.value;
5682
5693
  }
@@ -9527,7 +9538,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9527
9538
  pasteCell(origin, target, clipboardOption) {
9528
9539
  const { sheetId, col, row } = target;
9529
9540
  const targetCell = this.getters.getEvaluatedCell(target);
9530
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9541
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9531
9542
  if (clipboardOption?.pasteOption === "asValue") {
9532
9543
  this.dispatch("UPDATE_CELL", {
9533
9544
  ...target,
@@ -13900,7 +13911,7 @@ const GROWTH = {
13900
13911
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
13901
13912
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
13902
13913
  }
13903
- 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)));
13914
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13904
13915
  },
13905
13916
  };
13906
13917
  // -----------------------------------------------------------------------------
@@ -13973,7 +13984,7 @@ const LINEST = {
13973
13984
  if (dataY.length === 0 || dataY[0].length === 0) {
13974
13985
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13975
13986
  }
13976
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13987
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13977
13988
  },
13978
13989
  isExported: true,
13979
13990
  };
@@ -13992,7 +14003,7 @@ const LOGEST = {
13992
14003
  if (dataY.length === 0 || dataY[0].length === 0) {
13993
14004
  return new EvaluationError(emptyDataErrorMessage("data_y"));
13994
14005
  }
13995
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
14006
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13996
14007
  for (let i = 0; i < coeffs.length; i++) {
13997
14008
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13998
14009
  }
@@ -14613,7 +14624,7 @@ const TREND = {
14613
14624
  if (knownDataY.length === 0 || knownDataY[0].length === 0) {
14614
14625
  return new EvaluationError(emptyDataErrorMessage("known_data_y"));
14615
14626
  }
14616
- 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));
14627
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14617
14628
  },
14618
14629
  };
14619
14630
  // -----------------------------------------------------------------------------
@@ -23134,6 +23145,10 @@ const chartShowValuesPlugin = {
23134
23145
  }
23135
23146
  const ctx = chart.ctx;
23136
23147
  ctx.save();
23148
+ const { left, top, height, width } = chart.chartArea;
23149
+ ctx.beginPath();
23150
+ ctx.rect(left, top, width, height);
23151
+ ctx.clip();
23137
23152
  ctx.textAlign = "center";
23138
23153
  ctx.textBaseline = "middle";
23139
23154
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -24177,7 +24192,7 @@ class ChartJsComponent extends Component {
24177
24192
  this.chart.update();
24178
24193
  }
24179
24194
  hasChartDataChanged() {
24180
- return !deepEquals(this.currentRuntime.chartJsConfig.data, this.chartRuntime.chartJsConfig.data);
24195
+ return !deepEquals(this.getChartDataInRuntime(this.currentRuntime), this.getChartDataInRuntime(this.chartRuntime));
24181
24196
  }
24182
24197
  enableAnimationInChartData(chartData) {
24183
24198
  return {
@@ -24185,6 +24200,17 @@ class ChartJsComponent extends Component {
24185
24200
  options: { ...chartData.options, animation: { animateRotate: true } },
24186
24201
  };
24187
24202
  }
24203
+ getChartDataInRuntime(runtime) {
24204
+ const data = runtime.chartJsConfig.data;
24205
+ return {
24206
+ labels: data.labels,
24207
+ dataset: data.datasets.map((dataset) => ({
24208
+ data: dataset.data,
24209
+ label: dataset.label,
24210
+ tree: dataset.tree,
24211
+ })),
24212
+ };
24213
+ }
24188
24214
  get animationChartId() {
24189
24215
  return this.props.isFullScreen ? this.props.chartId + "-fullscreen" : this.props.chartId;
24190
24216
  }
@@ -24852,6 +24878,7 @@ class ScorecardChart extends Component {
24852
24878
  static template = "o-spreadsheet-ScorecardChart";
24853
24879
  static props = {
24854
24880
  chartId: String,
24881
+ isFullScreen: { type: Boolean, optional: true },
24855
24882
  };
24856
24883
  canvas = useRef("chartContainer");
24857
24884
  get runtime() {
@@ -25607,6 +25634,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
25607
25634
  parser: luxonFormat,
25608
25635
  displayFormats,
25609
25636
  unit: timeUnit ?? false,
25637
+ tooltipFormat: luxonFormat,
25610
25638
  };
25611
25639
  }
25612
25640
  /**
@@ -26675,6 +26703,7 @@ function getLineChartScales(definition, args) {
26675
26703
  };
26676
26704
  Object.assign(scales.x, axis);
26677
26705
  scales.x.ticks.maxTicksLimit = 15;
26706
+ delete scales?.x?.ticks?.callback;
26678
26707
  }
26679
26708
  else if (axisType === "linear") {
26680
26709
  scales.x.type = "linear";
@@ -27615,26 +27644,6 @@ function createBarChartRuntime(chart, getters) {
27615
27644
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
27616
27645
  }
27617
27646
 
27618
- class FullScreenChartStore extends SpreadsheetStore {
27619
- mutators = ["toggleFullScreenChart"];
27620
- fullScreenFigure = undefined;
27621
- toggleFullScreenChart(figureId) {
27622
- if (this.fullScreenFigure?.id === figureId) {
27623
- this.fullScreenFigure = undefined;
27624
- }
27625
- else {
27626
- this.makeFullScreen(figureId);
27627
- }
27628
- }
27629
- makeFullScreen(figureId) {
27630
- const sheetId = this.getters.getActiveSheetId();
27631
- const figure = this.getters.getFigure(sheetId, figureId);
27632
- if (figure) {
27633
- this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
27634
- }
27635
- }
27636
- }
27637
-
27638
27647
  const TREND_LINE_AXES_IDS = [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID];
27639
27648
  const ZOOMABLE_AXIS_IDS = ["x", ...TREND_LINE_AXES_IDS];
27640
27649
  class ZoomableChartStore extends SpreadsheetStore {
@@ -27806,7 +27815,6 @@ chartJsExtensionRegistry.add("zoomWindowPlugin", {
27806
27815
  class ZoomableChartJsComponent extends ChartJsComponent {
27807
27816
  static template = "o-spreadsheet-ZoomableChartJsComponent";
27808
27817
  store;
27809
- fullScreenChartStore;
27810
27818
  masterChartCanvas = useRef("masterChartCanvas");
27811
27819
  masterChart;
27812
27820
  mode;
@@ -27817,7 +27825,6 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27817
27825
  removeEventListeners = () => { };
27818
27826
  setup() {
27819
27827
  this.store = useStore(ZoomableChartStore);
27820
- this.fullScreenChartStore = useStore(FullScreenChartStore);
27821
27828
  super.setup();
27822
27829
  }
27823
27830
  unmount() {
@@ -27832,12 +27839,8 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27832
27839
  `;
27833
27840
  }
27834
27841
  get sliceable() {
27835
- if (this.env.isDashboard()) {
27836
- const fullScreenFigureId = this.fullScreenChartStore.fullScreenFigure?.id;
27837
- const chartFigureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
27838
- if (fullScreenFigureId === chartFigureId) {
27839
- return true;
27840
- }
27842
+ if (this.props.isFullScreen) {
27843
+ return true;
27841
27844
  }
27842
27845
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27843
27846
  return ("zoomable" in definition && definition?.zoomable) ?? false;
@@ -27900,9 +27903,6 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27900
27903
  const xMax = Math.max(...xValues);
27901
27904
  return { xMin, xMax };
27902
27905
  }
27903
- get shouldAnimate() {
27904
- return this.env.model.getters.isDashboard() && !this.sliceable;
27905
- }
27906
27906
  createChart(chartRuntime) {
27907
27907
  const chartData = chartRuntime.chartJsConfig;
27908
27908
  this.isBarChart = chartData.type === "bar";
@@ -27921,6 +27921,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27921
27921
  const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
27922
27922
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27923
27923
  this.resetAxesLimits();
27924
+ if (this.chart?.options) {
27925
+ this.chart.options.animation = false;
27926
+ }
27924
27927
  }
27925
27928
  updateChartJs(chartRuntime) {
27926
27929
  const chartData = chartRuntime.chartJsConfig;
@@ -27954,6 +27957,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27954
27957
  }
27955
27958
  }
27956
27959
  this.resetAxesLimits();
27960
+ if (this.chart?.options) {
27961
+ this.chart.options.animation = false;
27962
+ }
27957
27963
  }
27958
27964
  resetAxesLimits() {
27959
27965
  if (!this.chart) {
@@ -28051,7 +28057,7 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28051
28057
  onPointerDownInMasterChart(ev) {
28052
28058
  this.removeEventListeners();
28053
28059
  const position = ev.offsetX;
28054
- if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
28060
+ if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
28055
28061
  return;
28056
28062
  }
28057
28063
  const { left, right, top, bottom } = this.masterChart.chartArea;
@@ -31609,6 +31615,26 @@ function getCarouselItemTitle(getters, item) {
31609
31615
  return matchedChart.displayName;
31610
31616
  }
31611
31617
 
31618
+ class FullScreenFigureStore extends SpreadsheetStore {
31619
+ mutators = ["toggleFullScreenFigure"];
31620
+ fullScreenFigure = undefined;
31621
+ toggleFullScreenFigure(figureId) {
31622
+ if (this.fullScreenFigure?.id === figureId) {
31623
+ this.fullScreenFigure = undefined;
31624
+ }
31625
+ else {
31626
+ this.makeFullScreen(figureId);
31627
+ }
31628
+ }
31629
+ makeFullScreen(figureId) {
31630
+ const sheetId = this.getters.getActiveSheetId();
31631
+ const figure = this.getters.getFigure(sheetId, figureId);
31632
+ if (figure) {
31633
+ this.fullScreenFigure = { ...figure, x: 0, y: 0, width: 0, height: 0 };
31634
+ }
31635
+ }
31636
+ }
31637
+
31612
31638
  /**
31613
31639
  * Repeatedly calls a callback function with a time delay between calls.
31614
31640
  */
@@ -32346,12 +32372,13 @@ class MenuPopover extends Component {
32346
32372
  class ChartDashboardMenu extends Component {
32347
32373
  static template = "o-spreadsheet-ChartDashboardMenu";
32348
32374
  static components = { MenuPopover };
32349
- static props = { chartId: String };
32375
+ static props = { chartId: String, hasFullScreenButton: { type: Boolean, optional: true } };
32376
+ static defaultProps = { hasFullScreenButton: true };
32350
32377
  fullScreenFigureStore;
32351
32378
  menuState = useState({ isOpen: false, anchorRect: null, menuItems: [] });
32352
32379
  setup() {
32353
32380
  super.setup();
32354
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
32381
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32355
32382
  }
32356
32383
  getMenuItems() {
32357
32384
  return [this.fullScreenMenuItem].filter(isDefined);
@@ -32362,11 +32389,14 @@ class ChartDashboardMenu extends Component {
32362
32389
  }
32363
32390
  openContextMenu(ev) {
32364
32391
  this.menuState.isOpen = true;
32365
- this.menuState.anchorRect = { x: ev.clientX, y: ev.clientY, width: 0, height: 0 };
32392
+ this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32366
32393
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32367
32394
  this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32368
32395
  }
32369
32396
  get fullScreenMenuItem() {
32397
+ if (!this.props.hasFullScreenButton) {
32398
+ return undefined;
32399
+ }
32370
32400
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
32371
32401
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32372
32402
  if (definition.type === "scorecard") {
@@ -32378,7 +32408,7 @@ class ChartDashboardMenu extends Component {
32378
32408
  label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
32379
32409
  class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
32380
32410
  onClick: () => {
32381
- this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32411
+ this.fullScreenFigureStore.toggleFullScreenFigure(figureId);
32382
32412
  },
32383
32413
  };
32384
32414
  }
@@ -32390,6 +32420,8 @@ class CarouselFigure extends Component {
32390
32420
  figureUI: Object,
32391
32421
  onFigureDeleted: Function,
32392
32422
  editFigureStyle: { type: Function, optional: true },
32423
+ isFullScreen: { type: Boolean, optional: true },
32424
+ openContextMenu: { type: Function, optional: true },
32393
32425
  };
32394
32426
  static components = { ChartDashboardMenu, MenuPopover };
32395
32427
  carouselTabsRef = useRef("carouselTabs");
@@ -32397,8 +32429,10 @@ class CarouselFigure extends Component {
32397
32429
  menuState = useState({ isOpen: false, anchorRect: null, menuItems: [] });
32398
32430
  hiddenItems = [];
32399
32431
  animationStore;
32432
+ fullScreenFigureStore;
32400
32433
  setup() {
32401
32434
  this.animationStore = useStore(ChartAnimationStore);
32435
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
32402
32436
  useEffect(() => {
32403
32437
  if (this.selectedCarouselItem?.type === "carouselDataView") {
32404
32438
  this.props.editFigureStyle?.({ "pointer-events": "none" });
@@ -32445,7 +32479,8 @@ class CarouselFigure extends Component {
32445
32479
  item,
32446
32480
  });
32447
32481
  if (item.type === "chart") {
32448
- this.animationStore?.enableAnimationForChart(item.chartId);
32482
+ const animationChartId = item.chartId + (this.props.isFullScreen ? "-fullscreen" : "");
32483
+ this.animationStore?.enableAnimationForChart(animationChartId);
32449
32484
  }
32450
32485
  }
32451
32486
  get headerStyle() {
@@ -32509,6 +32544,23 @@ class CarouselFigure extends Component {
32509
32544
  this.menuState.anchorRect = rect;
32510
32545
  this.menuState.menuItems = createActions(menuItems);
32511
32546
  }
32547
+ toggleFullScreen() {
32548
+ if (this.selectedCarouselItem?.type === "chart") {
32549
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.props.figureUI.id);
32550
+ }
32551
+ }
32552
+ get fullScreenButtonTitle() {
32553
+ return this.props.isFullScreen ? _t("Exit Full Screen") : _t("Full Screen");
32554
+ }
32555
+ get visibleCarouselItems() {
32556
+ return this.carousel.items.filter((item) => item.type === "carouselDataView" && this.props.isFullScreen ? false : true);
32557
+ }
32558
+ openContextMenu(event) {
32559
+ const target = event.currentTarget;
32560
+ if (target) {
32561
+ this.props.openContextMenu?.(getBoundingRectAsPOJO(target));
32562
+ }
32563
+ }
32512
32564
  }
32513
32565
 
32514
32566
  class ChartFigure extends Component {
@@ -32517,6 +32569,8 @@ class ChartFigure extends Component {
32517
32569
  figureUI: Object,
32518
32570
  onFigureDeleted: Function,
32519
32571
  editFigureStyle: { type: Function, optional: true },
32572
+ isFullScreen: { type: Boolean, optional: true },
32573
+ openContextMenu: { type: Function, optional: true },
32520
32574
  };
32521
32575
  static components = { ChartDashboardMenu };
32522
32576
  onDoubleClick() {
@@ -32549,6 +32603,7 @@ class ImageFigure extends Component {
32549
32603
  figureUI: Object,
32550
32604
  onFigureDeleted: Function,
32551
32605
  editFigureStyle: { type: Function, optional: true },
32606
+ openContextMenu: { type: Function, optional: true },
32552
32607
  };
32553
32608
  static components = {};
32554
32609
  // ---------------------------------------------------------------------------
@@ -34600,8 +34655,11 @@ class Composer extends Component {
34600
34655
  }
34601
34656
  const newSelection = this.contentHelper.getCurrentSelection();
34602
34657
  this.props.composerStore.stopComposerRangeSelection();
34603
- this.props.onComposerContentFocused();
34604
- this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34658
+ const isCurrentlyInactive = this.props.composerStore.editionMode === "inactive";
34659
+ this.props.onComposerContentFocused(newSelection);
34660
+ if (!isCurrentlyInactive) {
34661
+ this.props.composerStore.changeComposerCursorSelection(newSelection.start, newSelection.end);
34662
+ }
34605
34663
  this.processTokenAtCursor();
34606
34664
  }
34607
34665
  onDblClick() {
@@ -35096,13 +35154,6 @@ class AbstractComposerStore extends SpreadsheetStore {
35096
35154
  }
35097
35155
  }
35098
35156
  startEdition(text, selection) {
35099
- if (selection) {
35100
- const content = text || this.getComposerContent(this.getters.getActivePosition());
35101
- const validSelection = this.isSelectionValid(content.length, selection.start, selection.end);
35102
- if (!validSelection) {
35103
- return;
35104
- }
35105
- }
35106
35157
  const { col, row } = this.getters.getActivePosition();
35107
35158
  this.model.dispatch("SELECT_FIGURE", { figureId: null });
35108
35159
  this.model.dispatch("SCROLL_TO_CELL", { col, row });
@@ -35159,7 +35210,7 @@ class AbstractComposerStore extends SpreadsheetStore {
35159
35210
  // ---------------------------------------------------------------------------
35160
35211
  get currentContent() {
35161
35212
  if (this.editionMode === "inactive") {
35162
- return this.getComposerContent(this.getters.getActivePosition());
35213
+ return this.getComposerContent(this.getters.getActivePosition()).text;
35163
35214
  }
35164
35215
  return this._currentContent;
35165
35216
  }
@@ -35358,8 +35409,9 @@ class AbstractComposerStore extends SpreadsheetStore {
35358
35409
  this.sheetId = sheetId;
35359
35410
  this.row = row;
35360
35411
  this.editionMode = "editing";
35361
- this.initialContent = this.getComposerContent({ sheetId, col, row });
35362
- this.setContent(str || this.initialContent, selection);
35412
+ const { text, adjustedSelection } = this.getComposerContent({ sheetId, col, row }, selection);
35413
+ this.initialContent = text;
35414
+ this.setContent(str || this.initialContent, adjustedSelection ?? selection);
35363
35415
  this.colorIndexByRange = {};
35364
35416
  const zone = positionToZone({ col: this.col, row: this.row });
35365
35417
  this.captureSelection(zone, col, row);
@@ -35836,7 +35888,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35836
35888
  constructor(get, args) {
35837
35889
  super(get);
35838
35890
  this.args = args;
35839
- this._currentContent = this.getComposerContent();
35891
+ this._currentContent = this.getComposerContent().text;
35840
35892
  }
35841
35893
  getAutoCompleteProviders() {
35842
35894
  const providersDefinitions = super.getAutoCompleteProviders();
@@ -35873,7 +35925,7 @@ class StandaloneComposerStore extends AbstractComposerStore {
35873
35925
  })
35874
35926
  .join("");
35875
35927
  }
35876
- return localizeContent(content, this.getters.getLocale());
35928
+ return { text: localizeContent(content, this.getters.getLocale()) };
35877
35929
  }
35878
35930
  stopEdition() {
35879
35931
  this._stopEdition();
@@ -39551,6 +39603,74 @@ function getPath2D(svgPath) {
39551
39603
  return path2D;
39552
39604
  }
39553
39605
 
39606
+ /**
39607
+ * Get the relative path between two files
39608
+ *
39609
+ * Eg.:
39610
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
39611
+ */
39612
+ function getRelativePath(from, to) {
39613
+ const fromPathParts = from.split("/");
39614
+ const toPathParts = to.split("/");
39615
+ let relPath = "";
39616
+ let startIndex = 0;
39617
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
39618
+ if (fromPathParts[i] === toPathParts[i]) {
39619
+ startIndex++;
39620
+ }
39621
+ else {
39622
+ relPath += "../";
39623
+ }
39624
+ }
39625
+ relPath += toPathParts.slice(startIndex).join("/");
39626
+ return relPath;
39627
+ }
39628
+ /**
39629
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
39630
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
39631
+ *
39632
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
39633
+ */
39634
+ function arrayToObject(array, indexOffset = 0) {
39635
+ const obj = {};
39636
+ for (let i = 0; i < array.length; i++) {
39637
+ if (array[i]) {
39638
+ obj[i + indexOffset] = array[i];
39639
+ }
39640
+ }
39641
+ return obj;
39642
+ }
39643
+ /**
39644
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
39645
+ * Replace with characters understandable by JS
39646
+ */
39647
+ function fixXlsxUnicode(str) {
39648
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
39649
+ return String.fromCharCode(parseInt(code, 16));
39650
+ });
39651
+ }
39652
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
39653
+ function getSheetDataHeader(sheetData, dimension, index) {
39654
+ if (dimension === "COL") {
39655
+ if (!sheetData.cols[index]) {
39656
+ sheetData.cols[index] = {};
39657
+ }
39658
+ return sheetData.cols[index];
39659
+ }
39660
+ if (!sheetData.rows[index]) {
39661
+ sheetData.rows[index] = {};
39662
+ }
39663
+ return sheetData.rows[index];
39664
+ }
39665
+ /** Prefix the string by "=" if the string looks like a formula */
39666
+ function prefixFormulaWithEqual(formula) {
39667
+ if (formula[0] === "=") {
39668
+ return formula;
39669
+ }
39670
+ const tokens = tokenize(formula);
39671
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
39672
+ }
39673
+
39554
39674
  /**
39555
39675
  * Map of the different types of conversions warnings and their name in error messages
39556
39676
  */
@@ -40073,66 +40193,6 @@ function hexaToInt(hex) {
40073
40193
  */
40074
40194
  const DEFAULT_SYSTEM_COLOR = "FF000000";
40075
40195
 
40076
- /**
40077
- * Get the relative path between two files
40078
- *
40079
- * Eg.:
40080
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
40081
- */
40082
- function getRelativePath(from, to) {
40083
- const fromPathParts = from.split("/");
40084
- const toPathParts = to.split("/");
40085
- let relPath = "";
40086
- let startIndex = 0;
40087
- for (let i = 0; i < fromPathParts.length - 1; i++) {
40088
- if (fromPathParts[i] === toPathParts[i]) {
40089
- startIndex++;
40090
- }
40091
- else {
40092
- relPath += "../";
40093
- }
40094
- }
40095
- relPath += toPathParts.slice(startIndex).join("/");
40096
- return relPath;
40097
- }
40098
- /**
40099
- * Convert an array of element into an object where the objects keys were the elements position in the array.
40100
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
40101
- *
40102
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
40103
- */
40104
- function arrayToObject(array, indexOffset = 0) {
40105
- const obj = {};
40106
- for (let i = 0; i < array.length; i++) {
40107
- if (array[i]) {
40108
- obj[i + indexOffset] = array[i];
40109
- }
40110
- }
40111
- return obj;
40112
- }
40113
- /**
40114
- * In xlsx we can have string with unicode characters with the format _x00fa_.
40115
- * Replace with characters understandable by JS
40116
- */
40117
- function fixXlsxUnicode(str) {
40118
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
40119
- return String.fromCharCode(parseInt(code, 16));
40120
- });
40121
- }
40122
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
40123
- function getSheetDataHeader(sheetData, dimension, index) {
40124
- if (dimension === "COL") {
40125
- if (!sheetData.cols[index]) {
40126
- sheetData.cols[index] = {};
40127
- }
40128
- return sheetData.cols[index];
40129
- }
40130
- if (!sheetData.rows[index]) {
40131
- sheetData.rows[index] = {};
40132
- }
40133
- return sheetData.rows[index];
40134
- }
40135
-
40136
40196
  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;
40137
40197
  /**
40138
40198
  * Convert excel format to o_spreadsheet format
@@ -40347,9 +40407,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
40347
40407
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
40348
40408
  continue;
40349
40409
  operator = CF_OPERATOR_TYPE_CONVERSION_MAP[rule.operator];
40350
- values.push(prefixFormula(rule.formula[0]));
40410
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
40351
40411
  if (rule.formula.length === 2) {
40352
- values.push(prefixFormula(rule.formula[1]));
40412
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
40353
40413
  }
40354
40414
  break;
40355
40415
  }
@@ -40507,11 +40567,6 @@ function convertIcons(xlsxIconSet, index) {
40507
40567
  ? ICON_SETS[iconSet].neutral
40508
40568
  : ICON_SETS[iconSet].good;
40509
40569
  }
40510
- /** Prefix the string by "=" if the string looks like a formula */
40511
- function prefixFormula(formula) {
40512
- const tokens = tokenize(formula);
40513
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
40514
- }
40515
40570
  // ---------------------------------------------------------------------------
40516
40571
  // Warnings
40517
40572
  // ---------------------------------------------------------------------------
@@ -40987,7 +41042,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
40987
41042
  dvRules.push(decimalRule);
40988
41043
  break;
40989
41044
  case "list":
40990
- const listRule = convertListrule(dvId++, dv);
41045
+ const listRule = convertListRule(dvId++, dv);
40991
41046
  dvRules.push(listRule);
40992
41047
  break;
40993
41048
  case "date":
@@ -41007,9 +41062,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
41007
41062
  return dvRules;
41008
41063
  }
41009
41064
  function convertDecimalRule(id, dv) {
41010
- const values = [dv.formula1.toString()];
41065
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41011
41066
  if (dv.formula2) {
41012
- values.push(dv.formula2.toString());
41067
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41013
41068
  }
41014
41069
  return {
41015
41070
  id: id.toString(),
@@ -41021,7 +41076,7 @@ function convertDecimalRule(id, dv) {
41021
41076
  },
41022
41077
  };
41023
41078
  }
41024
- function convertListrule(id, dv) {
41079
+ function convertListRule(id, dv) {
41025
41080
  const formula1 = dv.formula1.toString();
41026
41081
  const isRangeRule = rangeReference.test(formula1);
41027
41082
  return {
@@ -41037,9 +41092,9 @@ function convertListrule(id, dv) {
41037
41092
  }
41038
41093
  function convertDateRule(id, dv) {
41039
41094
  let criterion;
41040
- const values = [dv.formula1.toString()];
41095
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
41041
41096
  if (dv.formula2) {
41042
- values.push(dv.formula2.toString());
41097
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
41043
41098
  criterion = {
41044
41099
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
41045
41100
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -41066,7 +41121,7 @@ function convertCustomRule(id, dv) {
41066
41121
  isBlocking: dv.errorStyle !== "warning",
41067
41122
  criterion: {
41068
41123
  type: "customFormula",
41069
- values: [`=${dv.formula1.toString()}`],
41124
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
41070
41125
  },
41071
41126
  };
41072
41127
  }
@@ -49730,39 +49785,63 @@ class CellComposerStore extends AbstractComposerStore {
49730
49785
  this.model.dispatch("AUTOFILL_TABLE_COLUMN", { ...this.currentEditedCell });
49731
49786
  this.setContent("");
49732
49787
  }
49733
- getComposerContent(position) {
49788
+ getComposerContent(position, selection) {
49734
49789
  const locale = this.getters.getLocale();
49735
49790
  const cell = this.getters.getCell(position);
49736
49791
  if (cell?.isFormula) {
49737
49792
  const prettifiedContent = this.getPrettifiedFormula(cell);
49738
- return localizeFormula(prettifiedContent, locale);
49793
+ // when a formula is prettified (multi lines, indented), adapt the cursor position
49794
+ // to take into account line breaks and tabs
49795
+ function adjustCursorIndex(targetIndex) {
49796
+ let adjustedIndex = 0;
49797
+ let originalIndex = 0;
49798
+ while (originalIndex < targetIndex) {
49799
+ adjustedIndex++;
49800
+ const char = prettifiedContent[adjustedIndex];
49801
+ if (char !== "\n" && char !== "\t") {
49802
+ originalIndex++;
49803
+ }
49804
+ }
49805
+ return adjustedIndex;
49806
+ }
49807
+ let adjustedSelection = selection;
49808
+ if (selection) {
49809
+ adjustedSelection = {
49810
+ start: adjustCursorIndex(selection.start),
49811
+ end: adjustCursorIndex(selection.end),
49812
+ };
49813
+ }
49814
+ return {
49815
+ text: localizeFormula(prettifiedContent, locale),
49816
+ adjustedSelection,
49817
+ };
49739
49818
  }
49740
49819
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49741
49820
  if (spreader) {
49742
- return "";
49821
+ return { text: "" };
49743
49822
  }
49744
49823
  const { format, value, type, formattedValue } = this.getters.getEvaluatedCell(position);
49745
49824
  switch (type) {
49746
49825
  case CellValueType.empty:
49747
- return "";
49826
+ return { text: "" };
49748
49827
  case CellValueType.text:
49749
49828
  case CellValueType.error:
49750
- return value;
49829
+ return { text: value };
49751
49830
  case CellValueType.boolean:
49752
- return formattedValue;
49831
+ return { text: formattedValue };
49753
49832
  case CellValueType.number:
49754
49833
  if (format && isDateTimeFormat(format)) {
49755
49834
  if (parseDateTime(formattedValue, locale) !== null) {
49756
49835
  // formatted string can be parsed again
49757
- return formattedValue;
49836
+ return { text: formattedValue };
49758
49837
  }
49759
49838
  // display a simplified and parsable string otherwise
49760
49839
  const timeFormat = Number.isInteger(value)
49761
49840
  ? locale.dateFormat
49762
49841
  : getDateTimeFormat(locale);
49763
- return formatValue(value, { locale, format: timeFormat });
49842
+ return { text: formatValue(value, { locale, format: timeFormat }) };
49764
49843
  }
49765
- return this.numberComposerContent(value, format, locale);
49844
+ return { text: this.numberComposerContent(value, format, locale) };
49766
49845
  }
49767
49846
  }
49768
49847
  getPrettifiedFormula(cell) {
@@ -49931,8 +50010,9 @@ class GridComposer extends Component {
49931
50010
  },
49932
50011
  focus: this.focus,
49933
50012
  isDefaultFocus: true,
49934
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
50013
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
49935
50014
  focusMode: "contentFocus",
50015
+ selection,
49936
50016
  }),
49937
50017
  onComposerCellFocused: (content) => this.composerFocusStore.focusComposer(this.composerInterface, {
49938
50018
  focusMode: "cellFocus",
@@ -57338,12 +57418,13 @@ class DataValidationEditor extends Component {
57338
57418
  onCloseSidePanel: { type: Function, optional: true },
57339
57419
  };
57340
57420
  state = useState({ rule: this.defaultDataValidationRule, errors: [] });
57421
+ editingSheetId;
57341
57422
  setup() {
57423
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
57342
57424
  if (this.props.rule) {
57343
- const sheetId = this.env.model.getters.getActiveSheetId();
57344
57425
  this.state.rule = {
57345
57426
  ...this.props.rule,
57346
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
57427
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
57347
57428
  };
57348
57429
  this.state.rule.criterion.type = this.props.rule.criterion.type;
57349
57430
  }
@@ -57377,7 +57458,6 @@ class DataValidationEditor extends Component {
57377
57458
  const locale = this.env.model.getters.getLocale();
57378
57459
  const criterion = rule.criterion;
57379
57460
  const criterionEvaluator = criterionEvaluatorRegistry.get(criterion.type);
57380
- const sheetId = this.env.model.getters.getActiveSheetId();
57381
57461
  const values = criterion.values
57382
57462
  .slice(0, criterionEvaluator.numberOfValues(criterion))
57383
57463
  .map((value) => value?.trim())
@@ -57385,8 +57465,8 @@ class DataValidationEditor extends Component {
57385
57465
  .map((value) => canonicalizeContent(value, locale));
57386
57466
  rule.criterion = { ...criterion, values };
57387
57467
  return {
57388
- sheetId,
57389
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
57468
+ sheetId: this.editingSheetId,
57469
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
57390
57470
  rule,
57391
57471
  };
57392
57472
  }
@@ -57913,6 +57993,7 @@ css /* scss */ `
57913
57993
  .o-button {
57914
57994
  height: 19px;
57915
57995
  width: 19px;
57996
+ box-sizing: content-box;
57916
57997
  .o-icon {
57917
57998
  height: 14px;
57918
57999
  width: 14px;
@@ -58658,7 +58739,7 @@ class PivotMeasureEditor extends Component {
58658
58739
  return undefined;
58659
58740
  }
58660
58741
  get isCalculatedMeasureInvalid() {
58661
- return this.env.model.getters.getMeasureCompiledFormula(this.props.measure).isBadExpression;
58742
+ return compile(this.props.measure.computedBy?.formula ?? "").isBadExpression;
58662
58743
  }
58663
58744
  }
58664
58745
 
@@ -61536,16 +61617,16 @@ class ClickableCellSortIcon extends Component {
61536
61617
  }
61537
61618
  }
61538
61619
 
61539
- class FullScreenChart extends Component {
61540
- static template = "o-spreadsheet-FullScreenChart";
61620
+ class FullScreenFigure extends Component {
61621
+ static template = "o-spreadsheet-FullScreenFigure";
61541
61622
  static props = {};
61542
- static components = { ChartDashboardMenu };
61543
- fullScreenChartStore;
61544
- ref = useRef("fullScreenChart");
61623
+ static components = { ChartFigure };
61624
+ fullScreenFigureStore;
61625
+ ref = useRef("fullScreenFigure");
61545
61626
  spreadsheetRect = useSpreadsheetRect();
61546
61627
  figureRegistry = figureRegistry;
61547
61628
  setup() {
61548
- this.fullScreenChartStore = useStore(FullScreenChartStore);
61629
+ this.fullScreenFigureStore = useStore(FullScreenFigureStore);
61549
61630
  const animationStore = useStore(ChartAnimationStore);
61550
61631
  let lastFigureId = undefined;
61551
61632
  onWillUpdateProps(() => {
@@ -61557,7 +61638,7 @@ class FullScreenChart extends Component {
61557
61638
  useEffect((el) => el?.focus(), () => [this.ref.el]);
61558
61639
  }
61559
61640
  get figureUI() {
61560
- return this.fullScreenChartStore.fullScreenFigure;
61641
+ return this.fullScreenFigureStore.fullScreenFigure;
61561
61642
  }
61562
61643
  get chartId() {
61563
61644
  if (!this.figureUI)
@@ -61566,7 +61647,7 @@ class FullScreenChart extends Component {
61566
61647
  }
61567
61648
  exitFullScreen() {
61568
61649
  if (this.figureUI) {
61569
- this.fullScreenChartStore.toggleFullScreenChart(this.figureUI.id);
61650
+ this.fullScreenFigureStore.toggleFullScreenFigure(this.figureUI.id);
61570
61651
  }
61571
61652
  }
61572
61653
  onKeyDown(ev) {
@@ -61574,15 +61655,10 @@ class FullScreenChart extends Component {
61574
61655
  this.exitFullScreen();
61575
61656
  }
61576
61657
  }
61577
- get chartComponent() {
61578
- if (!this.chartId)
61658
+ get figureComponent() {
61659
+ if (!this.figureUI)
61579
61660
  return undefined;
61580
- const type = this.env.model.getters.getChartType(this.chartId);
61581
- const component = chartComponentRegistry.get(type);
61582
- if (!component) {
61583
- throw new Error(`Component is not defined for type ${type}`);
61584
- }
61585
- return component;
61661
+ return figureRegistry.get(this.figureUI.tag).Component;
61586
61662
  }
61587
61663
  }
61588
61664
 
@@ -64322,11 +64398,11 @@ class HeaderSizePlugin extends CorePlugin {
64322
64398
  break;
64323
64399
  }
64324
64400
  case "ADD_COLUMNS_ROWS": {
64325
- const sizes = [...this.sizes[cmd.sheetId][cmd.dimension]];
64401
+ const sizes = this.sizes[cmd.sheetId][cmd.dimension];
64326
64402
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
64327
64403
  const baseSize = sizes[cmd.base];
64328
- sizes.splice(addIndex, 0, ...Array(cmd.quantity).fill(baseSize));
64329
- this.history.update("sizes", cmd.sheetId, cmd.dimension, sizes);
64404
+ const newSizes = insertItemsAtIndex(sizes, Array(cmd.quantity).fill(baseSize), addIndex);
64405
+ this.history.update("sizes", cmd.sheetId, cmd.dimension, newSizes);
64330
64406
  break;
64331
64407
  }
64332
64408
  case "RESIZE_COLUMNS_ROWS":
@@ -64477,9 +64553,8 @@ class HeaderVisibilityPlugin extends CorePlugin {
64477
64553
  break;
64478
64554
  }
64479
64555
  case "ADD_COLUMNS_ROWS": {
64480
- const hiddenHeaders = [...this.hiddenHeaders[cmd.sheetId][cmd.dimension]];
64481
64556
  const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
64482
- hiddenHeaders.splice(addIndex, 0, ...Array(cmd.quantity).fill(false));
64557
+ const hiddenHeaders = insertItemsAtIndex([...this.hiddenHeaders[cmd.sheetId][cmd.dimension]], Array(cmd.quantity).fill(false), addIndex);
64483
64558
  this.history.update("hiddenHeaders", cmd.sheetId, cmd.dimension, hiddenHeaders);
64484
64559
  break;
64485
64560
  }
@@ -68663,12 +68738,12 @@ class SpreadsheetRTree {
68663
68738
  this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
68664
68739
  }
68665
68740
  rtreeItemComparer(left, right) {
68666
- return (left.data === right.data &&
68667
- left.boundingBox.sheetId === right.boundingBox.sheetId &&
68741
+ return (left.boundingBox.sheetId === right.boundingBox.sheetId &&
68668
68742
  left.boundingBox?.zone.left === right.boundingBox.zone.left &&
68669
68743
  left.boundingBox?.zone.top === right.boundingBox.zone.top &&
68670
68744
  left.boundingBox?.zone.right === right.boundingBox.zone.right &&
68671
- left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
68745
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom &&
68746
+ deepEquals(left.data, right.data));
68672
68747
  }
68673
68748
  }
68674
68749
  /**
@@ -68741,7 +68816,7 @@ class FormulaDependencyGraph {
68741
68816
  * in the correct order they should be evaluated.
68742
68817
  * This is called a topological ordering (excluding cycles)
68743
68818
  */
68744
- getCellsDependingOn(ranges) {
68819
+ getCellsDependingOn(ranges, ignore) {
68745
68820
  const visited = this.createEmptyPositionSet();
68746
68821
  const queue = Array.from(ranges).reverse();
68747
68822
  while (queue.length > 0) {
@@ -68756,7 +68831,7 @@ class FormulaDependencyGraph {
68756
68831
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
68757
68832
  const nextInQueue = {};
68758
68833
  for (const position of impactedPositions) {
68759
- if (!visited.has(position)) {
68834
+ if (!visited.has(position) && !ignore.has(position)) {
68760
68835
  if (!nextInQueue[position.sheetId]) {
68761
68836
  nextInQueue[position.sheetId] = [];
68762
68837
  }
@@ -69314,7 +69389,7 @@ class Evaluator {
69314
69389
  }
69315
69390
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
69316
69391
  // the result matrix is split in 2 zones to exclude the array formula position
69317
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
69392
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
69318
69393
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
69319
69394
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
69320
69395
  }
@@ -69432,7 +69507,7 @@ class Evaluator {
69432
69507
  for (const sheetId in zonesBySheetIds) {
69433
69508
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
69434
69509
  }
69435
- return this.formulaDependencies().getCellsDependingOn(ranges);
69510
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
69436
69511
  }
69437
69512
  }
69438
69513
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -70943,7 +71018,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
70943
71018
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
70944
71019
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
70945
71020
  if (!parentSpreadingCell) {
70946
- return false;
71021
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
71022
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
70947
71023
  }
70948
71024
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
70949
71025
  return true;
@@ -82115,6 +82191,7 @@ class RibbonMenu extends Component {
82115
82191
  static components = { Menu };
82116
82192
  rootItems = topbarMenuRegistry.getMenuItems();
82117
82193
  menuRef = useRef("menu");
82194
+ containerRef = useRef("container");
82118
82195
  state = useState({
82119
82196
  menuItems: this.rootItems,
82120
82197
  title: _t("Menu Bar"),
@@ -82122,6 +82199,7 @@ class RibbonMenu extends Component {
82122
82199
  });
82123
82200
  setup() {
82124
82201
  useExternalListener(window, "click", this.onExternalClick, { capture: true });
82202
+ onMounted(this.updateShadows);
82125
82203
  }
82126
82204
  onExternalClick(ev) {
82127
82205
  if (!this.menuRef.el?.contains(ev.target)) {
@@ -82134,6 +82212,7 @@ class RibbonMenu extends Component {
82134
82212
  this.state.parentState = { ...this.state };
82135
82213
  this.state.menuItems = children;
82136
82214
  this.state.title = menu.name(this.env);
82215
+ this.containerRef.el?.scrollTo({ top: 0 });
82137
82216
  }
82138
82217
  else {
82139
82218
  this.state.menuItems = this.rootItems;
@@ -82155,6 +82234,19 @@ class RibbonMenu extends Component {
82155
82234
  height: `${this.props.height}px`,
82156
82235
  });
82157
82236
  }
82237
+ updateShadows() {
82238
+ if (!this.containerRef.el) {
82239
+ return;
82240
+ }
82241
+ this.containerRef.el.classList.remove("scroll-top", "scroll-bottom");
82242
+ const maxScroll = this.containerRef.el.scrollHeight - this.containerRef.el.clientHeight || 0;
82243
+ if (this.containerRef.el.scrollTop < maxScroll - 1) {
82244
+ this.containerRef.el.classList.add("scroll-bottom");
82245
+ }
82246
+ if (this.containerRef.el.scrollTop > 0) {
82247
+ this.containerRef.el.classList.add("scroll-top");
82248
+ }
82249
+ }
82158
82250
  onClickBack() {
82159
82251
  if (!this.state.parentState) {
82160
82252
  this.props.onClose();
@@ -82163,6 +82255,7 @@ class RibbonMenu extends Component {
82163
82255
  this.state.menuItems = this.state.parentState.menuItems;
82164
82256
  this.state.title = this.state.parentState.title;
82165
82257
  this.state.parentState = this.state.parentState.parentState;
82258
+ this.containerRef.el?.scrollTo({ top: 0 });
82166
82259
  }
82167
82260
  get backTitle() {
82168
82261
  return this.state.parentState ? _t("Go to previous menu") : _t("Close menu bar");
@@ -82218,6 +82311,11 @@ class SmallBottomBar extends Component {
82218
82311
  ? this.composerFocusStore.focusMode
82219
82312
  : "inactive";
82220
82313
  }
82314
+ get showFxIcon() {
82315
+ return (this.focus === "inactive" &&
82316
+ !this.composerStore.currentContent &&
82317
+ !this.composerStore.placeholder);
82318
+ }
82221
82319
  get rect() {
82222
82320
  return this.composerRef.el
82223
82321
  ? getBoundingRectAsPOJO(this.composerRef.el)
@@ -82233,8 +82331,9 @@ class SmallBottomBar extends Component {
82233
82331
  },
82234
82332
  focus: this.focus,
82235
82333
  composerStore: this.composerStore,
82236
- onComposerContentFocused: () => this.composerFocusStore.focusComposer(this.composerInterface, {
82334
+ onComposerContentFocused: (selection) => this.composerFocusStore.focusComposer(this.composerInterface, {
82237
82335
  focusMode: "contentFocus",
82336
+ selection,
82238
82337
  }),
82239
82338
  isDefaultFocus: false,
82240
82339
  inputStyle: cssPropertiesToCss({
@@ -82242,6 +82341,7 @@ class SmallBottomBar extends Component {
82242
82341
  "max-height": `130px`,
82243
82342
  }),
82244
82343
  showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82344
+ placeholder: this.composerStore.placeholder,
82245
82345
  };
82246
82346
  }
82247
82347
  get symbols() {
@@ -82260,12 +82360,6 @@ class SmallBottomBar extends Component {
82260
82360
  }
82261
82361
 
82262
82362
  const COMPOSER_MAX_HEIGHT = 300;
82263
- /* svg free of use from https://uxwing.com/formula-fx-icon/ */
82264
- const FX_SVG = /*xml*/ `
82265
- <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
82266
- <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'/>
82267
- </svg>
82268
- `;
82269
82363
  css /* scss */ `
82270
82364
  .o-topbar-composer-container {
82271
82365
  height: ${DESKTOP_TOPBAR_TOOLBAR_HEIGHT}px;
@@ -82277,14 +82371,6 @@ css /* scss */ `
82277
82371
  margin-bottom: -1px;
82278
82372
  border: 1px solid;
82279
82373
  font-family: ${DEFAULT_FONT};
82280
-
82281
- /* In readonly we always show the fx icon if the composer is empty, not matter the focus */
82282
- .o-composer:empty:not(:focus):not(.active)::before,
82283
- &.o-topbar-composer-readonly .o-composer:empty::before {
82284
- content: url("data:image/svg+xml,${encodeURIComponent(FX_SVG)}");
82285
- position: relative;
82286
- top: 20%;
82287
- }
82288
82374
  }
82289
82375
 
82290
82376
  .user-select-text {
@@ -82317,6 +82403,11 @@ class TopBarComposer extends Component {
82317
82403
  ? this.composerFocusStore.focusMode
82318
82404
  : "inactive";
82319
82405
  }
82406
+ get showFxIcon() {
82407
+ return (this.focus === "inactive" &&
82408
+ !this.composerStore.currentContent &&
82409
+ !this.composerStore.placeholder);
82410
+ }
82320
82411
  get composerStyle() {
82321
82412
  const style = {
82322
82413
  padding: "5px 0px 5px 8px",
@@ -83668,7 +83759,7 @@ class Spreadsheet extends Component {
83668
83759
  SidePanels,
83669
83760
  SpreadsheetDashboard,
83670
83761
  HeaderGroupContainer,
83671
- FullScreenChart,
83762
+ FullScreenFigure,
83672
83763
  };
83673
83764
  sidePanel;
83674
83765
  spreadsheetRef = useRef("spreadsheet");
@@ -88392,6 +88483,7 @@ const components = {
88392
88483
  Grid,
88393
88484
  GridOverlay,
88394
88485
  ScorecardChart,
88486
+ GaugeChartComponent,
88395
88487
  LineConfigPanel,
88396
88488
  BarConfigPanel,
88397
88489
  PieChartDesignPanel,
@@ -88430,7 +88522,7 @@ const components = {
88430
88522
  RadioSelection,
88431
88523
  GeoChartRegionSelectSection,
88432
88524
  ChartDashboardMenu,
88433
- FullScreenChart,
88525
+ FullScreenFigure,
88434
88526
  };
88435
88527
  const hooks = {
88436
88528
  useDragAndDropListItems,
@@ -88480,6 +88572,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
88480
88572
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
88481
88573
 
88482
88574
 
88483
- __info__.version = "19.0.4";
88484
- __info__.date = "2025-09-23T12:37:28.362Z";
88485
- __info__.hash = "87b774d";
88575
+ __info__.version = "19.0.6";
88576
+ __info__.date = "2025-10-16T06:39:36.282Z";
88577
+ __info__.hash = "0d4315a";