@odoo/o-spreadsheet 18.3.28 → 18.3.32

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.3.28
6
- * @date 2025-12-02T05:34:06.602Z
7
- * @hash a205f91
5
+ * @version 18.3.32
6
+ * @date 2026-01-14T10:00:27.532Z
7
+ * @hash 8d1d321
8
8
  */
9
9
 
10
10
  'use strict';
@@ -6045,17 +6045,41 @@ function toCriterionDateNumber(dateValue) {
6045
6045
  const today = DateTime.now();
6046
6046
  switch (dateValue) {
6047
6047
  case "today":
6048
- return jsDateToNumber(today);
6049
- case "yesterday":
6050
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
6051
- case "tomorrow":
6052
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
6048
+ return Math.floor(jsDateToNumber(today));
6049
+ case "yesterday": {
6050
+ today.setDate(today.getDate() - 1);
6051
+ return Math.floor(jsDateToNumber(today));
6052
+ }
6053
+ case "tomorrow": {
6054
+ today.setDate(today.getDate() + 1);
6055
+ return Math.floor(jsDateToNumber(today));
6056
+ }
6053
6057
  case "lastWeek":
6054
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
6055
- case "lastMonth":
6056
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
6058
+ today.setDate(today.getDate() - 6);
6059
+ return Math.floor(jsDateToNumber(today));
6060
+ case "lastMonth": {
6061
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
6062
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
6063
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
6064
+ today.setDate(1);
6065
+ }
6066
+ else {
6067
+ today.setDate(today.getDate() + 1);
6068
+ today.setMonth(today.getMonth() - 1);
6069
+ }
6070
+ return Math.floor(jsDateToNumber(today));
6071
+ }
6057
6072
  case "lastYear":
6058
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
6073
+ // Handle leap year case
6074
+ if (today.getMonth() === 1 && today.getDate() === 29) {
6075
+ today.setDate(28);
6076
+ today.setFullYear(today.getFullYear() - 1);
6077
+ }
6078
+ else {
6079
+ today.setDate(today.getDate() + 1);
6080
+ today.setFullYear(today.getFullYear() - 1);
6081
+ }
6082
+ return Math.floor(jsDateToNumber(today));
6059
6083
  }
6060
6084
  }
6061
6085
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -18391,9 +18415,10 @@ function assertDomainLength(domain) {
18391
18415
  throw new EvaluationError(_t("Function PIVOT takes an even number of arguments."));
18392
18416
  }
18393
18417
  }
