@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
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -6043,17 +6043,41 @@ function toCriterionDateNumber(dateValue) {
6043
6043
  const today = DateTime.now();
6044
6044
  switch (dateValue) {
6045
6045
  case "today":
6046
- return jsDateToNumber(today);
6047
- case "yesterday":
6048
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
6049
- case "tomorrow":
6050
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
6046
+ return Math.floor(jsDateToNumber(today));
6047
+ case "yesterday": {
6048
+ today.setDate(today.getDate() - 1);
6049
+ return Math.floor(jsDateToNumber(today));
6050
+ }
6051
+ case "tomorrow": {
6052
+ today.setDate(today.getDate() + 1);
6053
+ return Math.floor(jsDateToNumber(today));
6054
+ }
6051
6055
  case "lastWeek":
6052
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
6053
- case "lastMonth":
6054
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
6056
+ today.setDate(today.getDate() - 6);
6057
+ return Math.floor(jsDateToNumber(today));
6058
+ case "lastMonth": {
6059
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
6060
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
6061
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
6062
+ today.setDate(1);
6063
+ }
6064
+ else {
6065
+ today.setDate(today.getDate() + 1);
6066
+ today.setMonth(today.getMonth() - 1);
6067
+ }
6068
+ return Math.floor(jsDateToNumber(today));
6069
+ }
6055
6070
  case "lastYear":
6056
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
6071
+ // Handle leap year case
6072
+ if (today.getMonth() === 1 && today.getDate() === 29) {
6073
+ today.setDate(28);
6074
+ today.setFullYear(today.getFullYear() - 1);
6075
+ }
6076
+ else {
6077
+ today.setDate(today.getDate() + 1);
6078
+ today.setFullYear(today.getFullYear() - 1);
6079
+ }
6080
+ return Math.floor(jsDateToNumber(today));
6057
6081
  }
6058
6082
  }
6059
6083
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -18389,9 +18413,10 @@ function assertDomainLength(domain) {
18389
18413
  throw new EvaluationError(_t("Function PIVOT takes an even number of arguments."));
18390
18414
  }
18391
18415
  }
