@odoo/o-spreadsheet 18.4.18 → 18.4.22

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 18.4.18
6
- * @date 2025-11-24T07:42:07.821Z
7
- * @hash 4f83667
5
+ * @version 18.4.22
6
+ * @date 2025-12-26T10:19:07.786Z
7
+ * @hash 6ddac00
8
8
  */
9
9
 
10
10
  'use strict';
@@ -2615,7 +2615,6 @@ const invalidateEvaluationCommands = new Set([
2615
2615
  "REDO",
2616
2616
  "ADD_MERGE",
2617
2617
  "REMOVE_MERGE",
2618
- "DUPLICATE_SHEET",
2619
2618
  "UPDATE_LOCALE",
2620
2619
  "ADD_PIVOT",
2621
2620
  "UPDATE_PIVOT",
@@ -5216,17 +5215,41 @@ function toCriterionDateNumber(dateValue) {
5216
5215
  const today = DateTime.now();
5217
5216
  switch (dateValue) {
5218
5217
  case "today":
5219
- return jsDateToNumber(today);
5220
- case "yesterday":
5221
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
5222
- case "tomorrow":
5223
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
5218
+ return Math.floor(jsDateToNumber(today));
5219
+ case "yesterday": {
5220
+ today.setDate(today.getDate() - 1);
5221
+ return Math.floor(jsDateToNumber(today));
5222
+ }
5223
+ case "tomorrow": {
5224
+ today.setDate(today.getDate() + 1);
5225
+ return Math.floor(jsDateToNumber(today));
5226
+ }
5224
5227
  case "lastWeek":
5225
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
5226
- case "lastMonth":
5227
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
5228
+ today.setDate(today.getDate() - 6);
5229
+ return Math.floor(jsDateToNumber(today));
5230
+ case "lastMonth": {
5231
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
5232
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
5233
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
5234
+ today.setDate(1);
5235
+ }
5236
+ else {
5237
+ today.setDate(today.getDate() + 1);
5238
+ today.setMonth(today.getMonth() - 1);
5239
+ }
5240
+ return Math.floor(jsDateToNumber(today));
5241
+ }
5228
5242
  case "lastYear":
5229
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
5243
+ // Handle leap year case
5244
+ if (today.getMonth() === 1 && today.getDate() === 29) {
5245
+ today.setDate(28);
5246
+ today.setFullYear(today.getFullYear() - 1);
5247
+ }
5248
+ else {
5249
+ today.setDate(today.getDate() + 1);
5250
+ today.setFullYear(today.getFullYear() - 1);
5251
+ }
5252
+ return Math.floor(jsDateToNumber(today));
5230
5253
  }
5231
5254
  }
5232
5255
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -22584,6 +22607,9 @@ function getTreeMapGroupColors(definition, tree) {
22584
22607
  }));
22585
22608
  }
