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