18394
- function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
18418
+ function addPivotDependencies(evalContext, pivotId, forMeasures) {
18395
18419
  //TODO This function can be very costly when used with PIVOT.VALUE and PIVOT.HEADER
18396
18420
  const dependencies = [];
18421
+ const coreDefinition = evalContext.getters.getPivotCoreDefinition(pivotId);
18397
18422
  if (coreDefinition.type === "SPREADSHEET" && coreDefinition.dataSet) {
18398
18423
  const { sheetId, zone } = coreDefinition.dataSet;
18399
18424
  const xc = zoneToXc(zone);
@@ -18410,8 +18435,7 @@ function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
18410
18435
  }
18411
18436
  for (const measure of forMeasures) {
18412
18437
  if (measure.computedBy) {
18413
- const formula = evalContext.getters.getMeasureCompiledFormula(measure);
18414
- dependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
18438
+ dependencies.push(...evalContext.getters.getMeasureFullDependencies(pivotId, measure));
18415
18439
  }
18416
18440
  }
18417
18441
  const originPosition = evalContext.__originCellPosition;
@@ -18846,7 +18870,7 @@ const PIVOT_VALUE = {
18846
18870
  assertDomainLength(domainArgs);
18847
18871
  const pivot = this.getters.getPivot(pivotId);
18848
18872
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
18849
- addPivotDependencies(this, coreDefinition, coreDefinition.measures.filter((m) => m.id === _measure));
18873
+ addPivotDependencies(this, pivotId, coreDefinition.measures.filter((m) => m.id === _measure));
18850
18874
  pivot.init({ reload: pivot.needsReevaluation });
18851
18875
  const error = pivot.assertIsValid({ throwOnError: false });
18852
18876
  if (error) {
@@ -18879,8 +18903,7 @@ const PIVOT_HEADER = {
18879
18903
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
18880
18904
  assertDomainLength(domainArgs);
18881
18905
  const pivot = this.getters.getPivot(_pivotId);
18882
- const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
18883
- addPivotDependencies(this, coreDefinition, []);
18906
+ addPivotDependencies(this, _pivotId, []);
18884
18907
  pivot.init({ reload: pivot.needsReevaluation });
18885
18908
  const error = pivot.assertIsValid({ throwOnError: false });
18886
18909
  if (error) {
@@ -18934,7 +18957,7 @@ const PIVOT = {
18934
18957
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
18935
18958
  const pivot = this.getters.getPivot(pivotId);
18936
18959
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
18937
- addPivotDependencies(this, coreDefinition, coreDefinition.measures);
18960
+ addPivotDependencies(this, pivotId, coreDefinition.measures);
18938
18961
  pivot.init({ reload: pivot.needsReevaluation });
18939
18962
  const error = pivot.assertIsValid({ throwOnError: false });
18940
18963
  if (error) {
@@ -23206,7 +23229,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23206
23229
  }
23207
23230
  captureSelection(zone, col, row) {
23208
23231
  this.model.selection.capture(this, {
23209
- cell: { col: col ?? zone.left, row: row ?? zone.right },
23232
+ cell: { col: col ?? zone.left, row: row ?? zone.top },
23210
23233
  zone,
23211
23234
  }, {
23212
23235
  handleEvent: this.handleEvent.bind(this),
@@ -29440,7 +29463,6 @@ class ChartFigure extends owl.Component {
29440
29463
  static template = "o-spreadsheet-ChartFigure";
29441
29464
  static props = {
29442
29465
  figureUI: Object,
29443
- onFigureDeleted: Function,
29444
29466
  };
29445
29467
  static components = {};
29446
29468
  onDoubleClick() {
@@ -29464,7 +29486,6 @@ class ImageFigure extends owl.Component {
29464
29486
  static template = "o-spreadsheet-ImageFigure";
29465
29487
  static props = {
29466
29488
  figureUI: Object,
29467
- onFigureDeleted: Function,
29468
29489
  };
29469
29490
  static components = {};
29470
29491
  // ---------------------------------------------------------------------------
@@ -30132,7 +30153,7 @@ figureRegistry.add("image", {
30132
30153
  borderWidth: 0,
30133
30154
  menuBuilder: getImageMenuRegistry,
30134
30155
  });
30135
- function getChartMenu(figureId, onFigureDeleted, env) {
30156
+ function getChartMenu(figureId, env) {
30136
30157
  const menuItemSpecs = [
30137
30158
  {
30138
30159
  id: "edit",
@@ -30184,11 +30205,11 @@ function getChartMenu(figureId, onFigureDeleted, env) {
30184
30205
  downloadFile(url, "chart");
30185
30206
  },
30186
30207
  },
30187
- getDeleteMenuItem(figureId, onFigureDeleted, env),
30208
+ getDeleteMenuItem(figureId, env),
30188
30209
  ];
30189
30210
  return createActions(menuItemSpecs);
30190
30211
  }
30191
- function getImageMenuRegistry(figureId, onFigureDeleted, env) {
30212
+ function getImageMenuRegistry(figureId, env) {
30192
30213
  const menuItemSpecs = [
30193
30214
  getCopyMenuItem(figureId, env),
30194
30215
  getCutMenuItem(figureId, env),
@@ -30233,7 +30254,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
30233
30254
  },
30234
30255
  icon: "o-spreadsheet-Icon.DOWNLOAD",
30235
30256
  },
30236
- getDeleteMenuItem(figureId, onFigureDeleted, env),
30257
+ getDeleteMenuItem(figureId, env),
30237
30258
  ];
30238
30259
  return createActions(menuItemSpecs);
30239
30260
  }
@@ -30266,7 +30287,7 @@ function getCutMenuItem(figureId, env) {
30266
30287
  icon: "o-spreadsheet-Icon.CUT",
30267
30288
  };
30268
30289
  }
30269
- function getDeleteMenuItem(figureId, onFigureDeleted, env) {
30290
+ function getDeleteMenuItem(figureId, env) {
30270
30291
  return {
30271
30292
  id: "delete",
30272
30293
  name: _t("Delete"),
@@ -30276,7 +30297,6 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
30276
30297
  sheetId: env.model.getters.getActiveSheetId(),
30277
30298
  figureId,
30278
30299
  });
30279
- onFigureDeleted();
30280
30300
  },
30281
30301
  icon: "o-spreadsheet-Icon.TRASH",
30282
30302
  };
@@ -45378,7 +45398,7 @@ function useHighlights(highlightProvider) {
45378
45398
  }
45379
45399
 
45380
45400
  css /* scss */ `
45381
- .o-cf-preview {
45401
+ .o-spreadsheet .o-cf-preview {
45382
45402
  &.o-cf-cursor-ptr {
45383
45403
  cursor: pointer;
45384
45404
  }
@@ -45386,6 +45406,7 @@ css /* scss */ `
45386
45406
  border-bottom: 1px solid ${GRAY_300};
45387
45407
  height: 80px;
45388
45408
  padding: 10px;
45409
+ box-sizing: border-box;
45389
45410
  position: relative;
45390
45411
  cursor: pointer;
45391
45412
  &:hover,
@@ -45399,7 +45420,6 @@ css /* scss */ `
45399
45420
  .o-cf-preview-icon {
45400
45421
  border: 1px solid ${GRAY_300};
45401
45422
  background-color: #fff;
45402
- position: absolute;
45403
45423
  height: 50px;
45404
45424
  width: 50px;
45405
45425
  .o-icon {
@@ -45408,12 +45428,6 @@ css /* scss */ `
45408
45428
  }
45409
45429
  }
45410
45430
  .o-cf-preview-description {
45411
- left: 65px;
45412
- margin-bottom: auto;
45413
- margin-right: 8px;
45414
- margin-top: auto;
45415
- position: relative;
45416
- width: 142px;
45417
45431
  .o-cf-preview-description-rule {
45418
45432
  margin-bottom: 4px;
45419
45433
  max-height: 2.8em;
@@ -45423,16 +45437,11 @@ css /* scss */ `
45423
45437
  font-size: 12px;
45424
45438
  }
45425
45439
  }
45426
- .o-cf-delete {
45427
- left: 90%;
45428
- top: 39%;
45429
- position: absolute;
45430
- }
45431
45440
  &:not(:hover):not(.o-cf-dragging) .o-cf-drag-handle {
45432
45441
  display: none !important;
45433
45442
  }
45434
45443
  .o-cf-drag-handle {
45435
- left: -8px;
45444
+ left: 2px;
45436
45445
  cursor: move;
45437
45446
  .o-icon {
45438
45447
  width: 6px;
@@ -46421,7 +46430,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
46421
46430
  return false;
46422
46431
  }
46423
46432
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
46424
- const today = jsDateToRoundNumber(DateTime.now());
46433
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
46425
46434
  return isDateBetween(dateValue, today, criterionValue);
46426
46435
  }
46427
46436
  return areDatesSameDay(dateValue, criterionValue);
@@ -47608,6 +47617,12 @@ class FindAndReplaceStore extends SpreadsheetStore {
47608
47617
  case "ACTIVATE_SHEET":
47609
47618
  this.isSearchDirty = true;
47610
47619
  this.shouldFinalizeUpdateSelection = true;
47620
+ if (this.searchOptions.specificRange) {
47621
+ this.searchOptions.specificRange = {
47622
+ ...this.searchOptions.specificRange,
47623
+ sheetId: this.getters.getActiveSheetId(),
47624
+ };
47625
+ }
47611
47626
  break;
47612
47627
  case "REPLACE_SEARCH":
47613
47628
  for (const match of cmd.matches) {
@@ -48029,9 +48044,20 @@ class FindAndReplacePanel extends owl.Component {
48029
48044
  const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.state.dataRange);
48030
48045
  this.store.updateSearchOptions({ specificRange });
48031
48046
  }
48047
+ get specificRange() {
48048
+ const range = this.store.searchOptions.specificRange;
48049
+ return range ? this.env.model.getters.getRangeString(range, "forceSheetReference") : "";
48050
+ }
48032
48051
  get pendingSearch() {
48033
48052
  return this.updateSearchContent.isDebouncePending();
48034
48053
  }
48054
+ get selectionInputKey() {
48055
+ // Selections input are made to work with objects linked to a sheet id. They store the active sheet id at their creation,
48056
+ // and have specific behaviour linked to it (eg. go back to the initial sheet after confirmation).
48057
+ // We don't want all those behaviors here, so we force the recreation of the component when the active sheet changes.
48058
+ // The only drawback is that the input loses focus when changing sheet.
48059
+ return this.env.model.getters.getActiveSheetId();
48060
+ }
48035
48061
  }
48036
48062
 
48037
48063
  css /* scss */ `
@@ -50278,7 +50304,37 @@ pivotRegistry.add("SPREADSHEET", {
50278
50304
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
50279
50305
  isMeasureCandidate: (field) => field.type !== "boolean",
50280
50306
  isGroupable: () => true,
50307
+ adaptRanges: (getters, definition, applyChange) => {
50308
+ if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
50309
+ return definition;
50310
+ }
50311
+ const { sheetId, zone } = definition.dataSet;
50312
+ const range = getters.getRangeFromZone(sheetId, zone);
50313
+ const adaptedRange = adaptPivotRange(range, applyChange);
50314
+ if (adaptedRange === range) {
50315
+ return definition;
50316
+ }
50317
+ const dataSet = adaptedRange && {
50318
+ sheetId: adaptedRange.sheetId,
50319
+ zone: adaptedRange.zone,
50320
+ };
50321
+ return { ...definition, dataSet };
50322
+ },
50281
50323
  });
50324
+ function adaptPivotRange(range, applyChange) {
50325
+ if (!range) {
50326
+ return undefined;
50327
+ }
50328
+ const change = applyChange(range);
50329
+ switch (change.changeType) {
50330
+ case "NONE":
50331
+ return range;
50332
+ case "REMOVE":
50333
+ return undefined;
50334
+ default:
50335
+ return change.range;
50336
+ }
50337
+ }
50282
50338
 
50283
50339
  class PivotSidePanelStore extends SpreadsheetStore {
50284
50340
  pivotId;
@@ -51918,13 +51974,11 @@ class FigureComponent extends owl.Component {
51918
51974
  static props = {
51919
51975
  figureUI: Object,
51920
51976
  style: { type: String, optional: true },
51921
- onFigureDeleted: { type: Function, optional: true },
51922
51977
  onMouseDown: { type: Function, optional: true },
51923
51978
  onClickAnchor: { type: Function, optional: true },
51924
51979
  };
51925
51980
  static components = { Menu };
51926
51981
  static defaultProps = {
51927
- onFigureDeleted: () => { },
51928
51982
  onMouseDown: () => { },
51929
51983
  onClickAnchor: () => { },
51930
51984
  };
@@ -52002,9 +52056,6 @@ class FigureComponent extends owl.Component {
52002
52056
  this.props.figureUI.id,
52003
52057
  this.figureRef.el,
52004
52058
  ]);
52005
- owl.onWillUnmount(() => {
52006
- this.props.onFigureDeleted();
52007
- });
52008
52059
  }
52009
52060
  clickAnchor(dirX, dirY, ev) {
52010
52061
  this.props.onClickAnchor(dirX, dirY, ev);
@@ -52021,7 +52072,6 @@ class FigureComponent extends owl.Component {
52021
52072
  sheetId: this.env.model.getters.getActiveSheetId(),
52022
52073
  figureId: this.props.figureUI.id,
52023
52074
  });
52024
- this.props.onFigureDeleted();
52025
52075
  ev.preventDefault();
52026
52076
  ev.stopPropagation();
52027
52077
  break;
@@ -52114,7 +52164,7 @@ class FigureComponent extends owl.Component {
52114
52164
  this.menuState.anchorRect = anchorRect;
52115
52165
  this.menuState.menuItems = figureRegistry
52116
52166
  .get(this.props.figureUI.tag)
52117
- .menuBuilder(this.props.figureUI.id, this.props.onFigureDeleted, this.env);
52167
+ .menuBuilder(this.props.figureUI.id, this.env);
52118
52168
  }
52119
52169
  }
52120
52170
 
@@ -52227,21 +52277,20 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
52227
52277
  this.highlightStore.register(this);
52228
52278
  }
52229
52279
  get highlights() {
52230
- let zone;
52231
52280
  const position = this.model.getters.getActivePosition();
52232
- const cell = this.getters.getEvaluatedCell(position);
52233
52281
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
52234
- zone = spreader
52282
+ const zone = spreader
52235
52283
  ? this.model.getters.getSpreadZone(spreader, { ignoreSpillError: true })
52236
52284
  : this.model.getters.getSpreadZone(position, { ignoreSpillError: true });
52237
52285
  if (!zone) {
52238
52286
  return [];
52239
52287
  }
52288
+ const isArrayFormulaBlocked = this.model.getters.isArrayFormulaSpillBlocked(spreader ?? position);
52240
52289
  return [
52241
52290
  {
52242
52291
  sheetId: position.sheetId,
52243
52292
  zone,
52244
- dashed: cell.value === CellErrorType.SpilledBlocked,
52293
+ dashed: isArrayFormulaBlocked,
52245
52294
  color: "#17A2B8",
52246
52295
  noFill: true,
52247
52296
  thinLine: true,
@@ -53409,9 +53458,7 @@ css /*SCSS*/ `
53409
53458
  */
53410
53459
  class FiguresContainer extends owl.Component {
53411
53460
  static template = "o-spreadsheet-FiguresContainer";
53412
- static props = {
53413
- onFigureDeleted: Function,
53414
- };
53461
+ static props = {};
53415
53462
  static components = { FigureComponent };
53416
53463
  dnd = owl.useState({
53417
53464
  draggedFigure: undefined,
@@ -53802,16 +53849,16 @@ css /* scss */ `
53802
53849
  `;
53803
53850
  class GridAddRowsFooter extends owl.Component {
53804
53851
  static template = "o-spreadsheet-GridAddRowsFooter";
53805
- static props = {
53806
- focusGrid: Function,
53807
- };
53852
+ static props = {};
53808
53853
  static components = { ValidationMessages };
53854
+ DOMFocusableElementStore;
53809
53855
  inputRef = owl.useRef("inputRef");
53810
53856
  state = owl.useState({
53811
53857
  inputValue: "100",
53812
53858
  errorFlag: false,
53813
53859
  });
53814
53860
  setup() {
53861
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
53815
53862
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
53816
53863
  }
53817
53864
  get addRowsPosition() {
@@ -53829,7 +53876,7 @@ class GridAddRowsFooter extends owl.Component {
53829
53876
  }
53830
53877
  onKeydown(ev) {
53831
53878
  if (ev.key.toUpperCase() === "ESCAPE") {
53832
- this.props.focusGrid();
53879
+ this.focusDefaultElement();
53833
53880
  }
53834
53881
  else if (ev.key.toUpperCase() === "ENTER") {
53835
53882
  this.onConfirm();
@@ -53856,7 +53903,7 @@ class GridAddRowsFooter extends owl.Component {
53856
53903
  quantity,
53857
53904
  dimension: "ROW",
53858
53905
  });
53859
- this.props.focusGrid();
53906
+ this.focusDefaultElement();
53860
53907
  // After adding new rows, scroll down to the new last row
53861
53908
  const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
53862
53909
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
@@ -53869,7 +53916,12 @@ class GridAddRowsFooter extends owl.Component {
53869
53916
  if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) {
53870
53917
  return;
53871
53918
  }
53872
- this.props.focusGrid();
53919
+ this.focusDefaultElement();
53920
+ }
53921
+ focusDefaultElement() {
53922
+ if (document.activeElement === this.inputRef.el) {
53923
+ this.DOMFocusableElementStore.focus();
53924
+ }
53873
53925
  }
53874
53926
  }
53875
53927
 
@@ -54114,7 +54166,6 @@ class GridOverlay extends owl.Component {
54114
54166
  onCellClicked: { type: Function, optional: true },
54115
54167
  onCellRightClicked: { type: Function, optional: true },
54116
54168
  onGridResized: { type: Function, optional: true },
54117
- onFigureDeleted: { type: Function, optional: true },
54118
54169
  onGridMoved: Function,
54119
54170
  gridOverlayDimensions: String,
54120
54171
  slots: { type: Object, optional: true },
@@ -54130,7 +54181,6 @@ class GridOverlay extends owl.Component {
54130
54181
  onCellClicked: () => { },
54131
54182
  onCellRightClicked: () => { },
54132
54183
  onGridResized: () => { },
54133
- onFigureDeleted: () => { },
54134
54184
  };
54135
54185
  gridOverlay = owl.useRef("gridOverlay");
54136
54186
  gridOverlayRect = useAbsoluteBoundingRect(this.gridOverlay);
@@ -60699,6 +60749,7 @@ function rangeToMerge(mergeId, range) {
60699
60749
  class RangeAdapter {
60700
60750
  getters;
60701
60751
  providers = [];
60752
+ isAdaptingRanges = false;
60702
60753
  constructor(getters) {
60703
60754
  this.getters = getters;
60704
60755
  }
@@ -60730,6 +60781,9 @@ class RangeAdapter {
60730
60781
  }
60731
60782
  beforeHandle(command) { }
60732
60783
  handle(cmd) {
60784
+ if (this.isAdaptingRanges) {
60785
+ throw new Error("Plugins cannot dispatch commands during adaptRanges phase");
60786
+ }
60733
60787
  const rangeAdapter = getRangeAdapter(cmd);
60734
60788
  if (rangeAdapter?.applyChange) {
60735
60789
  this.executeOnAllRanges(rangeAdapter.applyChange, rangeAdapter.sheetId, rangeAdapter.sheetName);
@@ -60752,10 +60806,12 @@ class RangeAdapter {
60752
60806
  };
60753
60807
  }
60754
60808
  executeOnAllRanges(adaptRange, sheetId, sheetName) {
60809
+ this.isAdaptingRanges = true;
60755
60810
  const func = this.verifyRangeRemoved(adaptRange);
60756
60811
  for (const provider of this.providers) {
60757
60812
  provider(func, sheetId, sheetName);
60758
60813
  }
60814
+ this.isAdaptingRanges = false;
60759
60815
  }
60760
60816
  /**
60761
60817
  * Stores the functions bound to each plugin to be able to iterate over all ranges of the application,
@@ -62703,6 +62759,7 @@ class PivotCorePlugin extends CorePlugin {
62703
62759
  "getMeasureCompiledFormula",
62704
62760
  "getPivotName",
62705
62761
  "isExistingPivot",
62762
+ "getMeasureFullDependencies",
62706
62763
  ];
62707
62764
  nextFormulaId = 1;
62708
62765
  pivots = {};
@@ -62785,15 +62842,32 @@ class PivotCorePlugin extends CorePlugin {
62785
62842
  }
62786
62843
  case "UPDATE_PIVOT": {
62787
62844
  this.history.update("pivots", cmd.pivotId, "definition", this.repairSortedColumn(deepCopy(cmd.pivot)));
62788
- this.compileCalculatedMeasures(cmd.pivot.measures);
62845
+ this.compileCalculatedMeasures(cmd.pivotId, cmd.pivot.measures);
62789
62846
  break;
62790
62847
  }
62791
62848
  }
62792
62849
  }
62793
62850
  adaptRanges(applyChange) {
62794
- for (const sheetId in this.compiledMeasureFormulas) {
62795
- for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
62796
- const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
62851
+ for (const pivotId in this.pivots) {
62852
+ const definition = deepCopy(this.pivots[pivotId]?.definition);
62853
+ if (!definition) {
62854
+ continue;
62855
+ }
62856
+ const newDefinition = pivotRegistry
62857
+ .get(definition.type)
62858
+ ?.adaptRanges?.(this.getters, definition, applyChange);
62859
+ if (newDefinition && !deepEquals(definition, newDefinition)) {
62860
+ this.history.update("pivots", pivotId, "definition", newDefinition);
62861
+ }
62862
+ }
62863
+ for (const pivotId in this.compiledMeasureFormulas) {
62864
+ for (const measureId in this.compiledMeasureFormulas[pivotId]) {
62865
+ const measure = this.pivots[pivotId]?.definition.measures.find((m) => m.id === measureId);
62866
+ if (!measure || !measure.computedBy) {
62867
+ continue;
62868
+ }
62869
+ const sheetId = measure.computedBy.sheetId;
62870
+ const compiledFormula = this.compiledMeasureFormulas[pivotId][measureId].formula;
62797
62871
  const newDependencies = [];
62798
62872
  for (const range of compiledFormula.dependencies) {
62799
62873
  const change = applyChange(range);
@@ -62805,8 +62879,9 @@ class PivotCorePlugin extends CorePlugin {
62805
62879
  }
62806
62880
  }
62807
62881
  const newFormulaString = this.getters.getFormulaString(sheetId, compiledFormula.tokens, newDependencies);
62808
- if (newFormulaString !== formulaString) {
62809
- this.replaceMeasureFormula(sheetId, formulaString, newFormulaString);
62882
+ const oldFormulaString = measure.computedBy.formula;
62883
+ if (newFormulaString !== oldFormulaString) {
62884
+ this.replaceMeasureFormula(pivotId, measure, newFormulaString);
62810
62885
  }
62811
62886
  }
62812
62887
  }
@@ -62844,12 +62919,17 @@ class PivotCorePlugin extends CorePlugin {
62844
62919
  isExistingPivot(pivotId) {
62845
62920
  return pivotId in this.pivots;
62846
62921
  }
62847
- getMeasureCompiledFormula(measure) {
62922
+ getMeasureCompiledFormula(pivotId, measure) {
62923
+ if (!measure.computedBy) {
62924
+ throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
62925
+ }
62926
+ return this.compiledMeasureFormulas[pivotId][measure.id].formula;
62927
+ }
62928
+ getMeasureFullDependencies(pivotId, measure) {
62848
62929
  if (!measure.computedBy) {
62849
62930
  throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
62850
62931
  }
62851
- const sheetId = measure.computedBy.sheetId;
62852
- return this.compiledMeasureFormulas[sheetId][measure.computedBy.formula];
62932
+ return this.compiledMeasureFormulas[pivotId][measure.id].dependencies;
62853
62933
  }
62854
62934
  // -------------------------------------------------------------------------
62855
62935
  // Private
@@ -62859,18 +62939,42 @@ class PivotCorePlugin extends CorePlugin {
62859
62939
  definition: this.repairSortedColumn(deepCopy(pivot)),
62860
62940
  formulaId,
62861
62941
  });
62862
- this.compileCalculatedMeasures(pivot.measures);
62942
+ this.compileCalculatedMeasures(pivotId, pivot.measures);
62863
62943
  this.history.update("formulaIds", formulaId, pivotId);
62864
62944
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
62865
62945
  }
62866
- compileCalculatedMeasures(measures) {
62946
+ compileCalculatedMeasures(pivotId, measures) {
62867
62947
  for (const measure of measures) {
62868
62948
  if (measure.computedBy) {
62869
- const sheetId = measure.computedBy.sheetId;
62870
62949
  const compiledFormula = this.compileMeasureFormula(measure.computedBy.sheetId, measure.computedBy.formula);
62871
- this.history.update("compiledMeasureFormulas", sheetId, measure.computedBy.formula, compiledFormula);
62950
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "formula", compiledFormula);
62951
+ }
62952
+ }
62953
+ for (const measure of measures) {
62954
+ if (measure.computedBy) {
62955
+ const dependencies = this.computeMeasureFullDependencies(pivotId, measure);
62956
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "dependencies", dependencies);
62957
+ }
62958
+ }
62959
+ }
62960
+ computeMeasureFullDependencies(pivotId, measure, exploredMeasures = new Set()) {
62961
+ const rangeDependencies = [];
62962
+ const definition = this.getPivotCoreDefinition(pivotId);
62963
+ const formula = this.getMeasureCompiledFormula(pivotId, measure);
62964
+ exploredMeasures.add(measure.id);
62965
+ for (const token of formula.tokens) {
62966
+ if (token.type !== "SYMBOL") {
62967
+ continue;
62968
+ }
62969
+ const otherMeasure = definition.measures.find((measureCandidate) => getCanonicalSymbolName(measureCandidate.id) === token.value &&
62970
+ measure.id !== measureCandidate.id);
62971
+ if (!otherMeasure || exploredMeasures.has(otherMeasure.id) || !otherMeasure.computedBy) {
62972
+ continue;
62872
62973
  }
62974
+ rangeDependencies.push(...this.computeMeasureFullDependencies(pivotId, otherMeasure, exploredMeasures));
62873
62975
  }
62976
+ rangeDependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
62977
+ return rangeDependencies;
62874
62978
  }
62875
62979
  insertPivot(position, formulaId, table) {
62876
62980
  this.resizeSheet(position.sheetId, position, table);
@@ -62930,28 +63034,17 @@ class PivotCorePlugin extends CorePlugin {
62930
63034
  dependencies: rangeDependencies,
62931
63035
  };
62932
63036
  }
62933
- replaceMeasureFormula(sheetId, formulaString, newFormulaString) {
62934
- this.history.update("compiledMeasureFormulas", sheetId, formulaString, undefined);
62935
- this.history.update("compiledMeasureFormulas", sheetId, newFormulaString, this.compileMeasureFormula(sheetId, newFormulaString));
62936
- for (const pivotId in this.pivots) {
62937
- const pivot = this.pivots[pivotId];
62938
- if (!pivot) {
62939
- continue;
62940
- }
62941
- const def = deepCopy(pivot.definition);
62942
- for (const measure of def.measures) {
62943
- if (measure.computedBy?.formula === formulaString) {
62944
- const measureIndex = def.measures.indexOf(measure);
62945
- if (measureIndex !== -1) {
62946
- def.measures[measureIndex].computedBy = {
62947
- formula: newFormulaString,
62948
- sheetId,
62949
- };
62950
- }
62951
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
62952
- }
62953
- }
63037
+ replaceMeasureFormula(pivotId, measure, newFormulaString) {
63038
+ const pivot = this.pivots[pivotId];
63039
+ if (!pivot) {
63040
+ return;
62954
63041
  }
63042
+ const measureIndex = pivot.definition.measures.indexOf(measure);
63043
+ this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", {
63044
+ formula: newFormulaString,
63045
+ sheetId: measure.computedBy.sheetId,
63046
+ });
63047
+ this.compileCalculatedMeasures(pivotId, pivot.definition.measures);
62955
63048
  }
62956
63049
  checkSortedColumnInMeasures(definition) {
62957
63050
  definition = this.repairSortedColumn(definition);
@@ -63075,20 +63168,6 @@ class SettingsPlugin extends CorePlugin {
63075
63168
  }
63076
63169
  }
63077
63170
 
63078
- function adaptPivotRange(range, applyChange) {
63079
- if (!range) {
63080
- return undefined;
63081
- }
63082
- const change = applyChange(range);
63083
- switch (change.changeType) {
63084
- case "NONE":
63085
- return range;
63086
- case "REMOVE":
63087
- return undefined;
63088
- default:
63089
- return change.range;
63090
- }
63091
- }
63092
63171
  class SpreadsheetPivotCorePlugin extends CorePlugin {
63093
63172
  allowDispatch(cmd) {
63094
63173
  switch (cmd.type) {
@@ -63099,27 +63178,6 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
63099
63178
  }
63100
63179
  return "Success" /* CommandResult.Success */;
63101
63180
  }
63102
- adaptRanges(applyChange) {
63103
- for (const pivotId of this.getters.getPivotIds()) {
63104
- const definition = this.getters.getPivotCoreDefinition(pivotId);
63105
- if (definition.type !== "SPREADSHEET") {
63106
- continue;
63107
- }
63108
- if (definition.dataSet) {
63109
- const { sheetId, zone } = definition.dataSet;
63110
- const range = this.getters.getRangeFromZone(sheetId, zone);
63111
- const adaptedRange = adaptPivotRange(range, applyChange);
63112
- if (adaptedRange === range) {
63113
- return;
63114
- }
63115
- const dataSet = adaptedRange && {
63116
- sheetId: adaptedRange.sheetId,
63117
- zone: adaptedRange.zone,
63118
- };
63119
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
63120
- }
63121
- }
63122
- }
63123
63181
  checkDataSetValidity(definition) {
63124
63182
  if (definition.type === "SPREADSHEET" && definition.dataSet) {
63125
63183
  const { zone, sheetId } = definition.dataSet;
@@ -64461,6 +64519,9 @@ class Evaluator {
64461
64519
  const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
64462
64520
  return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
64463
64521
  }
64522
+ isArrayFormulaSpillBlocked(position) {
64523
+ return this.blockedArrayFormulas.has(position);
64524
+ }
64464
64525
  updateDependencies(position) {
64465
64526
  // removing dependencies is slow because it requires
64466
64527
  // to traverse the entire r-tree.
@@ -64472,13 +64533,8 @@ class Evaluator {
64472
64533
  addDependencies(position, dependencies) {
64473
64534
  this.formulaDependencies().addDependencies(position, dependencies);
64474
64535
  for (const range of dependencies) {
64475
- const sheetId = range.sheetId;
64476
- const { left, bottom, right, top } = range.zone;
64477
- for (let col = left; col <= right; col++) {
64478
- for (let row = top; row <= bottom; row++) {
64479
- this.computeAndSave({ sheetId, col, row });
64480
- }
64481
- }
64536
+ // ensure that all ranges are computed
64537
+ this.compilationParams.ensureRange(range);
64482
64538
  }
64483
64539
  }
64484
64540
  updateCompilationParameters() {
@@ -64681,6 +64737,10 @@ class Evaluator {
64681
64737
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
64682
64738
  const nbColumns = formulaReturn.length;
64683
64739
  const nbRows = formulaReturn[0].length;
64740
+ if (nbRows === 0) {
64741
+ // empty matrix
64742
+ return createEvaluatedCell({ value: 0 }, this.getters.getLocale(), cellData);
64743
+ }
64684
64744
  const resultZone = {
64685
64745
  top: formulaPosition.row,
64686
64746
  bottom: formulaPosition.row + nbRows - 1,
@@ -64956,6 +65016,7 @@ class EvaluationPlugin extends CoreViewPlugin {
64956
65016
  "getEvaluatedCellsPositions",
64957
65017
  "getSpreadZone",
64958
65018
  "getArrayFormulaSpreadingOn",
65019
+ "isArrayFormulaSpillBlocked",
64959
65020
  "isEmpty",
64960
65021
  ];
64961
65022
  shouldRebuildDependenciesGraph = true;
@@ -65068,6 +65129,9 @@ class EvaluationPlugin extends CoreViewPlugin {
65068
65129
  getArrayFormulaSpreadingOn(position) {
65069
65130
  return this.evaluator.getArrayFormulaSpreadingOn(position);
65070
65131
  }
65132
+ isArrayFormulaSpillBlocked(position) {
65133
+ return this.evaluator.isArrayFormulaSpillBlocked(position);
65134
+ }
65071
65135
  /**
65072
65136
  * Check if a zone only contains empty cells
65073
65137
  */
@@ -66270,14 +66334,16 @@ const PERCENT_FORMAT = "0.00%";
66270
66334
  function withPivotPresentationLayer (PivotClass) {
66271
66335
  class PivotPresentationLayer extends PivotClass {
66272
66336
  getters;
66337
+ pivotId;
66273
66338
  cache = {};
66274
66339
  rankAsc = {};
66275
66340
  rankDesc = {};
66276
66341
  runningTotal = {};
66277
66342
  runningTotalInPercent = {};
66278
- constructor(custom, params) {
66343
+ constructor(pivotId, custom, params) {
66279
66344
  super(custom, params);
66280
66345
  this.getters = params.getters;
66346
+ this.pivotId = pivotId;
66281
66347
  }
66282
66348
  markAsDirtyForEvaluation() {
66283
66349
  this.cache = {};
@@ -66327,7 +66393,7 @@ function withPivotPresentationLayer (PivotClass) {
66327
66393
  return handleError(error, measure.aggregator.toUpperCase());
66328
66394
  }
66329
66395
  }
66330
- const formula = this.getters.getMeasureCompiledFormula(measure);
66396
+ const formula = this.getters.getMeasureCompiledFormula(this.pivotId, measure);
66331
66397
  const getSymbolValue = (symbolName) => {
66332
66398
  const { columns, rows } = this.definition;
66333
66399
  if (columns.find((col) => col.nameWithGranularity === symbolName)) {
@@ -67055,7 +67121,7 @@ class PivotUIPlugin extends CoreViewPlugin {
67055
67121
  const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
67056
67122
  if (!(pivotId in this.pivots)) {
67057
67123
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
67058
- this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
67124
+ this.pivots[pivotId] = new Pivot(pivotId, this.custom, { definition, getters: this.getters });
67059
67125
  }
67060
67126
  else if (recreate) {
67061
67127
  this.pivots[pivotId].onDefinitionChange(definition);
@@ -76521,7 +76587,7 @@ class Spreadsheet extends owl.Component {
76521
76587
  document.activeElement?.contains(this.spreadsheetRef.el)) {
76522
76588
  this.focusGrid();
76523
76589
  }
76524
- }, () => [this.env.model.getters.getActiveSheetId()]);
76590
+ });
76525
76591
  owl.useExternalListener(window, "resize", () => this.render(true));
76526
76592
  // For some reason, the wheel event is not properly registered inside templates
76527
76593
  // in Chromium-based browsers based on chromium 125
@@ -81098,6 +81164,6 @@ exports.tokenColors = tokenColors;
81098
81164
  exports.tokenize = tokenize;
81099
81165
 
81100
81166
 
81101
- __info__.version = "18.3.28";
81102
- __info__.date = "2025-12-02T05:34:06.602Z";
81103
- __info__.hash = "a205f91";
81167
+ __info__.version = "18.3.32";
81168
+ __info__.date = "2026-01-14T10:00:27.532Z";
81169
+ __info__.hash = "8d1d321";