22586
22609
  function getTreeMapColorScale(tree, coloringOption) {
22610
+ if (tree.length === 0) {
22611
+ return undefined;
22612
+ }
22587
22613
  const treeNodesByLevel = pyramidizeTree(tree);
22588
22614
  const nodes = treeNodesByLevel[treeNodesByLevel.length - 1];
22589
22615
  const minValue = Math.min(...nodes.map((node) => node.value));
@@ -24253,11 +24279,18 @@ function chartToImageUrl(runtime, figure, type) {
24253
24279
  // we have to add the canvas to the DOM otherwise it won't be rendered
24254
24280
  document.body.append(div);
24255
24281
  if ("chartJsConfig" in runtime) {
24282
+ const extensionsLoaded = areChartJSExtensionsLoaded();
24283
+ if (!extensionsLoaded) {
24284
+ registerChartJSExtensions();
24285
+ }
24256
24286
  const config = deepCopy(runtime.chartJsConfig);
24257
24287
  config.plugins = [backgroundColorChartJSPlugin];
24258
24288
  const chart = new window.Chart(canvas, config);
24259
24289
  imageContent = chart.toBase64Image();
24260
24290
  chart.destroy();
24291
+ if (!extensionsLoaded) {
24292
+ unregisterChartJsExtensions();
24293
+ }
24261
24294
  }
24262
24295
  else if (type === "scorecard") {
24263
24296
  const design = getScorecardConfiguration(figure, runtime);
@@ -24287,11 +24320,18 @@ async function chartToImageFile(runtime, figure, type) {
24287
24320
  document.body.append(div);
24288
24321
  let chartBlob = null;
24289
24322
  if ("chartJsConfig" in runtime) {
24323
+ const extensionsLoaded = areChartJSExtensionsLoaded();
24324
+ if (!extensionsLoaded) {
24325
+ registerChartJSExtensions();
24326
+ }
24290
24327
  const config = deepCopy(runtime.chartJsConfig);
24291
24328
  config.plugins = [backgroundColorChartJSPlugin];
24292
24329
  const chart = new window.Chart(canvas, config);
24293
24330
  chartBlob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
24294
24331
  chart.destroy();
24332
+ if (!extensionsLoaded) {
24333
+ unregisterChartJsExtensions();
24334
+ }
24295
24335
  }
24296
24336
  else if (type === "scorecard") {
24297
24337
  const design = getScorecardConfiguration(figure, runtime);
@@ -29704,7 +29744,7 @@ function escapeQueryNameSpaces(query) {
29704
29744
  return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
29705
29745
  }
29706
29746
 
29707
- function getChartMenuActions(figureId, onFigureDeleted, env) {
29747
+ function getChartMenuActions(figureId, env) {
29708
29748
  const menuItemSpecs = [
29709
29749
  {
29710
29750
  id: "edit",
@@ -29755,11 +29795,11 @@ function getChartMenuActions(figureId, onFigureDeleted, env) {
29755
29795
  },
29756
29796
  isReadonlyAllowed: true,
29757
29797
  },
29758
- getDeleteMenuItem(figureId, onFigureDeleted, env),
29798
+ getDeleteMenuItem(figureId, env),
29759
29799
  ];
29760
29800
  return createActions(menuItemSpecs).filter((action) => env.model.getters.isReadonly() ? action.isReadonlyAllowed : true);
29761
29801
  }
29762
- function getImageMenuActions(figureId, onFigureDeleted, env) {
29802
+ function getImageMenuActions(figureId, env) {
29763
29803
  const menuItemSpecs = [
29764
29804
  getCopyMenuItem(figureId, env, _t("Image copied to clipboard")),
29765
29805
  getCutMenuItem(figureId, env),
@@ -29804,7 +29844,7 @@ function getImageMenuActions(figureId, onFigureDeleted, env) {
29804
29844
  },
29805
29845
  icon: "o-spreadsheet-Icon.DOWNLOAD",
29806
29846
  },
29807
- getDeleteMenuItem(figureId, onFigureDeleted, env),
29847
+ getDeleteMenuItem(figureId, env),
29808
29848
  ];
29809
29849
  return createActions(menuItemSpecs);
29810
29850
  }
@@ -29840,7 +29880,7 @@ function getCutMenuItem(figureId, env) {
29840
29880
  icon: "o-spreadsheet-Icon.CUT",
29841
29881
  };
29842
29882
  }
29843
- function getDeleteMenuItem(figureId, onFigureDeleted, env) {
29883
+ function getDeleteMenuItem(figureId, env) {
29844
29884
  return {
29845
29885
  id: "delete",
29846
29886
  name: _t("Delete"),
@@ -29850,7 +29890,6 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
29850
29890
  sheetId: env.model.getters.getActiveSheetId(),
29851
29891
  figureId,
29852
29892
  });
29853
- onFigureDeleted();
29854
29893
  },
29855
29894
  icon: "o-spreadsheet-Icon.TRASH",
29856
29895
  };
@@ -30695,7 +30734,7 @@ class ChartDashboardMenu extends owl.Component {
30695
30734
  openContextMenu(ev) {
30696
30735
  this.menuState.isOpen = true;
30697
30736
  this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
30698
- this.menuState.menuItems = getChartMenuActions(this.props.figureUI.id, () => { }, this.env);
30737
+ this.menuState.menuItems = getChartMenuActions(this.props.figureUI.id, this.env);
30699
30738
  }
30700
30739
  get fullScreenMenuItem() {
30701
30740
  const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
@@ -30737,7 +30776,6 @@ class ChartFigure extends owl.Component {
30737
30776
  static template = "o-spreadsheet-ChartFigure";
30738
30777
  static props = {
30739
30778
  figureUI: Object,
30740
- onFigureDeleted: Function,
30741
30779
  };
30742
30780
  static components = { ChartDashboardMenu };
30743
30781
  onDoubleClick() {
@@ -30761,7 +30799,6 @@ class ImageFigure extends owl.Component {
30761
30799
  static template = "o-spreadsheet-ImageFigure";
30762
30800
  static props = {
30763
30801
  figureUI: Object,
30764
- onFigureDeleted: Function,
30765
30802
  };
30766
30803
  static components = {};
30767
30804
  // ---------------------------------------------------------------------------
@@ -30872,13 +30909,11 @@ class FigureComponent extends owl.Component {
30872
30909
  static props = {
30873
30910
  figureUI: Object,
30874
30911
  style: { type: String, optional: true },
30875
- onFigureDeleted: { type: Function, optional: true },
30876
30912
  onMouseDown: { type: Function, optional: true },
30877
30913
  onClickAnchor: { type: Function, optional: true },
30878
30914
  };
30879
30915
  static components = { MenuPopover };
30880
30916
  static defaultProps = {
30881
- onFigureDeleted: () => { },
30882
30917
  onMouseDown: () => { },
30883
30918
  onClickAnchor: () => { },
30884
30919
  };
@@ -30955,9 +30990,6 @@ class FigureComponent extends owl.Component {
30955
30990
  this.props.figureUI.id,
30956
30991
  this.figureRef.el,
30957
30992
  ]);
30958
- owl.onWillUnmount(() => {
30959
- this.props.onFigureDeleted();
30960
- });
30961
30993
  }
30962
30994
  clickAnchor(dirX, dirY, ev) {
30963
30995
  this.props.onClickAnchor(dirX, dirY, ev);
@@ -30981,7 +31013,6 @@ class FigureComponent extends owl.Component {
30981
31013
  sheetId: this.env.model.getters.getActiveSheetId(),
30982
31014
  figureId: this.props.figureUI.id,
30983
31015
  });
30984
- this.props.onFigureDeleted();
30985
31016
  ev.preventDefault();
30986
31017
  ev.stopPropagation();
30987
31018
  break;
@@ -31074,7 +31105,7 @@ class FigureComponent extends owl.Component {
31074
31105
  this.menuState.anchorRect = anchorRect;
31075
31106
  this.menuState.menuItems = figureRegistry
31076
31107
  .get(this.props.figureUI.tag)
31077
- .menuBuilder(this.props.figureUI.id, this.props.onFigureDeleted, this.env);
31108
+ .menuBuilder(this.props.figureUI.id, this.env);
31078
31109
  }
31079
31110
  }
31080
31111
 
@@ -31439,7 +31470,7 @@ criterionEvaluatorRegistry.add("dateIs", {
31439
31470
  return false;
31440
31471
  }
31441
31472
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
31442
- const today = jsDateToRoundNumber(DateTime.now());
31473
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
31443
31474
  return isDateBetween(dateValue, today, criterionValue);
31444
31475
  }
31445
31476
  return areDatesSameDay(dateValue, criterionValue);
@@ -32644,7 +32675,7 @@ class AbstractComposerStore extends SpreadsheetStore {
32644
32675
  }
32645
32676
  this.selectionStart = start;
32646
32677
  this.selectionEnd = end;
32647
- this.editionMode = "editing";
32678
+ this.stopComposerRangeSelection();
32648
32679
  this.computeFormulaCursorContext();
32649
32680
  this.computeParenthesisRelatedToCursor();
32650
32681
  this.updateAutoCompleteProvider();
@@ -33549,7 +33580,8 @@ class Composer extends owl.Component {
33549
33580
  assistantStyle["max-height"] = `${availableSpaceAbove - CLOSE_ICON_RADIUS}px`;
33550
33581
  // render top
33551
33582
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
33552
- assistantStyle.transform = `translate(0, calc(-100% - ${cellHeight + 3}px))`;
33583
+ assistantStyle.top = `-3px`;
33584
+ assistantStyle.transform = `translate(0, -100%)`;
33553
33585
  }
33554
33586
  if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
33555
33587
  // render left
@@ -45240,7 +45272,6 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
45240
45272
  }
45241
45273
  get highlights() {
45242
45274
  const position = this.model.getters.getActivePosition();
45243
- const cell = this.getters.getEvaluatedCell(position);
45244
45275
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
45245
45276
  const zone = spreader
45246
45277
  ? this.model.getters.getSpreadZone(spreader, { ignoreSpillError: true })
@@ -45248,10 +45279,11 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
45248
45279
  if (!zone) {
45249
45280
  return [];
45250
45281
  }
45282
+ const isArrayFormulaBlocked = this.model.getters.isArrayFormulaSpillBlocked(spreader ?? position);
45251
45283
  return [
45252
45284
  {
45253
45285
  range: this.model.getters.getRangeFromZone(position.sheetId, zone),
45254
- dashed: cell.value === CellErrorType.SpilledBlocked,
45286
+ dashed: isArrayFormulaBlocked,
45255
45287
  color: "#17A2B8",
45256
45288
  noFill: true,
45257
45289
  thinLine: true,
@@ -46349,9 +46381,7 @@ css /*SCSS*/ `
46349
46381
  */
46350
46382
  class FiguresContainer extends owl.Component {
46351
46383
  static template = "o-spreadsheet-FiguresContainer";
46352
- static props = {
46353
- onFigureDeleted: Function,
46354
- };
46384
+ static props = {};
46355
46385
  static components = { FigureComponent };
46356
46386
  dnd = owl.useState({
46357
46387
  draggedFigure: undefined,
@@ -46726,16 +46756,16 @@ css /* scss */ `
46726
46756
  `;
46727
46757
  class GridAddRowsFooter extends owl.Component {
46728
46758
  static template = "o-spreadsheet-GridAddRowsFooter";
46729
- static props = {
46730
- focusGrid: Function,
46731
- };
46759
+ static props = {};
46732
46760
  static components = { ValidationMessages };
46761
+ DOMFocusableElementStore;
46733
46762
  inputRef = owl.useRef("inputRef");
46734
46763
  state = owl.useState({
46735
46764
  inputValue: "100",
46736
46765
  errorFlag: false,
46737
46766
  });
46738
46767
  setup() {
46768
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
46739
46769
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
46740
46770
  }
46741
46771
  get addRowsPosition() {
@@ -46753,7 +46783,7 @@ class GridAddRowsFooter extends owl.Component {
46753
46783
  }
46754
46784
  onKeydown(ev) {
46755
46785
  if (ev.key.toUpperCase() === "ESCAPE") {
46756
- this.props.focusGrid();
46786
+ this.focusDefaultElement();
46757
46787
  }
46758
46788
  else if (ev.key.toUpperCase() === "ENTER") {
46759
46789
  this.onConfirm();
@@ -46780,7 +46810,7 @@ class GridAddRowsFooter extends owl.Component {
46780
46810
  quantity,
46781
46811
  dimension: "ROW",
46782
46812
  });
46783
- this.props.focusGrid();
46813
+ this.focusDefaultElement();
46784
46814
  // After adding new rows, scroll down to the new last row
46785
46815
  const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
46786
46816
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
@@ -46793,7 +46823,12 @@ class GridAddRowsFooter extends owl.Component {
46793
46823
  if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) {
46794
46824
  return;
46795
46825
  }
46796
- this.props.focusGrid();
46826
+ this.focusDefaultElement();
46827
+ }
46828
+ focusDefaultElement() {
46829
+ if (document.activeElement === this.inputRef.el) {
46830
+ this.DOMFocusableElementStore.focus();
46831
+ }
46797
46832
  }
46798
46833
  }
46799
46834
 
@@ -47068,7 +47103,6 @@ class GridOverlay extends owl.Component {
47068
47103
  onCellClicked: { type: Function, optional: true },
47069
47104
  onCellRightClicked: { type: Function, optional: true },
47070
47105
  onGridResized: { type: Function, optional: true },
47071
- onFigureDeleted: { type: Function, optional: true },
47072
47106
  onGridMoved: Function,
47073
47107
  gridOverlayDimensions: String,
47074
47108
  slots: { type: Object, optional: true },
@@ -47083,7 +47117,6 @@ class GridOverlay extends owl.Component {
47083
47117
  onCellClicked: () => { },
47084
47118
  onCellRightClicked: () => { },
47085
47119
  onGridResized: () => { },
47086
- onFigureDeleted: () => { },
47087
47120
  };
47088
47121
  gridOverlay = owl.useRef("gridOverlay");
47089
47122
  cellPopovers;
@@ -51942,7 +51975,7 @@ function useHighlights(highlightProvider) {
51942
51975
  }
51943
51976
 
51944
51977
  css /* scss */ `
51945
- .o-cf-preview {
51978
+ .o-spreadsheet .o-cf-preview {
51946
51979
  &.o-cf-cursor-ptr {
51947
51980
  cursor: pointer;
51948
51981
  }
@@ -51950,6 +51983,7 @@ css /* scss */ `
51950
51983
  border-bottom: 1px solid ${GRAY_300};
51951
51984
  height: 80px;
51952
51985
  padding: 10px;
51986
+ box-sizing: border-box;
51953
51987
  position: relative;
51954
51988
  cursor: pointer;
51955
51989
  &:hover,
@@ -51963,7 +51997,6 @@ css /* scss */ `
51963
51997
  .o-cf-preview-icon {
51964
51998
  border: 1px solid ${GRAY_300};
51965
51999
  background-color: #fff;
51966
- position: absolute;
51967
52000
  height: 50px;
51968
52001
  width: 50px;
51969
52002
  .o-icon {
@@ -51972,12 +52005,6 @@ css /* scss */ `
51972
52005
  }
51973
52006
  }
51974
52007
  .o-cf-preview-description {
51975
- left: 65px;
51976
- margin-bottom: auto;
51977
- margin-right: 8px;
51978
- margin-top: auto;
51979
- position: relative;
51980
- width: 142px;
51981
52008
  .o-cf-preview-description-rule {
51982
52009
  margin-bottom: 4px;
51983
52010
  max-height: 2.8em;
@@ -51987,16 +52014,11 @@ css /* scss */ `
51987
52014
  font-size: 12px;
51988
52015
  }
51989
52016
  }
51990
- .o-cf-delete {
51991
- left: 90%;
51992
- top: 39%;
51993
- position: absolute;
51994
- }
51995
52017
  &:not(:hover):not(.o-cf-dragging) .o-cf-drag-handle {
51996
52018
  display: none !important;
51997
52019
  }
51998
52020
  .o-cf-drag-handle {
51999
- left: -8px;
52021
+ left: 2px;
52000
52022
  cursor: move;
52001
52023
  .o-icon {
52002
52024
  width: 6px;
@@ -56041,12 +56063,41 @@ const dateGranularities = [
56041
56063
  pivotRegistry.add("SPREADSHEET", {
56042
56064
  ui: SpreadsheetPivot,
56043
56065
  definition: SpreadsheetPivotRuntimeDefinition,
56044
- externalData: false,
56045
56066
  dateGranularities: [...dateGranularities],
56046
56067
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
56047
56068
  isMeasureCandidate: (field) => field.type !== "boolean",
56048
56069
  isGroupable: () => true,
56070
+ adaptRanges: (getters, definition, applyChange) => {
56071
+ if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
56072
+ return definition;
56073
+ }
56074
+ const { sheetId, zone } = definition.dataSet;
56075
+ const range = getters.getRangeFromZone(sheetId, zone);
56076
+ const adaptedRange = adaptPivotRange(range, applyChange);
56077
+ if (adaptedRange === range) {
56078
+ return definition;
56079
+ }
56080
+ const dataSet = adaptedRange && {
56081
+ sheetId: adaptedRange.sheetId,
56082
+ zone: adaptedRange.zone,
56083
+ };
56084
+ return { ...definition, dataSet };
56085
+ },
56049
56086
  });
56087
+ function adaptPivotRange(range, applyChange) {
56088
+ if (!range) {
56089
+ return undefined;
56090
+ }
56091
+ const change = applyChange(range);
56092
+ switch (change.changeType) {
56093
+ case "NONE":
56094
+ return range;
56095
+ case "REMOVE":
56096
+ return undefined;
56097
+ default:
56098
+ return change.range;
56099
+ }
56100
+ }
56050
56101
 
56051
56102
  class PivotSidePanelStore extends SpreadsheetStore {
56052
56103
  pivotId;
@@ -62199,6 +62250,7 @@ function rangeToMerge(mergeId, range) {
62199
62250
  class RangeAdapter {
62200
62251
  getters;
62201
62252
  providers = [];
62253
+ isAdaptingRanges = false;
62202
62254
  constructor(getters) {
62203
62255
  this.getters = getters;
62204
62256
  }
@@ -62230,6 +62282,9 @@ class RangeAdapter {
62230
62282
  }
62231
62283
  beforeHandle(command) { }
62232
62284
  handle(cmd) {
62285
+ if (this.isAdaptingRanges) {
62286
+ throw new Error("Plugins cannot dispatch commands during adaptRanges phase");
62287
+ }
62233
62288
  const rangeAdapter = getRangeAdapter(cmd);
62234
62289
  if (rangeAdapter?.applyChange) {
62235
62290
  this.executeOnAllRanges(rangeAdapter.applyChange, rangeAdapter.sheetId, rangeAdapter.sheetName);
@@ -62252,10 +62307,12 @@ class RangeAdapter {
62252
62307
  };
62253
62308
  }
62254
62309
  executeOnAllRanges(adaptRange, sheetId, sheetName) {
62310
+ this.isAdaptingRanges = true;
62255
62311
  const func = this.verifyRangeRemoved(adaptRange);
62256
62312
  for (const provider of this.providers) {
62257
62313
  provider(func, sheetId, sheetName);
62258
62314
  }
62315
+ this.isAdaptingRanges = false;
62259
62316
  }
62260
62317
  /**
62261
62318
  * Stores the functions bound to each plugin to be able to iterate over all ranges of the application,
@@ -64291,6 +64348,18 @@ class PivotCorePlugin extends CorePlugin {
64291
64348
  }
64292
64349
  }
64293
64350
  adaptRanges(applyChange) {
64351
+ for (const pivotId in this.pivots) {
64352
+ const definition = deepCopy(this.pivots[pivotId]?.definition);
64353
+ if (!definition) {
64354
+ continue;
64355
+ }
64356
+ const newDefinition = pivotRegistry
64357
+ .get(definition.type)
64358
+ ?.adaptRanges?.(this.getters, definition, applyChange);
64359
+ if (newDefinition && !deepEquals(definition, newDefinition)) {
64360
+ this.history.update("pivots", pivotId, "definition", newDefinition);
64361
+ }
64362
+ }
64294
64363
  for (const sheetId in this.compiledMeasureFormulas) {
64295
64364
  for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
64296
64365
  const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
@@ -64542,20 +64611,6 @@ class SettingsPlugin extends CorePlugin {
64542
64611
  }
64543
64612
  }
64544
64613
 
64545
- function adaptPivotRange(range, applyChange) {
64546
- if (!range) {
64547
- return undefined;
64548
- }
64549
- const change = applyChange(range);
64550
- switch (change.changeType) {
64551
- case "NONE":
64552
- return range;
64553
- case "REMOVE":
64554
- return undefined;
64555
- default:
64556
- return change.range;
64557
- }
64558
- }
64559
64614
  class SpreadsheetPivotCorePlugin extends CorePlugin {
64560
64615
  allowDispatch(cmd) {
64561
64616
  switch (cmd.type) {
@@ -64566,24 +64621,6 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
64566
64621
  }
64567
64622
  return "Success" /* CommandResult.Success */;
64568
64623
  }
64569
- adaptRanges(applyChange) {
64570
- for (const pivotId of this.getters.getPivotIds()) {
64571
- const definition = this.getters.getPivotCoreDefinition(pivotId);
64572
- if (definition.type !== "SPREADSHEET") {
64573
- continue;
64574
- }
64575
- if (definition.dataSet) {
64576
- const { sheetId, zone } = definition.dataSet;
64577
- const range = this.getters.getRangeFromZone(sheetId, zone);
64578
- const adaptedRange = adaptPivotRange(range, applyChange);
64579
- const dataSet = adaptedRange && {
64580
- sheetId: adaptedRange.sheetId,
64581
- zone: adaptedRange.zone,
64582
- };
64583
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
64584
- }
64585
- }
64586
- }
64587
64624
  checkDataSetValidity(definition) {
64588
64625
  if (definition.type === "SPREADSHEET" && definition.dataSet) {
64589
64626
  const { zone, sheetId } = definition.dataSet;
@@ -65925,6 +65962,9 @@ class Evaluator {
65925
65962
  const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
65926
65963
  return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
65927
65964
  }
65965
+ isArrayFormulaSpillBlocked(position) {
65966
+ return this.blockedArrayFormulas.has(position);
65967
+ }
65928
65968
  updateDependencies(position) {
65929
65969
  // removing dependencies is slow because it requires
65930
65970
  // to traverse the entire r-tree.
@@ -65936,13 +65976,8 @@ class Evaluator {
65936
65976
  addDependencies(position, dependencies) {
65937
65977
  this.formulaDependencies().addDependencies(position, dependencies);
65938
65978
  for (const range of dependencies) {
65939
- const sheetId = range.sheetId;
65940
- const { left, bottom, right, top } = range.zone;
65941
- for (let col = left; col <= right; col++) {
65942
- for (let row = top; row <= bottom; row++) {
65943
- this.computeAndSave({ sheetId, col, row });
65944
- }
65945
- }
65979
+ // ensure that all ranges are computed
65980
+ this.compilationParams.ensureRange(range);
65946
65981
  }
65947
65982
  }
65948
65983
  updateCompilationParameters() {
@@ -66145,6 +66180,10 @@ class Evaluator {
66145
66180
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
66146
66181
  const nbColumns = formulaReturn.length;
66147
66182
  const nbRows = formulaReturn[0].length;
66183
+ if (nbRows === 0) {
66184
+ // empty matrix
66185
+ return createEvaluatedCell({ value: 0 }, this.getters.getLocale(), cellData);
66186
+ }
66148
66187
  const resultZone = {
66149
66188
  top: formulaPosition.row,
66150
66189
  bottom: formulaPosition.row + nbRows - 1,
@@ -66420,6 +66459,7 @@ class EvaluationPlugin extends CoreViewPlugin {
66420
66459
  "getEvaluatedCellsPositions",
66421
66460
  "getSpreadZone",
66422
66461
  "getArrayFormulaSpreadingOn",
66462
+ "isArrayFormulaSpillBlocked",
66423
66463
  "isEmpty",
66424
66464
  ];
66425
66465
  shouldRebuildDependenciesGraph = true;
@@ -66532,6 +66572,9 @@ class EvaluationPlugin extends CoreViewPlugin {
66532
66572
  getArrayFormulaSpreadingOn(position) {
66533
66573
  return this.evaluator.getArrayFormulaSpreadingOn(position);
66534
66574
  }
66575
+ isArrayFormulaSpillBlocked(position) {
66576
+ return this.evaluator.isArrayFormulaSpillBlocked(position);
66577
+ }
66535
66578
  /**
66536
66579
  * Check if a zone only contains empty cells
66537
66580
  */
@@ -68581,9 +68624,7 @@ class PivotUIPlugin extends CoreViewPlugin {
68581
68624
  handle(cmd) {
68582
68625
  if (invalidateEvaluationCommands.has(cmd.type)) {
68583
68626
  for (const pivotId of this.getters.getPivotIds()) {
68584
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
68585
- this.setupPivot(pivotId, { recreate: true });
68586
- }
68627
+ this.setupPivot(pivotId, { recreate: true });
68587
68628
  }
68588
68629
  }
68589
68630
  switch (cmd.type) {
@@ -68791,7 +68832,7 @@ class PivotUIPlugin extends CoreViewPlugin {
68791
68832
  pivot.init({ reload: true });
68792
68833
  }
68793
68834
  setupPivot(pivotId, { recreate } = { recreate: false }) {
68794
- const definition = this.getters.getPivotCoreDefinition(pivotId);
68835
+ const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
68795
68836
  if (!(pivotId in this.pivots)) {
68796
68837
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
68797
68838
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -78044,16 +78085,18 @@ class ClickableCellsStore extends SpreadsheetStore {
78044
78085
  get clickableCells() {
78045
78086
  const cells = [];
78046
78087
  const getters = this.getters;
78047
- const sheetId = getters.getActiveSheetId();
78048
78088
  for (const position of this.getters.getVisibleCellPositions()) {
78049
78089
  const item = this.getClickableItem(position);
78050
78090
  if (!item) {
78051
78091
  continue;
78052
78092
  }
78053
78093
  const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
78054
- const zone = getters.expandZone(sheetId, positionToZone(position));
78094
+ const rect = this.getClickableCellRect(position);
78095
+ if (!rect) {
78096
+ continue;
78097
+ }
78055
78098
  cells.push({
78056
- coordinates: getters.getVisibleRect(zone),
78099
+ coordinates: rect,
78057
78100
  position,
78058
78101
  action: item.execute,
78059
78102
  title: title || "",
@@ -78061,6 +78104,31 @@ class ClickableCellsStore extends SpreadsheetStore {
78061
78104
  }
78062
78105
  return cells;
78063
78106
  }
78107
+ getClickableCellRect(position) {
78108
+ const zone = this.getters.expandZone(position.sheetId, positionToZone(position));
78109
+ const clickableRect = this.getters.getVisibleRect(zone);
78110
+ const icons = this.getters.getCellIcons(position);
78111
+ const iconsAtPosition = {
78112
+ center: icons.find((icon) => icon.horizontalAlign === "center"),
78113
+ left: icons.find((icon) => icon.horizontalAlign === "left"),
78114
+ right: icons.find((icon) => icon.horizontalAlign === "right"),
78115
+ };
78116
+ if (iconsAtPosition.center?.onClick) {
78117
+ return undefined;
78118
+ }
78119
+ if (iconsAtPosition.right?.onClick) {
78120
+ const cellRect = this.getters.getRect(zone);
78121
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.right, cellRect);
78122
+ clickableRect.width -= iconRect.width + iconsAtPosition.right.margin;
78123
+ }
78124
+ if (iconsAtPosition.left?.onClick) {
78125
+ const cellRect = this.getters.getRect(zone);
78126
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.left, cellRect);
78127
+ clickableRect.x += iconRect.width + iconsAtPosition.left.margin;
78128
+ clickableRect.width -= iconRect.width + iconsAtPosition.left.margin;
78129
+ }
78130
+ return clickableRect;
78131
+ }
78064
78132
  }
78065
78133
 
78066
78134
  css /* scss */ `
@@ -78817,7 +78885,7 @@ class SmallBottomBar extends owl.Component {
78817
78885
  height: this.focus === "inactive" ? "26px" : "fit-content",
78818
78886
  "max-height": `130px`,
78819
78887
  }),
78820
- showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
78888
+ showAssistant: false, // Hide assistant in small composer as it gets cropped ATM
78821
78889
  placeholder: this.composerStore.placeholder,
78822
78890
  };
78823
78891
  }
@@ -80309,7 +80377,7 @@ class Spreadsheet extends owl.Component {
80309
80377
  document.activeElement?.contains(this.spreadsheetRef.el)) {
80310
80378
  this.focusGrid();
80311
80379
  }
80312
- }, () => [this.env.model.getters.getActiveSheetId()]);
80380
+ });
80313
80381
  owl.useExternalListener(window, "resize", () => this.render(true));
80314
80382
  // For some reason, the wheel event is not properly registered inside templates
80315
80383
  // in Chromium-based browsers based on chromium 125
@@ -84950,6 +85018,6 @@ exports.tokenColors = tokenColors;
84950
85018
  exports.tokenize = tokenize;
84951
85019
 
84952
85020
 
84953
- __info__.version = "18.4.18";
84954
- __info__.date = "2025-11-24T07:42:07.821Z";
84955
- __info__.hash = "4f83667";
85021
+ __info__.version = "18.4.22";
85022
+ __info__.date = "2025-12-26T10:19:07.786Z";
85023
+ __info__.hash = "6ddac00";