@odoo/o-spreadsheet 18.2.36 → 18.2.40

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.2.36
6
- * @date 2025-12-02T05:31:10.558Z
7
- * @hash d385099
5
+ * @version 18.2.40
6
+ * @date 2026-01-14T09:59:34.210Z
7
+ * @hash 755a787
8
8
  */
9
9
 
10
10
  'use strict';
@@ -6018,17 +6018,41 @@ function toCriterionDateNumber(dateValue) {
6018
6018
  const today = DateTime.now();
6019
6019
  switch (dateValue) {
6020
6020
  case "today":
6021
- return jsDateToNumber(today);
6022
- case "yesterday":
6023
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
6024
- case "tomorrow":
6025
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
6021
+ return Math.floor(jsDateToNumber(today));
6022
+ case "yesterday": {
6023
+ today.setDate(today.getDate() - 1);
6024
+ return Math.floor(jsDateToNumber(today));
6025
+ }
6026
+ case "tomorrow": {
6027
+ today.setDate(today.getDate() + 1);
6028
+ return Math.floor(jsDateToNumber(today));
6029
+ }
6026
6030
  case "lastWeek":
6027
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
6028
- case "lastMonth":
6029
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
6031
+ today.setDate(today.getDate() - 6);
6032
+ return Math.floor(jsDateToNumber(today));
6033
+ case "lastMonth": {
6034
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
6035
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
6036
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
6037
+ today.setDate(1);
6038
+ }
6039
+ else {
6040
+ today.setDate(today.getDate() + 1);
6041
+ today.setMonth(today.getMonth() - 1);
6042
+ }
6043
+ return Math.floor(jsDateToNumber(today));
6044
+ }
6030
6045
  case "lastYear":
6031
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
6046
+ // Handle leap year case
6047
+ if (today.getMonth() === 1 && today.getDate() === 29) {
6048
+ today.setDate(28);
6049
+ today.setFullYear(today.getFullYear() - 1);
6050
+ }
6051
+ else {
6052
+ today.setDate(today.getDate() + 1);
6053
+ today.setFullYear(today.getFullYear() - 1);
6054
+ }
6055
+ return Math.floor(jsDateToNumber(today));
6032
6056
  }
6033
6057
  }
6034
6058
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -19095,9 +19119,10 @@ function assertDomainLength(domain) {
19095
19119
  throw new EvaluationError(_t("Function PIVOT takes an even number of arguments."));
19096
19120
  }
19097
19121
  }