18392
- function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
18416
+ function addPivotDependencies(evalContext, pivotId, forMeasures) {
18393
18417
  //TODO This function can be very costly when used with PIVOT.VALUE and PIVOT.HEADER
18394
18418
  const dependencies = [];
18419
+ const coreDefinition = evalContext.getters.getPivotCoreDefinition(pivotId);
18395
18420
  if (coreDefinition.type === "SPREADSHEET" && coreDefinition.dataSet) {
18396
18421
  const { sheetId, zone } = coreDefinition.dataSet;
18397
18422
  const xc = zoneToXc(zone);
@@ -18408,8 +18433,7 @@ function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
18408
18433
  }
18409
18434
  for (const measure of forMeasures) {
18410
18435
  if (measure.computedBy) {
18411
- const formula = evalContext.getters.getMeasureCompiledFormula(measure);
18412
- dependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
18436
+ dependencies.push(...evalContext.getters.getMeasureFullDependencies(pivotId, measure));
18413
18437
  }
18414
18438
  }
18415
18439
  const originPosition = evalContext.__originCellPosition;
@@ -18844,7 +18868,7 @@ const PIVOT_VALUE = {
18844
18868
  assertDomainLength(domainArgs);
18845
18869
  const pivot = this.getters.getPivot(pivotId);
18846
18870
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
18847
- addPivotDependencies(this, coreDefinition, coreDefinition.measures.filter((m) => m.id === _measure));
18871
+ addPivotDependencies(this, pivotId, coreDefinition.measures.filter((m) => m.id === _measure));
18848
18872
  pivot.init({ reload: pivot.needsReevaluation });
18849
18873
  const error = pivot.assertIsValid({ throwOnError: false });
18850
18874
  if (error) {
@@ -18877,8 +18901,7 @@ const PIVOT_HEADER = {
18877
18901
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
18878
18902
  assertDomainLength(domainArgs);
18879
18903
  const pivot = this.getters.getPivot(_pivotId);
18880
- const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
18881
- addPivotDependencies(this, coreDefinition, []);
18904
+ addPivotDependencies(this, _pivotId, []);
18882
18905
  pivot.init({ reload: pivot.needsReevaluation });
18883
18906
  const error = pivot.assertIsValid({ throwOnError: false });
18884
18907
  if (error) {
@@ -18932,7 +18955,7 @@ const PIVOT = {
18932
18955
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
18933
18956
  const pivot = this.getters.getPivot(pivotId);
18934
18957
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
18935
- addPivotDependencies(this, coreDefinition, coreDefinition.measures);
18958
+ addPivotDependencies(this, pivotId, coreDefinition.measures);
18936
18959
  pivot.init({ reload: pivot.needsReevaluation });
18937
18960
  const error = pivot.assertIsValid({ throwOnError: false });
18938
18961
  if (error) {
@@ -23204,7 +23227,7 @@ class AbstractComposerStore extends SpreadsheetStore {
23204
23227
  }
23205
23228
  captureSelection(zone, col, row) {
23206
23229
  this.model.selection.capture(this, {
23207
- cell: { col: col ?? zone.left, row: row ?? zone.right },
23230
+ cell: { col: col ?? zone.left, row: row ?? zone.top },
23208
23231
  zone,
23209
23232
  }, {
23210
23233
  handleEvent: this.handleEvent.bind(this),
@@ -29438,7 +29461,6 @@ class ChartFigure extends Component {
29438
29461
  static template = "o-spreadsheet-ChartFigure";
29439
29462
  static props = {
29440
29463
  figureUI: Object,
29441
- onFigureDeleted: Function,
29442
29464
  };
29443
29465
  static components = {};
29444
29466
  onDoubleClick() {
@@ -29462,7 +29484,6 @@ class ImageFigure extends Component {
29462
29484
  static template = "o-spreadsheet-ImageFigure";
29463
29485
  static props = {
29464
29486
  figureUI: Object,
29465
- onFigureDeleted: Function,
29466
29487
  };
29467
29488
  static components = {};
29468
29489
  // ---------------------------------------------------------------------------
@@ -30130,7 +30151,7 @@ figureRegistry.add("image", {
30130
30151
  borderWidth: 0,
30131
30152
  menuBuilder: getImageMenuRegistry,
30132
30153
  });
30133
- function getChartMenu(figureId, onFigureDeleted, env) {
30154
+ function getChartMenu(figureId, env) {
30134
30155
  const menuItemSpecs = [
30135
30156
  {
30136
30157
  id: "edit",
@@ -30182,11 +30203,11 @@ function getChartMenu(figureId, onFigureDeleted, env) {
30182
30203
  downloadFile(url, "chart");
30183
30204
  },
30184
30205
  },
30185
- getDeleteMenuItem(figureId, onFigureDeleted, env),
30206
+ getDeleteMenuItem(figureId, env),
30186
30207
  ];
30187
30208
  return createActions(menuItemSpecs);
30188
30209
  }
30189
- function getImageMenuRegistry(figureId, onFigureDeleted, env) {
30210
+ function getImageMenuRegistry(figureId, env) {
30190
30211
  const menuItemSpecs = [
30191
30212
  getCopyMenuItem(figureId, env),
30192
30213
  getCutMenuItem(figureId, env),
@@ -30231,7 +30252,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
30231
30252
  },
30232
30253
  icon: "o-spreadsheet-Icon.DOWNLOAD",
30233
30254
  },
30234
- getDeleteMenuItem(figureId, onFigureDeleted, env),
30255
+ getDeleteMenuItem(figureId, env),
30235
30256
  ];
30236
30257
  return createActions(menuItemSpecs);
30237
30258
  }
@@ -30264,7 +30285,7 @@ function getCutMenuItem(figureId, env) {
30264
30285
  icon: "o-spreadsheet-Icon.CUT",
30265
30286
  };
30266
30287
  }
30267
- function getDeleteMenuItem(figureId, onFigureDeleted, env) {
30288
+ function getDeleteMenuItem(figureId, env) {
30268
30289
  return {
30269
30290
  id: "delete",
30270
30291
  name: _t("Delete"),
@@ -30274,7 +30295,6 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
30274
30295
  sheetId: env.model.getters.getActiveSheetId(),
30275
30296
  figureId,
30276
30297
  });
30277
- onFigureDeleted();
30278
30298
  },
30279
30299
  icon: "o-spreadsheet-Icon.TRASH",
30280
30300
  };
@@ -45376,7 +45396,7 @@ function useHighlights(highlightProvider) {
45376
45396
  }
45377
45397
 
45378
45398
  css /* scss */ `
45379
- .o-cf-preview {
45399
+ .o-spreadsheet .o-cf-preview {
45380
45400
  &.o-cf-cursor-ptr {
45381
45401
  cursor: pointer;
45382
45402
  }
@@ -45384,6 +45404,7 @@ css /* scss */ `
45384
45404
  border-bottom: 1px solid ${GRAY_300};
45385
45405
  height: 80px;
45386
45406
  padding: 10px;
45407
+ box-sizing: border-box;
45387
45408
  position: relative;
45388
45409
  cursor: pointer;
45389
45410
  &:hover,
@@ -45397,7 +45418,6 @@ css /* scss */ `
45397
45418
  .o-cf-preview-icon {
45398
45419
  border: 1px solid ${GRAY_300};
45399
45420
  background-color: #fff;
45400
- position: absolute;
45401
45421
  height: 50px;
45402
45422
  width: 50px;
45403
45423
  .o-icon {
@@ -45406,12 +45426,6 @@ css /* scss */ `
45406
45426
  }
45407
45427
  }
45408
45428
  .o-cf-preview-description {
45409
- left: 65px;
45410
- margin-bottom: auto;
45411
- margin-right: 8px;
45412
- margin-top: auto;
45413
- position: relative;
45414
- width: 142px;
45415
45429
  .o-cf-preview-description-rule {
45416
45430
  margin-bottom: 4px;
45417
45431
  max-height: 2.8em;
@@ -45421,16 +45435,11 @@ css /* scss */ `
45421
45435
  font-size: 12px;
45422
45436
  }
45423
45437
  }
45424
- .o-cf-delete {
45425
- left: 90%;
45426
- top: 39%;
45427
- position: absolute;
45428
- }
45429
45438
  &:not(:hover):not(.o-cf-dragging) .o-cf-drag-handle {
45430
45439
  display: none !important;
45431
45440
  }
45432
45441
  .o-cf-drag-handle {
45433
- left: -8px;
45442
+ left: 2px;
45434
45443
  cursor: move;
45435
45444
  .o-icon {
45436
45445
  width: 6px;
@@ -46419,7 +46428,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
46419
46428
  return false;
46420
46429
  }
46421
46430
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
46422
- const today = jsDateToRoundNumber(DateTime.now());
46431
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
46423
46432
  return isDateBetween(dateValue, today, criterionValue);
46424
46433
  }
46425
46434
  return areDatesSameDay(dateValue, criterionValue);
@@ -47606,6 +47615,12 @@ class FindAndReplaceStore extends SpreadsheetStore {
47606
47615
  case "ACTIVATE_SHEET":
47607
47616
  this.isSearchDirty = true;
47608
47617
  this.shouldFinalizeUpdateSelection = true;
47618
+ if (this.searchOptions.specificRange) {
47619
+ this.searchOptions.specificRange = {
47620
+ ...this.searchOptions.specificRange,
47621
+ sheetId: this.getters.getActiveSheetId(),
47622
+ };
47623
+ }
47609
47624
  break;
47610
47625
  case "REPLACE_SEARCH":
47611
47626
  for (const match of cmd.matches) {
@@ -48027,9 +48042,20 @@ class FindAndReplacePanel extends Component {
48027
48042
  const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.state.dataRange);
48028
48043
  this.store.updateSearchOptions({ specificRange });
48029
48044
  }
48045
+ get specificRange() {
48046
+ const range = this.store.searchOptions.specificRange;
48047
+ return range ? this.env.model.getters.getRangeString(range, "forceSheetReference") : "";
48048
+ }
48030
48049
  get pendingSearch() {
48031
48050
  return this.updateSearchContent.isDebouncePending();
48032
48051
  }
48052
+ get selectionInputKey() {
48053
+ // Selections input are made to work with objects linked to a sheet id. They store the active sheet id at their creation,
48054
+ // and have specific behaviour linked to it (eg. go back to the initial sheet after confirmation).
48055
+ // We don't want all those behaviors here, so we force the recreation of the component when the active sheet changes.
48056
+ // The only drawback is that the input loses focus when changing sheet.
48057
+ return this.env.model.getters.getActiveSheetId();
48058
+ }
48033
48059
  }
48034
48060
 
48035
48061
  css /* scss */ `
@@ -50276,7 +50302,37 @@ pivotRegistry.add("SPREADSHEET", {
50276
50302
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
50277
50303
  isMeasureCandidate: (field) => field.type !== "boolean",
50278
50304
  isGroupable: () => true,
50305
+ adaptRanges: (getters, definition, applyChange) => {
50306
+ if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
50307
+ return definition;
50308
+ }
50309
+ const { sheetId, zone } = definition.dataSet;
50310
+ const range = getters.getRangeFromZone(sheetId, zone);
50311
+ const adaptedRange = adaptPivotRange(range, applyChange);
50312
+ if (adaptedRange === range) {
50313
+ return definition;
50314
+ }
50315
+ const dataSet = adaptedRange && {
50316
+ sheetId: adaptedRange.sheetId,
50317
+ zone: adaptedRange.zone,
50318
+ };
50319
+ return { ...definition, dataSet };
50320
+ },
50279
50321
  });
50322
+ function adaptPivotRange(range, applyChange) {
50323
+ if (!range) {
50324
+ return undefined;
50325
+ }
50326
+ const change = applyChange(range);
50327
+ switch (change.changeType) {
50328
+ case "NONE":
50329
+ return range;
50330
+ case "REMOVE":
50331
+ return undefined;
50332
+ default:
50333
+ return change.range;
50334
+ }
50335
+ }
50280
50336
 
50281
50337
  class PivotSidePanelStore extends SpreadsheetStore {
50282
50338
  pivotId;
@@ -51916,13 +51972,11 @@ class FigureComponent extends Component {
51916
51972
  static props = {
51917
51973
  figureUI: Object,
51918
51974
  style: { type: String, optional: true },
51919
- onFigureDeleted: { type: Function, optional: true },
51920
51975
  onMouseDown: { type: Function, optional: true },
51921
51976
  onClickAnchor: { type: Function, optional: true },
51922
51977
  };
51923
51978
  static components = { Menu };
51924
51979
  static defaultProps = {
51925
- onFigureDeleted: () => { },
51926
51980
  onMouseDown: () => { },
51927
51981
  onClickAnchor: () => { },
51928
51982
  };
@@ -52000,9 +52054,6 @@ class FigureComponent extends Component {
52000
52054
  this.props.figureUI.id,
52001
52055
  this.figureRef.el,
52002
52056
  ]);
52003
- onWillUnmount(() => {
52004
- this.props.onFigureDeleted();
52005
- });
52006
52057
  }
52007
52058
  clickAnchor(dirX, dirY, ev) {
52008
52059
  this.props.onClickAnchor(dirX, dirY, ev);
@@ -52019,7 +52070,6 @@ class FigureComponent extends Component {
52019
52070
  sheetId: this.env.model.getters.getActiveSheetId(),
52020
52071
  figureId: this.props.figureUI.id,
52021
52072
  });
52022
- this.props.onFigureDeleted();
52023
52073
  ev.preventDefault();
52024
52074
  ev.stopPropagation();
52025
52075
  break;
@@ -52112,7 +52162,7 @@ class FigureComponent extends Component {
52112
52162
  this.menuState.anchorRect = anchorRect;
52113
52163
  this.menuState.menuItems = figureRegistry
52114
52164
  .get(this.props.figureUI.tag)
52115
- .menuBuilder(this.props.figureUI.id, this.props.onFigureDeleted, this.env);
52165
+ .menuBuilder(this.props.figureUI.id, this.env);
52116
52166
  }
52117
52167
  }
52118
52168
 
@@ -52225,21 +52275,20 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
52225
52275
  this.highlightStore.register(this);
52226
52276
  }
52227
52277
  get highlights() {
52228
- let zone;
52229
52278
  const position = this.model.getters.getActivePosition();
52230
- const cell = this.getters.getEvaluatedCell(position);
52231
52279
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
52232
- zone = spreader
52280
+ const zone = spreader
52233
52281
  ? this.model.getters.getSpreadZone(spreader, { ignoreSpillError: true })
52234
52282
  : this.model.getters.getSpreadZone(position, { ignoreSpillError: true });
52235
52283
  if (!zone) {
52236
52284
  return [];
52237
52285
  }
52286
+ const isArrayFormulaBlocked = this.model.getters.isArrayFormulaSpillBlocked(spreader ?? position);
52238
52287
  return [
52239
52288
  {
52240
52289
  sheetId: position.sheetId,
52241
52290
  zone,
52242
- dashed: cell.value === CellErrorType.SpilledBlocked,
52291
+ dashed: isArrayFormulaBlocked,
52243
52292
  color: "#17A2B8",
52244
52293
  noFill: true,
52245
52294
  thinLine: true,
@@ -53407,9 +53456,7 @@ css /*SCSS*/ `
53407
53456
  */
53408
53457
  class FiguresContainer extends Component {
53409
53458
  static template = "o-spreadsheet-FiguresContainer";
53410
- static props = {
53411
- onFigureDeleted: Function,
53412
- };
53459
+ static props = {};
53413
53460
  static components = { FigureComponent };
53414
53461
  dnd = useState({
53415
53462
  draggedFigure: undefined,
@@ -53800,16 +53847,16 @@ css /* scss */ `
53800
53847
  `;
53801
53848
  class GridAddRowsFooter extends Component {
53802
53849
  static template = "o-spreadsheet-GridAddRowsFooter";
53803
- static props = {
53804
- focusGrid: Function,
53805
- };
53850
+ static props = {};
53806
53851
  static components = { ValidationMessages };
53852
+ DOMFocusableElementStore;
53807
53853
  inputRef = useRef("inputRef");
53808
53854
  state = useState({
53809
53855
  inputValue: "100",
53810
53856
  errorFlag: false,
53811
53857
  });
53812
53858
  setup() {
53859
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
53813
53860
  useExternalListener(window, "click", this.onExternalClick, { capture: true });
53814
53861
  }
53815
53862
  get addRowsPosition() {
@@ -53827,7 +53874,7 @@ class GridAddRowsFooter extends Component {
53827
53874
  }
53828
53875
  onKeydown(ev) {
53829
53876
  if (ev.key.toUpperCase() === "ESCAPE") {
53830
- this.props.focusGrid();
53877
+ this.focusDefaultElement();
53831
53878
  }
53832
53879
  else if (ev.key.toUpperCase() === "ENTER") {
53833
53880
  this.onConfirm();
@@ -53854,7 +53901,7 @@ class GridAddRowsFooter extends Component {
53854
53901
  quantity,
53855
53902
  dimension: "ROW",
53856
53903
  });
53857
- this.props.focusGrid();
53904
+ this.focusDefaultElement();
53858
53905
  // After adding new rows, scroll down to the new last row
53859
53906
  const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
53860
53907
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
@@ -53867,7 +53914,12 @@ class GridAddRowsFooter extends Component {
53867
53914
  if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) {
53868
53915
  return;
53869
53916
  }
53870
- this.props.focusGrid();
53917
+ this.focusDefaultElement();
53918
+ }
53919
+ focusDefaultElement() {
53920
+ if (document.activeElement === this.inputRef.el) {
53921
+ this.DOMFocusableElementStore.focus();
53922
+ }
53871
53923
  }
53872
53924
  }
53873
53925
 
@@ -54112,7 +54164,6 @@ class GridOverlay extends Component {
54112
54164
  onCellClicked: { type: Function, optional: true },
54113
54165
  onCellRightClicked: { type: Function, optional: true },
54114
54166
  onGridResized: { type: Function, optional: true },
54115
- onFigureDeleted: { type: Function, optional: true },
54116
54167
  onGridMoved: Function,
54117
54168
  gridOverlayDimensions: String,
54118
54169
  slots: { type: Object, optional: true },
@@ -54128,7 +54179,6 @@ class GridOverlay extends Component {
54128
54179
  onCellClicked: () => { },
54129
54180
  onCellRightClicked: () => { },
54130
54181
  onGridResized: () => { },
54131
- onFigureDeleted: () => { },
54132
54182
  };
54133
54183
  gridOverlay = useRef("gridOverlay");
54134
54184
  gridOverlayRect = useAbsoluteBoundingRect(this.gridOverlay);
@@ -60697,6 +60747,7 @@ function rangeToMerge(mergeId, range) {
60697
60747
  class RangeAdapter {
60698
60748
  getters;
60699
60749
  providers = [];
60750
+ isAdaptingRanges = false;
60700
60751
  constructor(getters) {
60701
60752
  this.getters = getters;
60702
60753
  }
@@ -60728,6 +60779,9 @@ class RangeAdapter {
60728
60779
  }
60729
60780
  beforeHandle(command) { }
60730
60781
  handle(cmd) {
60782
+ if (this.isAdaptingRanges) {
60783
+ throw new Error("Plugins cannot dispatch commands during adaptRanges phase");
60784
+ }
60731
60785
  const rangeAdapter = getRangeAdapter(cmd);
60732
60786
  if (rangeAdapter?.applyChange) {
60733
60787
  this.executeOnAllRanges(rangeAdapter.applyChange, rangeAdapter.sheetId, rangeAdapter.sheetName);
@@ -60750,10 +60804,12 @@ class RangeAdapter {
60750
60804
  };
60751
60805
  }
60752
60806
  executeOnAllRanges(adaptRange, sheetId, sheetName) {
60807
+ this.isAdaptingRanges = true;
60753
60808
  const func = this.verifyRangeRemoved(adaptRange);
60754
60809
  for (const provider of this.providers) {
60755
60810
  provider(func, sheetId, sheetName);
60756
60811
  }
60812
+ this.isAdaptingRanges = false;
60757
60813
  }
60758
60814
  /**
60759
60815
  * Stores the functions bound to each plugin to be able to iterate over all ranges of the application,
@@ -62701,6 +62757,7 @@ class PivotCorePlugin extends CorePlugin {
62701
62757
  "getMeasureCompiledFormula",
62702
62758
  "getPivotName",
62703
62759
  "isExistingPivot",
62760
+ "getMeasureFullDependencies",
62704
62761
  ];
62705
62762
  nextFormulaId = 1;
62706
62763
  pivots = {};
@@ -62783,15 +62840,32 @@ class PivotCorePlugin extends CorePlugin {
62783
62840
  }
62784
62841
  case "UPDATE_PIVOT": {
62785
62842
  this.history.update("pivots", cmd.pivotId, "definition", this.repairSortedColumn(deepCopy(cmd.pivot)));
62786
- this.compileCalculatedMeasures(cmd.pivot.measures);
62843
+ this.compileCalculatedMeasures(cmd.pivotId, cmd.pivot.measures);
62787
62844
  break;
62788
62845
  }
62789
62846
  }
62790
62847
  }
62791
62848
  adaptRanges(applyChange) {
62792
- for (const sheetId in this.compiledMeasureFormulas) {
62793
- for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
62794
- const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
62849
+ for (const pivotId in this.pivots) {
62850
+ const definition = deepCopy(this.pivots[pivotId]?.definition);
62851
+ if (!definition) {
62852
+ continue;
62853
+ }
62854
+ const newDefinition = pivotRegistry
62855
+ .get(definition.type)
62856
+ ?.adaptRanges?.(this.getters, definition, applyChange);
62857
+ if (newDefinition && !deepEquals(definition, newDefinition)) {
62858
+ this.history.update("pivots", pivotId, "definition", newDefinition);
62859
+ }
62860
+ }
62861
+ for (const pivotId in this.compiledMeasureFormulas) {
62862
+ for (const measureId in this.compiledMeasureFormulas[pivotId]) {
62863
+ const measure = this.pivots[pivotId]?.definition.measures.find((m) => m.id === measureId);
62864
+ if (!measure || !measure.computedBy) {
62865
+ continue;
62866
+ }
62867
+ const sheetId = measure.computedBy.sheetId;
62868
+ const compiledFormula = this.compiledMeasureFormulas[pivotId][measureId].formula;
62795
62869
  const newDependencies = [];
62796
62870
  for (const range of compiledFormula.dependencies) {
62797
62871
  const change = applyChange(range);
@@ -62803,8 +62877,9 @@ class PivotCorePlugin extends CorePlugin {
62803
62877
  }
62804
62878
  }
62805
62879
  const newFormulaString = this.getters.getFormulaString(sheetId, compiledFormula.tokens, newDependencies);
62806
- if (newFormulaString !== formulaString) {
62807
- this.replaceMeasureFormula(sheetId, formulaString, newFormulaString);
62880
+ const oldFormulaString = measure.computedBy.formula;
62881
+ if (newFormulaString !== oldFormulaString) {
62882
+ this.replaceMeasureFormula(pivotId, measure, newFormulaString);
62808
62883
  }
62809
62884
  }
62810
62885
  }
@@ -62842,12 +62917,17 @@ class PivotCorePlugin extends CorePlugin {
62842
62917
  isExistingPivot(pivotId) {
62843
62918
  return pivotId in this.pivots;
62844
62919
  }
62845
- getMeasureCompiledFormula(measure) {
62920
+ getMeasureCompiledFormula(pivotId, measure) {
62921
+ if (!measure.computedBy) {
62922
+ throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
62923
+ }
62924
+ return this.compiledMeasureFormulas[pivotId][measure.id].formula;
62925
+ }
62926
+ getMeasureFullDependencies(pivotId, measure) {
62846
62927
  if (!measure.computedBy) {
62847
62928
  throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
62848
62929
  }
62849
- const sheetId = measure.computedBy.sheetId;
62850
- return this.compiledMeasureFormulas[sheetId][measure.computedBy.formula];
62930
+ return this.compiledMeasureFormulas[pivotId][measure.id].dependencies;
62851
62931
  }
62852
62932
  // -------------------------------------------------------------------------
62853
62933
  // Private
@@ -62857,18 +62937,42 @@ class PivotCorePlugin extends CorePlugin {
62857
62937
  definition: this.repairSortedColumn(deepCopy(pivot)),
62858
62938
  formulaId,
62859
62939
  });
62860
- this.compileCalculatedMeasures(pivot.measures);
62940
+ this.compileCalculatedMeasures(pivotId, pivot.measures);
62861
62941
  this.history.update("formulaIds", formulaId, pivotId);
62862
62942
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
62863
62943
  }
62864
- compileCalculatedMeasures(measures) {
62944
+ compileCalculatedMeasures(pivotId, measures) {
62865
62945
  for (const measure of measures) {
62866
62946
  if (measure.computedBy) {
62867
- const sheetId = measure.computedBy.sheetId;
62868
62947
  const compiledFormula = this.compileMeasureFormula(measure.computedBy.sheetId, measure.computedBy.formula);
62869
- this.history.update("compiledMeasureFormulas", sheetId, measure.computedBy.formula, compiledFormula);
62948
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "formula", compiledFormula);
62949
+ }
62950
+ }
62951
+ for (const measure of measures) {
62952
+ if (measure.computedBy) {
62953
+ const dependencies = this.computeMeasureFullDependencies(pivotId, measure);
62954
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "dependencies", dependencies);
62955
+ }
62956
+ }
62957
+ }
62958
+ computeMeasureFullDependencies(pivotId, measure, exploredMeasures = new Set()) {
62959
+ const rangeDependencies = [];
62960
+ const definition = this.getPivotCoreDefinition(pivotId);
62961
+ const formula = this.getMeasureCompiledFormula(pivotId, measure);
62962
+ exploredMeasures.add(measure.id);
62963
+ for (const token of formula.tokens) {
62964
+ if (token.type !== "SYMBOL") {
62965
+ continue;
62966
+ }
62967
+ const otherMeasure = definition.measures.find((measureCandidate) => getCanonicalSymbolName(measureCandidate.id) === token.value &&
62968
+ measure.id !== measureCandidate.id);
62969
+ if (!otherMeasure || exploredMeasures.has(otherMeasure.id) || !otherMeasure.computedBy) {
62970
+ continue;
62870
62971
  }
62972
+ rangeDependencies.push(...this.computeMeasureFullDependencies(pivotId, otherMeasure, exploredMeasures));
62871
62973
  }
62974
+ rangeDependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
62975
+ return rangeDependencies;
62872
62976
  }
62873
62977
  insertPivot(position, formulaId, table) {
62874
62978
  this.resizeSheet(position.sheetId, position, table);
@@ -62928,28 +63032,17 @@ class PivotCorePlugin extends CorePlugin {
62928
63032
  dependencies: rangeDependencies,
62929
63033
  };
62930
63034
  }
62931
- replaceMeasureFormula(sheetId, formulaString, newFormulaString) {
62932
- this.history.update("compiledMeasureFormulas", sheetId, formulaString, undefined);
62933
- this.history.update("compiledMeasureFormulas", sheetId, newFormulaString, this.compileMeasureFormula(sheetId, newFormulaString));
62934
- for (const pivotId in this.pivots) {
62935
- const pivot = this.pivots[pivotId];
62936
- if (!pivot) {
62937
- continue;
62938
- }
62939
- const def = deepCopy(pivot.definition);
62940
- for (const measure of def.measures) {
62941
- if (measure.computedBy?.formula === formulaString) {
62942
- const measureIndex = def.measures.indexOf(measure);
62943
- if (measureIndex !== -1) {
62944
- def.measures[measureIndex].computedBy = {
62945
- formula: newFormulaString,
62946
- sheetId,
62947
- };
62948
- }
62949
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
62950
- }
62951
- }
63035
+ replaceMeasureFormula(pivotId, measure, newFormulaString) {
63036
+ const pivot = this.pivots[pivotId];
63037
+ if (!pivot) {
63038
+ return;
62952
63039
  }
63040
+ const measureIndex = pivot.definition.measures.indexOf(measure);
63041
+ this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", {
63042
+ formula: newFormulaString,
63043
+ sheetId: measure.computedBy.sheetId,
63044
+ });
63045
+ this.compileCalculatedMeasures(pivotId, pivot.definition.measures);
62953
63046
  }
62954
63047
  checkSortedColumnInMeasures(definition) {
62955
63048
  definition = this.repairSortedColumn(definition);
@@ -63073,20 +63166,6 @@ class SettingsPlugin extends CorePlugin {
63073
63166
  }
63074
63167
  }
63075
63168
 
63076
- function adaptPivotRange(range, applyChange) {
63077
- if (!range) {
63078
- return undefined;
63079
- }
63080
- const change = applyChange(range);
63081
- switch (change.changeType) {
63082
- case "NONE":
63083
- return range;
63084
- case "REMOVE":
63085
- return undefined;
63086
- default:
63087
- return change.range;
63088
- }
63089
- }
63090
63169
  class SpreadsheetPivotCorePlugin extends CorePlugin {
63091
63170
  allowDispatch(cmd) {
63092
63171
  switch (cmd.type) {
@@ -63097,27 +63176,6 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
63097
63176
  }
63098
63177
  return "Success" /* CommandResult.Success */;
63099
63178
  }
63100
- adaptRanges(applyChange) {
63101
- for (const pivotId of this.getters.getPivotIds()) {
63102
- const definition = this.getters.getPivotCoreDefinition(pivotId);
63103
- if (definition.type !== "SPREADSHEET") {
63104
- continue;
63105
- }
63106
- if (definition.dataSet) {
63107
- const { sheetId, zone } = definition.dataSet;
63108
- const range = this.getters.getRangeFromZone(sheetId, zone);
63109
- const adaptedRange = adaptPivotRange(range, applyChange);
63110
- if (adaptedRange === range) {
63111
- return;
63112
- }
63113
- const dataSet = adaptedRange && {
63114
- sheetId: adaptedRange.sheetId,
63115
- zone: adaptedRange.zone,
63116
- };
63117
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
63118
- }
63119
- }
63120
- }
63121
63179
  checkDataSetValidity(definition) {
63122
63180
  if (definition.type === "SPREADSHEET" && definition.dataSet) {
63123
63181
  const { zone, sheetId } = definition.dataSet;
@@ -64459,6 +64517,9 @@ class Evaluator {
64459
64517
  const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
64460
64518
  return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
64461
64519
  }
64520
+ isArrayFormulaSpillBlocked(position) {
64521
+ return this.blockedArrayFormulas.has(position);
64522
+ }
64462
64523
  updateDependencies(position) {
64463
64524
  // removing dependencies is slow because it requires
64464
64525
  // to traverse the entire r-tree.
@@ -64470,13 +64531,8 @@ class Evaluator {
64470
64531
  addDependencies(position, dependencies) {
64471
64532
  this.formulaDependencies().addDependencies(position, dependencies);
64472
64533
  for (const range of dependencies) {
64473
- const sheetId = range.sheetId;
64474
- const { left, bottom, right, top } = range.zone;
64475
- for (let col = left; col <= right; col++) {
64476
- for (let row = top; row <= bottom; row++) {
64477
- this.computeAndSave({ sheetId, col, row });
64478
- }
64479
- }
64534
+ // ensure that all ranges are computed
64535
+ this.compilationParams.ensureRange(range);
64480
64536
  }
64481
64537
  }
64482
64538
  updateCompilationParameters() {
@@ -64679,6 +64735,10 @@ class Evaluator {
64679
64735
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
64680
64736
  const nbColumns = formulaReturn.length;
64681
64737
  const nbRows = formulaReturn[0].length;
64738
+ if (nbRows === 0) {
64739
+ // empty matrix
64740
+ return createEvaluatedCell({ value: 0 }, this.getters.getLocale(), cellData);
64741
+ }
64682
64742
  const resultZone = {
64683
64743
  top: formulaPosition.row,
64684
64744
  bottom: formulaPosition.row + nbRows - 1,
@@ -64954,6 +65014,7 @@ class EvaluationPlugin extends CoreViewPlugin {
64954
65014
  "getEvaluatedCellsPositions",
64955
65015
  "getSpreadZone",
64956
65016
  "getArrayFormulaSpreadingOn",
65017
+ "isArrayFormulaSpillBlocked",
64957
65018
  "isEmpty",
64958
65019
  ];
64959
65020
  shouldRebuildDependenciesGraph = true;
@@ -65066,6 +65127,9 @@ class EvaluationPlugin extends CoreViewPlugin {
65066
65127
  getArrayFormulaSpreadingOn(position) {
65067
65128
  return this.evaluator.getArrayFormulaSpreadingOn(position);
65068
65129
  }
65130
+ isArrayFormulaSpillBlocked(position) {
65131
+ return this.evaluator.isArrayFormulaSpillBlocked(position);
65132
+ }
65069
65133
  /**
65070
65134
  * Check if a zone only contains empty cells
65071
65135
  */
@@ -66268,14 +66332,16 @@ const PERCENT_FORMAT = "0.00%";
66268
66332
  function withPivotPresentationLayer (PivotClass) {
66269
66333
  class PivotPresentationLayer extends PivotClass {
66270
66334
  getters;
66335
+ pivotId;
66271
66336
  cache = {};
66272
66337
  rankAsc = {};
66273
66338
  rankDesc = {};
66274
66339
  runningTotal = {};
66275
66340
  runningTotalInPercent = {};
66276
- constructor(custom, params) {
66341
+ constructor(pivotId, custom, params) {
66277
66342
  super(custom, params);
66278
66343
  this.getters = params.getters;
66344
+ this.pivotId = pivotId;
66279
66345
  }
66280
66346
  markAsDirtyForEvaluation() {
66281
66347
  this.cache = {};
@@ -66325,7 +66391,7 @@ function withPivotPresentationLayer (PivotClass) {
66325
66391
  return handleError(error, measure.aggregator.toUpperCase());
66326
66392
  }
66327
66393
  }
66328
- const formula = this.getters.getMeasureCompiledFormula(measure);
66394
+ const formula = this.getters.getMeasureCompiledFormula(this.pivotId, measure);
66329
66395
  const getSymbolValue = (symbolName) => {
66330
66396
  const { columns, rows } = this.definition;
66331
66397
  if (columns.find((col) => col.nameWithGranularity === symbolName)) {
@@ -67053,7 +67119,7 @@ class PivotUIPlugin extends CoreViewPlugin {
67053
67119
  const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
67054
67120
  if (!(pivotId in this.pivots)) {
67055
67121
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
67056
- this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
67122
+ this.pivots[pivotId] = new Pivot(pivotId, this.custom, { definition, getters: this.getters });
67057
67123
  }
67058
67124
  else if (recreate) {
67059
67125
  this.pivots[pivotId].onDefinitionChange(definition);
@@ -76519,7 +76585,7 @@ class Spreadsheet extends Component {
76519
76585
  document.activeElement?.contains(this.spreadsheetRef.el)) {
76520
76586
  this.focusGrid();
76521
76587
  }
76522
- }, () => [this.env.model.getters.getActiveSheetId()]);
76588
+ });
76523
76589
  useExternalListener(window, "resize", () => this.render(true));
76524
76590
  // For some reason, the wheel event is not properly registered inside templates
76525
76591
  // in Chromium-based browsers based on chromium 125
@@ -81050,6 +81116,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
81050
81116
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
81051
81117
 
81052
81118
 
81053
- __info__.version = "18.3.28";
81054
- __info__.date = "2025-12-02T05:34:06.602Z";
81055
- __info__.hash = "a205f91";
81119
+ __info__.version = "18.3.32";
81120
+ __info__.date = "2026-01-14T10:00:27.532Z";
81121
+ __info__.hash = "8d1d321";