19098
- function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
19122
+ function addPivotDependencies(evalContext, pivotId, forMeasures) {
19099
19123
  //TODO This function can be very costly when used with PIVOT.VALUE and PIVOT.HEADER
19100
19124
  const dependencies = [];
19125
+ const coreDefinition = evalContext.getters.getPivotCoreDefinition(pivotId);
19101
19126
  if (coreDefinition.type === "SPREADSHEET" && coreDefinition.dataSet) {
19102
19127
  const { sheetId, zone } = coreDefinition.dataSet;
19103
19128
  const xc = zoneToXc(zone);
@@ -19114,8 +19139,7 @@ function addPivotDependencies(evalContext, coreDefinition, forMeasures) {
19114
19139
  }
19115
19140
  for (const measure of forMeasures) {
19116
19141
  if (measure.computedBy) {
19117
- const formula = evalContext.getters.getMeasureCompiledFormula(measure);
19118
- dependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
19142
+ dependencies.push(...evalContext.getters.getMeasureFullDependencies(pivotId, measure));
19119
19143
  }
19120
19144
  }
19121
19145
  const originPosition = evalContext.__originCellPosition;
@@ -19556,7 +19580,7 @@ const PIVOT_VALUE = {
19556
19580
  assertDomainLength(domainArgs);
19557
19581
  const pivot = this.getters.getPivot(pivotId);
19558
19582
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
19559
- addPivotDependencies(this, coreDefinition, coreDefinition.measures.filter((m) => m.id === _measure));
19583
+ addPivotDependencies(this, pivotId, coreDefinition.measures.filter((m) => m.id === _measure));
19560
19584
  pivot.init({ reload: pivot.needsReevaluation });
19561
19585
  const error = pivot.assertIsValid({ throwOnError: false });
19562
19586
  if (error) {
@@ -19589,8 +19613,7 @@ const PIVOT_HEADER = {
19589
19613
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
19590
19614
  assertDomainLength(domainArgs);
19591
19615
  const pivot = this.getters.getPivot(_pivotId);
19592
- const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
19593
- addPivotDependencies(this, coreDefinition, []);
19616
+ addPivotDependencies(this, _pivotId, []);
19594
19617
  pivot.init({ reload: pivot.needsReevaluation });
19595
19618
  const error = pivot.assertIsValid({ throwOnError: false });
19596
19619
  if (error) {
@@ -19644,7 +19667,7 @@ const PIVOT = {
19644
19667
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
19645
19668
  const pivot = this.getters.getPivot(pivotId);
19646
19669
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
19647
- addPivotDependencies(this, coreDefinition, coreDefinition.measures);
19670
+ addPivotDependencies(this, pivotId, coreDefinition.measures);
19648
19671
  pivot.init({ reload: pivot.needsReevaluation });
19649
19672
  const error = pivot.assertIsValid({ throwOnError: false });
19650
19673
  if (error) {
@@ -21301,7 +21324,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21301
21324
  }
21302
21325
  captureSelection(zone, col, row) {
21303
21326
  this.model.selection.capture(this, {
21304
- cell: { col: col ?? zone.left, row: row ?? zone.right },
21327
+ cell: { col: col ?? zone.left, row: row ?? zone.top },
21305
21328
  zone,
21306
21329
  }, {
21307
21330
  handleEvent: this.handleEvent.bind(this),
@@ -32662,7 +32685,6 @@ class ChartFigure extends owl.Component {
32662
32685
  static template = "o-spreadsheet-ChartFigure";
32663
32686
  static props = {
32664
32687
  figure: Object,
32665
- onFigureDeleted: Function,
32666
32688
  };
32667
32689
  static components = {};
32668
32690
  onDoubleClick() {
@@ -32686,7 +32708,6 @@ class ImageFigure extends owl.Component {
32686
32708
  static template = "o-spreadsheet-ImageFigure";
32687
32709
  static props = {
32688
32710
  figure: Object,
32689
- onFigureDeleted: Function,
32690
32711
  };
32691
32712
  static components = {};
32692
32713
  // ---------------------------------------------------------------------------
@@ -32743,7 +32764,7 @@ figureRegistry.add("image", {
32743
32764
  borderWidth: 0,
32744
32765
  menuBuilder: getImageMenuRegistry,
32745
32766
  });
32746
- function getChartMenu(figureId, onFigureDeleted, env) {
32767
+ function getChartMenu(figureId, env) {
32747
32768
  const menuItemSpecs = [
32748
32769
  {
32749
32770
  id: "edit",
@@ -32757,11 +32778,11 @@ function getChartMenu(figureId, onFigureDeleted, env) {
32757
32778
  },
32758
32779
  getCopyMenuItem(figureId, env),
32759
32780
  getCutMenuItem(figureId, env),
32760
- getDeleteMenuItem(figureId, onFigureDeleted, env),
32781
+ getDeleteMenuItem(figureId, env),
32761
32782
  ];
32762
32783
  return createActions(menuItemSpecs);
32763
32784
  }
32764
- function getImageMenuRegistry(figureId, onFigureDeleted, env) {
32785
+ function getImageMenuRegistry(figureId, env) {
32765
32786
  const menuItemSpecs = [
32766
32787
  getCopyMenuItem(figureId, env),
32767
32788
  getCutMenuItem(figureId, env),
@@ -32787,7 +32808,7 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
32787
32808
  },
32788
32809
  icon: "o-spreadsheet-Icon.REFRESH",
32789
32810
  },
32790
- getDeleteMenuItem(figureId, onFigureDeleted, env),
32811
+ getDeleteMenuItem(figureId, env),
32791
32812
  ];
32792
32813
  return createActions(menuItemSpecs);
32793
32814
  }
@@ -32819,7 +32840,7 @@ function getCutMenuItem(figureId, env) {
32819
32840
  icon: "o-spreadsheet-Icon.CUT",
32820
32841
  };
32821
32842
  }
32822
- function getDeleteMenuItem(figureId, onFigureDeleted, env) {
32843
+ function getDeleteMenuItem(figureId, env) {
32823
32844
  return {
32824
32845
  id: "delete",
32825
32846
  name: _t("Delete"),
@@ -32829,7 +32850,6 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
32829
32850
  sheetId: env.model.getters.getActiveSheetId(),
32830
32851
  id: figureId,
32831
32852
  });
32832
- onFigureDeleted();
32833
32853
  },
32834
32854
  icon: "o-spreadsheet-Icon.TRASH",
32835
32855
  };
@@ -42623,7 +42643,7 @@ function useHighlights(highlightProvider) {
42623
42643
  }
42624
42644
 
42625
42645
  css /* scss */ `
42626
- .o-cf-preview {
42646
+ .o-spreadsheet .o-cf-preview {
42627
42647
  &.o-cf-cursor-ptr {
42628
42648
  cursor: pointer;
42629
42649
  }
@@ -42631,6 +42651,7 @@ css /* scss */ `
42631
42651
  border-bottom: 1px solid ${GRAY_300};
42632
42652
  height: 80px;
42633
42653
  padding: 10px;
42654
+ box-sizing: border-box;
42634
42655
  position: relative;
42635
42656
  cursor: pointer;
42636
42657
  &:hover,
@@ -42644,7 +42665,6 @@ css /* scss */ `
42644
42665
  .o-cf-preview-icon {
42645
42666
  border: 1px solid ${GRAY_300};
42646
42667
  background-color: #fff;
42647
- position: absolute;
42648
42668
  height: 50px;
42649
42669
  width: 50px;
42650
42670
  .o-icon {
@@ -42653,12 +42673,6 @@ css /* scss */ `
42653
42673
  }
42654
42674
  }
42655
42675
  .o-cf-preview-description {
42656
- left: 65px;
42657
- margin-bottom: auto;
42658
- margin-right: 8px;
42659
- margin-top: auto;
42660
- position: relative;
42661
- width: 142px;
42662
42676
  .o-cf-preview-description-rule {
42663
42677
  margin-bottom: 4px;
42664
42678
  max-height: 2.8em;
@@ -42668,16 +42682,11 @@ css /* scss */ `
42668
42682
  font-size: 12px;
42669
42683
  }
42670
42684
  }
42671
- .o-cf-delete {
42672
- left: 90%;
42673
- top: 39%;
42674
- position: absolute;
42675
- }
42676
42685
  &:not(:hover):not(.o-cf-dragging) .o-cf-drag-handle {
42677
42686
  display: none !important;
42678
42687
  }
42679
42688
  .o-cf-drag-handle {
42680
- left: -8px;
42689
+ left: 2px;
42681
42690
  cursor: move;
42682
42691
  .o-icon {
42683
42692
  width: 6px;
@@ -43666,7 +43675,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
43666
43675
  return false;
43667
43676
  }
43668
43677
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
43669
- const today = jsDateToRoundNumber(DateTime.now());
43678
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
43670
43679
  return isDateBetween(dateValue, today, criterionValue);
43671
43680
  }
43672
43681
  return areDatesSameDay(dateValue, criterionValue);
@@ -44856,6 +44865,11 @@ class FindAndReplaceStore extends SpreadsheetStore {
44856
44865
  case "ACTIVATE_SHEET":
44857
44866
  this.isSearchDirty = true;
44858
44867
  this.shouldFinalizeUpdateSelection = true;
44868
+ if (this.searchOptions.specificRange) {
44869
+ this.searchOptions.specificRange = this.searchOptions.specificRange.clone({
44870
+ sheetId: this.getters.getActiveSheetId(),
44871
+ });
44872
+ }
44859
44873
  break;
44860
44874
  case "REPLACE_SEARCH":
44861
44875
  for (const match of cmd.matches) {
@@ -45277,9 +45291,20 @@ class FindAndReplacePanel extends owl.Component {
45277
45291
  const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.state.dataRange);
45278
45292
  this.store.updateSearchOptions({ specificRange });
45279
45293
  }
45294
+ get specificRange() {
45295
+ const range = this.store.searchOptions.specificRange;
45296
+ return range ? this.env.model.getters.getRangeString(range, "forceSheetReference") : "";
45297
+ }
45280
45298
  get pendingSearch() {
45281
45299
  return this.updateSearchContent.isDebouncePending();
45282
45300
  }
45301
+ get selectionInputKey() {
45302
+ // Selections input are made to work with objects linked to a sheet id. They store the active sheet id at their creation,
45303
+ // and have specific behaviour linked to it (eg. go back to the initial sheet after confirmation).
45304
+ // We don't want all those behaviors here, so we force the recreation of the component when the active sheet changes.
45305
+ // The only drawback is that the input loses focus when changing sheet.
45306
+ return this.env.model.getters.getActiveSheetId();
45307
+ }
45283
45308
  }
45284
45309
 
45285
45310
  css /* scss */ `
@@ -47520,7 +47545,37 @@ pivotRegistry.add("SPREADSHEET", {
47520
47545
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
47521
47546
  isMeasureCandidate: (field) => field.type !== "boolean",
47522
47547
  isGroupable: () => true,
47548
+ adaptRanges: (getters, definition, applyChange) => {
47549
+ if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
47550
+ return definition;
47551
+ }
47552
+ const { sheetId, zone } = definition.dataSet;
47553
+ const range = getters.getRangeFromZone(sheetId, zone);
47554
+ const adaptedRange = adaptPivotRange(range, applyChange);
47555
+ if (adaptedRange === range) {
47556
+ return definition;
47557
+ }
47558
+ const dataSet = adaptedRange && {
47559
+ sheetId: adaptedRange.sheetId,
47560
+ zone: adaptedRange.zone,
47561
+ };
47562
+ return { ...definition, dataSet };
47563
+ },
47523
47564
  });
47565
+ function adaptPivotRange(range, applyChange) {
47566
+ if (!range) {
47567
+ return undefined;
47568
+ }
47569
+ const change = applyChange(range);
47570
+ switch (change.changeType) {
47571
+ case "NONE":
47572
+ return range;
47573
+ case "REMOVE":
47574
+ return undefined;
47575
+ default:
47576
+ return change.range;
47577
+ }
47578
+ }
47524
47579
 
47525
47580
  class PivotSidePanelStore extends SpreadsheetStore {
47526
47581
  pivotId;
@@ -49161,13 +49216,11 @@ class FigureComponent extends owl.Component {
49161
49216
  static props = {
49162
49217
  figure: Object,
49163
49218
  style: { type: String, optional: true },
49164
- onFigureDeleted: { type: Function, optional: true },
49165
49219
  onMouseDown: { type: Function, optional: true },
49166
49220
  onClickAnchor: { type: Function, optional: true },
49167
49221
  };
49168
49222
  static components = { Menu };
49169
49223
  static defaultProps = {
49170
- onFigureDeleted: () => { },
49171
49224
  onMouseDown: () => { },
49172
49225
  onClickAnchor: () => { },
49173
49226
  };
@@ -49241,9 +49294,6 @@ class FigureComponent extends owl.Component {
49241
49294
  el?.focus({ preventScroll: true });
49242
49295
  }
49243
49296
  }, () => [this.env.model.getters.getSelectedFigureId(), this.props.figure.id, this.figureRef.el]);
49244
- owl.onWillUnmount(() => {
49245
- this.props.onFigureDeleted();
49246
- });
49247
49297
  }
49248
49298
  clickAnchor(dirX, dirY, ev) {
49249
49299
  this.props.onClickAnchor(dirX, dirY, ev);
@@ -49261,7 +49311,6 @@ class FigureComponent extends owl.Component {
49261
49311
  sheetId: this.env.model.getters.getActiveSheetId(),
49262
49312
  id: figure.id,
49263
49313
  });
49264
- this.props.onFigureDeleted();
49265
49314
  ev.preventDefault();
49266
49315
  ev.stopPropagation();
49267
49316
  break;
@@ -49325,7 +49374,7 @@ class FigureComponent extends owl.Component {
49325
49374
  this.menuState.position = position;
49326
49375
  this.menuState.menuItems = figureRegistry
49327
49376
  .get(this.props.figure.tag)
49328
- .menuBuilder(this.props.figure.id, this.props.onFigureDeleted, this.env);
49377
+ .menuBuilder(this.props.figure.id, this.env);
49329
49378
  }
49330
49379
  }
49331
49380
 
@@ -49438,21 +49487,20 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
49438
49487
  this.highlightStore.register(this);
49439
49488
  }
49440
49489
  get highlights() {
49441
- let zone;
49442
49490
  const position = this.model.getters.getActivePosition();
49443
- const cell = this.getters.getEvaluatedCell(position);
49444
49491
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49445
- zone = spreader
49492
+ const zone = spreader
49446
49493
  ? this.model.getters.getSpreadZone(spreader, { ignoreSpillError: true })
49447
49494
  : this.model.getters.getSpreadZone(position, { ignoreSpillError: true });
49448
49495
  if (!zone) {
49449
49496
  return [];
49450
49497
  }
49498
+ const isArrayFormulaBlocked = this.model.getters.isArrayFormulaSpillBlocked(spreader ?? position);
49451
49499
  return [
49452
49500
  {
49453
49501
  sheetId: position.sheetId,
49454
49502
  zone,
49455
- dashed: cell.value === CellErrorType.SpilledBlocked,
49503
+ dashed: isArrayFormulaBlocked,
49456
49504
  color: "#17A2B8",
49457
49505
  noFill: true,
49458
49506
  thinLine: true,
@@ -50520,9 +50568,7 @@ css /*SCSS*/ `
50520
50568
  */
50521
50569
  class FiguresContainer extends owl.Component {
50522
50570
  static template = "o-spreadsheet-FiguresContainer";
50523
- static props = {
50524
- onFigureDeleted: Function,
50525
- };
50571
+ static props = {};
50526
50572
  static components = { FigureComponent };
50527
50573
  dnd = owl.useState({
50528
50574
  draggedFigure: undefined,
@@ -50878,16 +50924,16 @@ css /* scss */ `
50878
50924
  `;
50879
50925
  class GridAddRowsFooter extends owl.Component {
50880
50926
  static template = "o-spreadsheet-GridAddRowsFooter";
50881
- static props = {
50882
- focusGrid: Function,
50883
- };
50927
+ static props = {};
50884
50928
  static components = { ValidationMessages };
50929
+ DOMFocusableElementStore;
50885
50930
  inputRef = owl.useRef("inputRef");
50886
50931
  state = owl.useState({
50887
50932
  inputValue: "100",
50888
50933
  errorFlag: false,
50889
50934
  });
50890
50935
  setup() {
50936
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
50891
50937
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
50892
50938
  }
50893
50939
  get addRowsPosition() {
@@ -50905,7 +50951,7 @@ class GridAddRowsFooter extends owl.Component {
50905
50951
  }
50906
50952
  onKeydown(ev) {
50907
50953
  if (ev.key.toUpperCase() === "ESCAPE") {
50908
- this.props.focusGrid();
50954
+ this.focusDefaultElement();
50909
50955
  }
50910
50956
  else if (ev.key.toUpperCase() === "ENTER") {
50911
50957
  this.onConfirm();
@@ -50931,7 +50977,7 @@ class GridAddRowsFooter extends owl.Component {
50931
50977
  quantity,
50932
50978
  dimension: "ROW",
50933
50979
  });
50934
- this.props.focusGrid();
50980
+ this.focusDefaultElement();
50935
50981
  // After adding new rows, scroll down to the new last row
50936
50982
  const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
50937
50983
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
@@ -50944,7 +50990,12 @@ class GridAddRowsFooter extends owl.Component {
50944
50990
  if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) {
50945
50991
  return;
50946
50992
  }
50947
- this.props.focusGrid();
50993
+ this.focusDefaultElement();
50994
+ }
50995
+ focusDefaultElement() {
50996
+ if (document.activeElement === this.inputRef.el) {
50997
+ this.DOMFocusableElementStore.focus();
50998
+ }
50948
50999
  }
50949
51000
  }
50950
51001
 
@@ -51133,7 +51184,6 @@ class GridOverlay extends owl.Component {
51133
51184
  onCellClicked: { type: Function, optional: true },
51134
51185
  onCellRightClicked: { type: Function, optional: true },
51135
51186
  onGridResized: { type: Function, optional: true },
51136
- onFigureDeleted: { type: Function, optional: true },
51137
51187
  onGridMoved: Function,
51138
51188
  gridOverlayDimensions: String,
51139
51189
  };
@@ -51149,7 +51199,6 @@ class GridOverlay extends owl.Component {
51149
51199
  onCellClicked: () => { },
51150
51200
  onCellRightClicked: () => { },
51151
51201
  onGridResized: () => { },
51152
- onFigureDeleted: () => { },
51153
51202
  };
51154
51203
  gridOverlay = owl.useRef("gridOverlay");
51155
51204
  gridOverlayRect = useAbsoluteBoundingRect(this.gridOverlay);
@@ -57556,6 +57605,7 @@ function rangeToMerge(mergeId, range) {
57556
57605
  class RangeAdapter {
57557
57606
  getters;
57558
57607
  providers = [];
57608
+ isAdaptingRanges = false;
57559
57609
  constructor(getters) {
57560
57610
  this.getters = getters;
57561
57611
  }
@@ -57586,6 +57636,9 @@ class RangeAdapter {
57586
57636
  }
57587
57637
  beforeHandle(command) { }
57588
57638
  handle(cmd) {
57639
+ if (this.isAdaptingRanges) {
57640
+ throw new Error("Plugins cannot dispatch commands during adaptRanges phase");
57641
+ }
57589
57642
  switch (cmd.type) {
57590
57643
  case "REMOVE_COLUMNS_ROWS": {
57591
57644
  let start = cmd.dimension === "COL" ? "left" : "top";
@@ -57741,10 +57794,12 @@ class RangeAdapter {
57741
57794
  return adaptedRange;
57742
57795
  }
57743
57796
  executeOnAllRanges(adaptRange, sheetId) {
57797
+ this.isAdaptingRanges = true;
57744
57798
  const func = this.verifyRangeRemoved(adaptRange);
57745
57799
  for (const provider of this.providers) {
57746
57800
  provider(func, sheetId);
57747
57801
  }
57802
+ this.isAdaptingRanges = false;
57748
57803
  }
57749
57804
  /**
57750
57805
  * Stores the functions bound to each plugin to be able to iterate over all ranges of the application,
@@ -59780,6 +59835,7 @@ class PivotCorePlugin extends CorePlugin {
59780
59835
  "getMeasureCompiledFormula",
59781
59836
  "getPivotName",
59782
59837
  "isExistingPivot",
59838
+ "getMeasureFullDependencies",
59783
59839
  ];
59784
59840
  nextFormulaId = 1;
59785
59841
  pivots = {};
@@ -59862,15 +59918,32 @@ class PivotCorePlugin extends CorePlugin {
59862
59918
  }
59863
59919
  case "UPDATE_PIVOT": {
59864
59920
  this.history.update("pivots", cmd.pivotId, "definition", this.repairSortedColumn(deepCopy(cmd.pivot)));
59865
- this.compileCalculatedMeasures(cmd.pivot.measures);
59921
+ this.compileCalculatedMeasures(cmd.pivotId, cmd.pivot.measures);
59866
59922
  break;
59867
59923
  }
59868
59924
  }
59869
59925
  }
59870
59926
  adaptRanges(applyChange) {
59871
- for (const sheetId in this.compiledMeasureFormulas) {
59872
- for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
59873
- const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
59927
+ for (const pivotId in this.pivots) {
59928
+ const definition = deepCopy(this.pivots[pivotId]?.definition);
59929
+ if (!definition) {
59930
+ continue;
59931
+ }
59932
+ const newDefinition = pivotRegistry
59933
+ .get(definition.type)
59934
+ ?.adaptRanges?.(this.getters, definition, applyChange);
59935
+ if (newDefinition && !deepEquals(definition, newDefinition)) {
59936
+ this.history.update("pivots", pivotId, "definition", newDefinition);
59937
+ }
59938
+ }
59939
+ for (const pivotId in this.compiledMeasureFormulas) {
59940
+ for (const measureId in this.compiledMeasureFormulas[pivotId]) {
59941
+ const measure = this.pivots[pivotId]?.definition.measures.find((m) => m.id === measureId);
59942
+ if (!measure || !measure.computedBy) {
59943
+ continue;
59944
+ }
59945
+ const sheetId = measure.computedBy.sheetId;
59946
+ const compiledFormula = this.compiledMeasureFormulas[pivotId][measureId].formula;
59874
59947
  const newDependencies = [];
59875
59948
  for (const range of compiledFormula.dependencies) {
59876
59949
  const change = applyChange(range);
@@ -59882,8 +59955,9 @@ class PivotCorePlugin extends CorePlugin {
59882
59955
  }
59883
59956
  }
59884
59957
  const newFormulaString = this.getters.getFormulaString(sheetId, compiledFormula.tokens, newDependencies);
59885
- if (newFormulaString !== formulaString) {
59886
- this.replaceMeasureFormula(sheetId, formulaString, newFormulaString);
59958
+ const oldFormulaString = measure.computedBy.formula;
59959
+ if (newFormulaString !== oldFormulaString) {
59960
+ this.replaceMeasureFormula(pivotId, measure, newFormulaString);
59887
59961
  }
59888
59962
  }
59889
59963
  }
@@ -59921,12 +59995,17 @@ class PivotCorePlugin extends CorePlugin {
59921
59995
  isExistingPivot(pivotId) {
59922
59996
  return pivotId in this.pivots;
59923
59997
  }
59924
- getMeasureCompiledFormula(measure) {
59998
+ getMeasureCompiledFormula(pivotId, measure) {
59999
+ if (!measure.computedBy) {
60000
+ throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
60001
+ }
60002
+ return this.compiledMeasureFormulas[pivotId][measure.id].formula;
60003
+ }
60004
+ getMeasureFullDependencies(pivotId, measure) {
59925
60005
  if (!measure.computedBy) {
59926
60006
  throw new Error(`Measure ${measure.fieldName} is not computed by formula`);
59927
60007
  }
59928
- const sheetId = measure.computedBy.sheetId;
59929
- return this.compiledMeasureFormulas[sheetId][measure.computedBy.formula];
60008
+ return this.compiledMeasureFormulas[pivotId][measure.id].dependencies;
59930
60009
  }
59931
60010
  // -------------------------------------------------------------------------
59932
60011
  // Private
@@ -59936,18 +60015,42 @@ class PivotCorePlugin extends CorePlugin {
59936
60015
  definition: this.repairSortedColumn(deepCopy(pivot)),
59937
60016
  formulaId,
59938
60017
  });
59939
- this.compileCalculatedMeasures(pivot.measures);
60018
+ this.compileCalculatedMeasures(pivotId, pivot.measures);
59940
60019
  this.history.update("formulaIds", formulaId, pivotId);
59941
60020
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
59942
60021
  }
59943
- compileCalculatedMeasures(measures) {
60022
+ compileCalculatedMeasures(pivotId, measures) {
59944
60023
  for (const measure of measures) {
59945
60024
  if (measure.computedBy) {
59946
- const sheetId = measure.computedBy.sheetId;
59947
60025
  const compiledFormula = this.compileMeasureFormula(measure.computedBy.sheetId, measure.computedBy.formula);
59948
- this.history.update("compiledMeasureFormulas", sheetId, measure.computedBy.formula, compiledFormula);
60026
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "formula", compiledFormula);
60027
+ }
60028
+ }
60029
+ for (const measure of measures) {
60030
+ if (measure.computedBy) {
60031
+ const dependencies = this.computeMeasureFullDependencies(pivotId, measure);
60032
+ this.history.update("compiledMeasureFormulas", pivotId, measure.id, "dependencies", dependencies);
60033
+ }
60034
+ }
60035
+ }
60036
+ computeMeasureFullDependencies(pivotId, measure, exploredMeasures = new Set()) {
60037
+ const rangeDependencies = [];
60038
+ const definition = this.getPivotCoreDefinition(pivotId);
60039
+ const formula = this.getMeasureCompiledFormula(pivotId, measure);
60040
+ exploredMeasures.add(measure.id);
60041
+ for (const token of formula.tokens) {
60042
+ if (token.type !== "SYMBOL") {
60043
+ continue;
60044
+ }
60045
+ const otherMeasure = definition.measures.find((measureCandidate) => getCanonicalSymbolName(measureCandidate.id) === token.value &&
60046
+ measure.id !== measureCandidate.id);
60047
+ if (!otherMeasure || exploredMeasures.has(otherMeasure.id) || !otherMeasure.computedBy) {
60048
+ continue;
59949
60049
  }
60050
+ rangeDependencies.push(...this.computeMeasureFullDependencies(pivotId, otherMeasure, exploredMeasures));
59950
60051
  }
60052
+ rangeDependencies.push(...formula.dependencies.filter((range) => !range.invalidXc));
60053
+ return rangeDependencies;
59951
60054
  }
59952
60055
  insertPivot(position, formulaId, table) {
59953
60056
  this.resizeSheet(position.sheetId, position, table);
@@ -60005,28 +60108,17 @@ class PivotCorePlugin extends CorePlugin {
60005
60108
  dependencies: rangeDependencies,
60006
60109
  };
60007
60110
  }
60008
- replaceMeasureFormula(sheetId, formulaString, newFormulaString) {
60009
- this.history.update("compiledMeasureFormulas", sheetId, formulaString, undefined);
60010
- this.history.update("compiledMeasureFormulas", sheetId, newFormulaString, this.compileMeasureFormula(sheetId, newFormulaString));
60011
- for (const pivotId in this.pivots) {
60012
- const pivot = this.pivots[pivotId];
60013
- if (!pivot) {
60014
- continue;
60015
- }
60016
- const def = deepCopy(pivot.definition);
60017
- for (const measure of def.measures) {
60018
- if (measure.computedBy?.formula === formulaString) {
60019
- const measureIndex = def.measures.indexOf(measure);
60020
- if (measureIndex !== -1) {
60021
- def.measures[measureIndex].computedBy = {
60022
- formula: newFormulaString,
60023
- sheetId,
60024
- };
60025
- }
60026
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
60027
- }
60028
- }
60111
+ replaceMeasureFormula(pivotId, measure, newFormulaString) {
60112
+ const pivot = this.pivots[pivotId];
60113
+ if (!pivot) {
60114
+ return;
60029
60115
  }
60116
+ const measureIndex = pivot.definition.measures.indexOf(measure);
60117
+ this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", {
60118
+ formula: newFormulaString,
60119
+ sheetId: measure.computedBy.sheetId,
60120
+ });
60121
+ this.compileCalculatedMeasures(pivotId, pivot.definition.measures);
60030
60122
  }
60031
60123
  checkDuplicatedMeasureIds(definition) {
60032
60124
  const uniqueIds = new Set(definition.measures.map((m) => m.id));
@@ -60142,20 +60234,6 @@ class SettingsPlugin extends CorePlugin {
60142
60234
  }
60143
60235
  }
60144
60236
 
60145
- function adaptPivotRange(range, applyChange) {
60146
- if (!range) {
60147
- return undefined;
60148
- }
60149
- const change = applyChange(range);
60150
- switch (change.changeType) {
60151
- case "NONE":
60152
- return range;
60153
- case "REMOVE":
60154
- return undefined;
60155
- default:
60156
- return change.range;
60157
- }
60158
- }
60159
60237
  class SpreadsheetPivotCorePlugin extends CorePlugin {
60160
60238
  allowDispatch(cmd) {
60161
60239
  switch (cmd.type) {
@@ -60166,27 +60244,6 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
60166
60244
  }
60167
60245
  return "Success" /* CommandResult.Success */;
60168
60246
  }
60169
- adaptRanges(applyChange) {
60170
- for (const pivotId of this.getters.getPivotIds()) {
60171
- const definition = this.getters.getPivotCoreDefinition(pivotId);
60172
- if (definition.type !== "SPREADSHEET") {
60173
- continue;
60174
- }
60175
- if (definition.dataSet) {
60176
- const { sheetId, zone } = definition.dataSet;
60177
- const range = this.getters.getRangeFromZone(sheetId, zone);
60178
- const adaptedRange = adaptPivotRange(range, applyChange);
60179
- if (adaptedRange === range) {
60180
- return;
60181
- }
60182
- const dataSet = adaptedRange && {
60183
- sheetId: adaptedRange.sheetId,
60184
- zone: adaptedRange.zone,
60185
- };
60186
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
60187
- }
60188
- }
60189
- }
60190
60247
  checkDataSetValidity(definition) {
60191
60248
  if (definition.type === "SPREADSHEET" && definition.dataSet) {
60192
60249
  const { zone, sheetId } = definition.dataSet;
@@ -61528,6 +61585,9 @@ class Evaluator {
61528
61585
  const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
61529
61586
  return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
61530
61587
  }
61588
+ isArrayFormulaSpillBlocked(position) {
61589
+ return this.blockedArrayFormulas.has(position);
61590
+ }
61531
61591
  updateDependencies(position) {
61532
61592
  // removing dependencies is slow because it requires
61533
61593
  // to traverse the entire r-tree.
@@ -61539,13 +61599,8 @@ class Evaluator {
61539
61599
  addDependencies(position, dependencies) {
61540
61600
  this.formulaDependencies().addDependencies(position, dependencies);
61541
61601
  for (const range of dependencies) {
61542
- const sheetId = range.sheetId;
61543
- const { left, bottom, right, top } = range.zone;
61544
- for (let col = left; col <= right; col++) {
61545
- for (let row = top; row <= bottom; row++) {
61546
- this.computeAndSave({ sheetId, col, row });
61547
- }
61548
- }
61602
+ // ensure that all ranges are computed
61603
+ this.compilationParams.ensureRange(range);
61549
61604
  }
61550
61605
  }
61551
61606
  updateCompilationParameters() {
@@ -61748,6 +61803,10 @@ class Evaluator {
61748
61803
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
61749
61804
  const nbColumns = formulaReturn.length;
61750
61805
  const nbRows = formulaReturn[0].length;
61806
+ if (nbRows === 0) {
61807
+ // empty matrix
61808
+ return createEvaluatedCell({ value: 0 }, this.getters.getLocale(), cellData);
61809
+ }
61751
61810
  const resultZone = {
61752
61811
  top: formulaPosition.row,
61753
61812
  bottom: formulaPosition.row + nbRows - 1,
@@ -62023,6 +62082,7 @@ class EvaluationPlugin extends CoreViewPlugin {
62023
62082
  "getEvaluatedCellsPositions",
62024
62083
  "getSpreadZone",
62025
62084
  "getArrayFormulaSpreadingOn",
62085
+ "isArrayFormulaSpillBlocked",
62026
62086
  "isEmpty",
62027
62087
  ];
62028
62088
  shouldRebuildDependenciesGraph = true;
@@ -62135,6 +62195,9 @@ class EvaluationPlugin extends CoreViewPlugin {
62135
62195
  getArrayFormulaSpreadingOn(position) {
62136
62196
  return this.evaluator.getArrayFormulaSpreadingOn(position);
62137
62197
  }
62198
+ isArrayFormulaSpillBlocked(position) {
62199
+ return this.evaluator.isArrayFormulaSpillBlocked(position);
62200
+ }
62138
62201
  /**
62139
62202
  * Check if a zone only contains empty cells
62140
62203
  */
@@ -63320,14 +63383,16 @@ const PERCENT_FORMAT = "0.00%";
63320
63383
  function withPivotPresentationLayer (PivotClass) {
63321
63384
  class PivotPresentationLayer extends PivotClass {
63322
63385
  getters;
63386
+ pivotId;
63323
63387
  cache = {};
63324
63388
  rankAsc = {};
63325
63389
  rankDesc = {};
63326
63390
  runningTotal = {};
63327
63391
  runningTotalInPercent = {};
63328
- constructor(custom, params) {
63392
+ constructor(pivotId, custom, params) {
63329
63393
  super(custom, params);
63330
63394
  this.getters = params.getters;
63395
+ this.pivotId = pivotId;
63331
63396
  }
63332
63397
  markAsDirtyForEvaluation() {
63333
63398
  this.cache = {};
@@ -63377,7 +63442,7 @@ function withPivotPresentationLayer (PivotClass) {
63377
63442
  return handleError(error, measure.aggregator.toUpperCase());
63378
63443
  }
63379
63444
  }
63380
- const formula = this.getters.getMeasureCompiledFormula(measure);
63445
+ const formula = this.getters.getMeasureCompiledFormula(this.pivotId, measure);
63381
63446
  const getSymbolValue = (symbolName) => {
63382
63447
  const { columns, rows } = this.definition;
63383
63448
  if (columns.find((col) => col.nameWithGranularity === symbolName)) {
@@ -64105,7 +64170,7 @@ class PivotUIPlugin extends CoreViewPlugin {
64105
64170
  const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
64106
64171
  if (!(pivotId in this.pivots)) {
64107
64172
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
64108
- this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
64173
+ this.pivots[pivotId] = new Pivot(pivotId, this.custom, { definition, getters: this.getters });
64109
64174
  }
64110
64175
  else if (recreate) {
64111
64176
  this.pivots[pivotId].onDefinitionChange(definition);
@@ -72840,7 +72905,7 @@ class Spreadsheet extends owl.Component {
72840
72905
  document.activeElement?.contains(this.spreadsheetRef.el)) {
72841
72906
  this.focusGrid();
72842
72907
  }
72843
- }, () => [this.env.model.getters.getActiveSheetId()]);
72908
+ });
72844
72909
  owl.useExternalListener(window, "resize", () => this.render(true));
72845
72910
  // For some reason, the wheel event is not properly registered inside templates
72846
72911
  // in Chromium-based browsers based on chromium 125
@@ -77405,6 +77470,6 @@ exports.tokenColors = tokenColors;
77405
77470
  exports.tokenize = tokenize;
77406
77471
 
77407
77472
 
77408
- __info__.version = "18.2.36";
77409
- __info__.date = "2025-12-02T05:31:10.558Z";
77410
- __info__.hash = "d385099";
77473
+ __info__.version = "18.2.40";
77474
+ __info__.date = "2026-01-14T09:59:34.210Z";
77475
+ __info__.hash = "755a787";