@odoo/o-spreadsheet 19.0.12 → 19.0.15

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 19.0.12
6
- * @date 2025-12-02T05:34:17.495Z
7
- * @hash 32203f1
5
+ * @version 19.0.15
6
+ * @date 2025-12-26T10:19:23.408Z
7
+ * @hash fe625c9
8
8
  */
9
9
 
10
10
  'use strict';
@@ -1895,21 +1895,25 @@ profilesStartingPosition, profiles, zones, toRemove = false) {
1895
1895
  function profilesContainsZone(profilesStartingPosition, profiles, zone) {
1896
1896
  const leftValue = zone.left;
1897
1897
  const rightValue = zone.right;
1898
- const topValue = zone.top;
1899
- const bottomValue = zone.bottom + 1;
1900
1898
  const leftIndex = binaryPredecessorSearch(profilesStartingPosition, leftValue, 0);
1901
- const rightIndex = binaryPredecessorSearch(profilesStartingPosition, rightValue, leftIndex);
1902
- if (leftIndex === -1 || rightIndex === -1) {
1903
- return false;
1904
- }
1899
+ const rightIndex = rightValue === undefined
1900
+ ? profilesStartingPosition.length - 1
1901
+ : binaryPredecessorSearch(profilesStartingPosition, rightValue, leftIndex);
1902
+ /**
1903
+ * The `profilesStartingPosition` array always contains at least the value `0` at its first position,
1904
+ * ensuring that applying `binaryPredecessorSearch` will always return a valid index.
1905
+ * Therefore, it is not necessary to check if the result of `binaryPredecessorSearch` equals `-1`.
1906
+ */
1907
+ const topValue = zone.top;
1908
+ const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
1905
1909
  for (let i = leftIndex; i <= rightIndex; i++) {
1906
1910
  const profile = profiles.get(profilesStartingPosition[i]);
1907
- const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, true);
1908
- const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, true);
1911
+ const topPredIndex = binaryPredecessorSearch(profile, topValue, 0);
1909
1912
  if (topPredIndex === -1 || topPredIndex % 2 !== 0) {
1910
1913
  return false;
1911
1914
  }
1912
- if (topValue < profile[topPredIndex] || bottomValue > profile[bottomSuccIndex]) {
1915
+ const bottomSuccIndex = bottomValue === undefined ? profile.length : binarySuccessorSearch(profile, bottomValue, 0);
1916
+ if (topPredIndex + 1 !== bottomSuccIndex) {
1913
1917
  return false;
1914
1918
  }
1915
1919
  }
@@ -6595,17 +6599,41 @@ function toCriterionDateNumber(dateValue) {
6595
6599
  const today = DateTime.now();
6596
6600
  switch (dateValue) {
6597
6601
  case "today":
6598
- return jsDateToNumber(today);
6599
- case "yesterday":
6600
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
6601
- case "tomorrow":
6602
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
6602
+ return Math.floor(jsDateToNumber(today));
6603
+ case "yesterday": {
6604
+ today.setDate(today.getDate() - 1);
6605
+ return Math.floor(jsDateToNumber(today));
6606
+ }
6607
+ case "tomorrow": {
6608
+ today.setDate(today.getDate() + 1);
6609
+ return Math.floor(jsDateToNumber(today));
6610
+ }
6603
6611
  case "lastWeek":
6604
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
6605
- case "lastMonth":
6606
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
6612
+ today.setDate(today.getDate() - 6);
6613
+ return Math.floor(jsDateToNumber(today));
6614
+ case "lastMonth": {
6615
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
6616
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
6617
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
6618
+ today.setDate(1);
6619
+ }
6620
+ else {
6621
+ today.setDate(today.getDate() + 1);
6622
+ today.setMonth(today.getMonth() - 1);
6623
+ }
6624
+ return Math.floor(jsDateToNumber(today));
6625
+ }
6607
6626
  case "lastYear":
6608
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
6627
+ // Handle leap year case
6628
+ if (today.getMonth() === 1 && today.getDate() === 29) {
6629
+ today.setDate(28);
6630
+ today.setFullYear(today.getFullYear() - 1);
6631
+ }
6632
+ else {
6633
+ today.setDate(today.getDate() + 1);
6634
+ today.setFullYear(today.getFullYear() - 1);
6635
+ }
6636
+ return Math.floor(jsDateToNumber(today));
6609
6637
  }
6610
6638
  }
6611
6639
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -6778,67 +6806,6 @@ function getFullReference(sheetName, xc) {
6778
6806
  return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
6779
6807
  }
6780
6808
 
6781
- function createDefaultRows(rowNumber) {
6782
- const rows = [];
6783
- for (let i = 0; i < rowNumber; i++) {
6784
- const row = {
6785
- cells: {},
6786
- };
6787
- rows.push(row);
6788
- }
6789
- return rows;
6790
- }
6791
- function moveHeaderIndexesOnHeaderAddition(indexHeaderAdded, numberAdded, headers) {
6792
- return headers.map((header) => {
6793
- if (header >= indexHeaderAdded) {
6794
- return header + numberAdded;
6795
- }
6796
- return header;
6797
- });
6798
- }
6799
- function moveHeaderIndexesOnHeaderDeletion(deletedHeaders, headers) {
6800
- deletedHeaders = [...deletedHeaders].sort((a, b) => b - a);
6801
- return headers
6802
- .map((header) => {
6803
- for (const deletedHeader of deletedHeaders) {
6804
- if (header > deletedHeader) {
6805
- header--;
6806
- }
6807
- else if (header === deletedHeader) {
6808
- return undefined;
6809
- }
6810
- }
6811
- return header;
6812
- })
6813
- .filter(isDefined);
6814
- }
6815
- function getNextSheetName(existingNames, baseName = "Sheet") {
6816
- let i = 1;
6817
- let name = `${baseName}${i}`;
6818
- while (existingNames.includes(name)) {
6819
- name = `${baseName}${i}`;
6820
- i++;
6821
- }
6822
- return name;
6823
- }
6824
- function getDuplicateSheetName(nameToDuplicate, existingNames) {
6825
- let i = 1;
6826
- const baseName = _t("Copy of %s", nameToDuplicate);
6827
- let name = baseName.toString();
6828
- while (existingNames.includes(name)) {
6829
- name = `${baseName} (${i})`;
6830
- i++;
6831
- }
6832
- return name;
6833
- }
6834
- function isSheetNameEqual(name1, name2) {
6835
- if (name1 === undefined || name2 === undefined) {
6836
- return false;
6837
- }
6838
- return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
6839
- getUnquotedSheetName(name2.trim().toUpperCase()));
6840
- }
6841
-
6842
6809
  function createRange(args, getSheetSize) {
6843
6810
  const unboundedZone = args.zone;
6844
6811
  const zone = boundUnboundedZone(unboundedZone, getSheetSize(args.sheetId));
@@ -7086,7 +7053,7 @@ function getApplyRangeChangeRemoveColRow(cmd) {
7086
7053
  elements.sort((a, b) => b - a);
7087
7054
  const groups = groupConsecutive(elements);
7088
7055
  return (range) => {
7089
- if (!isSheetNameEqual(range.sheetId, cmd.sheetId)) {
7056
+ if (range.sheetId !== cmd.sheetId) {
7090
7057
  return { changeType: "NONE" };
7091
7058
  }
7092
7059
  let newRange = range;
@@ -7293,6 +7260,69 @@ function fuzzyLookup(pattern, list, fn) {
7293
7260
  return results.map((r) => r.elem);
7294
7261
  }
7295
7262
 
7263
+ function createDefaultRows(rowNumber) {
7264
+ const rows = [];
7265
+ for (let i = 0; i < rowNumber; i++) {
7266
+ const row = {
7267
+ cells: {},
7268
+ };
7269
+ rows.push(row);
7270
+ }
7271
+ return rows;
7272
+ }
7273
+ function moveHeaderIndexesOnHeaderAddition(indexHeaderAdded, numberAdded, headers) {
7274
+ return headers.map((header) => {
7275
+ if (header >= indexHeaderAdded) {
7276
+ return header + numberAdded;
7277
+ }
7278
+ return header;
7279
+ });
7280
+ }
7281
+ function moveHeaderIndexesOnHeaderDeletion(deletedHeaders, headers) {
7282
+ deletedHeaders = [...deletedHeaders].sort((a, b) => b - a);
7283
+ return headers
7284
+ .map((header) => {
7285
+ for (const deletedHeader of deletedHeaders) {
7286
+ if (header > deletedHeader) {
7287
+ header--;
7288
+ }
7289
+ else if (header === deletedHeader) {
7290
+ return undefined;
7291
+ }
7292
+ }
7293
+ return header;
7294
+ })
7295
+ .filter(isDefined);
7296
+ }
7297
+ function getNextSheetName(existingNames, baseName = "Sheet") {
7298
+ let i = 1;
7299
+ let name = `${baseName}${i}`;
7300
+ while (existingNames.includes(name)) {
7301
+ name = `${baseName}${i}`;
7302
+ i++;
7303
+ }
7304
+ return name;
7305
+ }
7306
+ function getDuplicateSheetName(nameToDuplicate, existingNames) {
7307
+ let i = 1;
7308
+ const baseName = _t("Copy of %s", nameToDuplicate);
7309
+ let name = baseName.toString();
7310
+ while (existingNames.includes(name)) {
7311
+ name = `${baseName} (${i})`;
7312
+ i++;
7313
+ }
7314
+ return name;
7315
+ }
7316
+ const toStandardizedSheetName = memoize(function toStandardizedSheetName(name) {
7317
+ return getUnquotedSheetName(name.trim().toUpperCase());
7318
+ });
7319
+ function isSheetNameEqual(name1, name2) {
7320
+ if (name1 === undefined || name2 === undefined) {
7321
+ return false;
7322
+ }
7323
+ return toStandardizedSheetName(name1) === toStandardizedSheetName(name2);
7324
+ }
7325
+
7296
7326
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
7297
7327
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
7298
7328
  }
@@ -31421,7 +31451,7 @@ function escapeQueryNameSpaces(query) {
31421
31451
  return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
31422
31452
  }
31423
31453
 
31424
- function getChartMenuActions(figureId, onFigureDeleted, env) {
31454
+ function getChartMenuActions(figureId, env) {
31425
31455
  const chartId = env.model.getters.getChartIdFromFigureId(figureId);
31426
31456
  if (!chartId) {
31427
31457
  return [];
@@ -31441,11 +31471,11 @@ function getChartMenuActions(figureId, onFigureDeleted, env) {
31441
31471
  getCutMenuItem(figureId, env),
31442
31472
  getCopyAsImageMenuItem(figureId, env),
31443
31473
  getDownloadChartMenuItem(figureId, env),
31444
- getDeleteMenuItem(figureId, onFigureDeleted, env),
31474
+ getDeleteMenuItem(figureId, env),
31445
31475
  ];
31446
31476
  return createActions(menuItemSpecs).filter((action) => env.model.getters.isReadonly() ? action.isReadonlyAllowed : true);
31447
31477
  }
31448
- function getImageMenuActions(figureId, onFigureDeleted, env) {
31478
+ function getImageMenuActions(figureId, env) {
31449
31479
  const menuItemSpecs = [
31450
31480
  getCopyMenuItem(figureId, env, _t("Image copied to clipboard")),
31451
31481
  getCutMenuItem(figureId, env),
@@ -31488,11 +31518,11 @@ function getImageMenuActions(figureId, onFigureDeleted, env) {
31488
31518
  },
31489
31519
  icon: "o-spreadsheet-Icon.DOWNLOAD",
31490
31520
  },
31491
- getDeleteMenuItem(figureId, onFigureDeleted, env),
31521
+ getDeleteMenuItem(figureId, env),
31492
31522
  ];
31493
31523
  return createActions(menuItemSpecs);
31494
31524
  }
31495
- function getCarouselMenuActions(figureId, onFigureDeleted, env) {
31525
+ function getCarouselMenuActions(figureId, env) {
31496
31526
  const isChartSelected = (env) => env.model.getters.getSelectedCarouselItem(figureId)?.type === "chart";
31497
31527
  const menuItemSpecs = [
31498
31528
  {
@@ -31511,7 +31541,7 @@ function getCarouselMenuActions(figureId, onFigureDeleted, env) {
31511
31541
  },
31512
31542
  { ...getCutMenuItem(figureId, env), name: _t("Cut carousel") },
31513
31543
  {
31514
- ...getDeleteMenuItem(figureId, onFigureDeleted, env),
31544
+ ...getDeleteMenuItem(figureId, env),
31515
31545
  name: _t("Delete carousel"),
31516
31546
  separator: true,
31517
31547
  },
@@ -31654,7 +31684,7 @@ function getDownloadChartMenuItem(figureId, env) {
31654
31684
  isReadonlyAllowed: true,
31655
31685
  };
31656
31686
  }
31657
- function getDeleteMenuItem(figureId, onFigureDeleted, env) {
31687
+ function getDeleteMenuItem(figureId, env) {
31658
31688
  return {
31659
31689
  id: "delete",
31660
31690
  name: _t("Delete"),
@@ -31663,7 +31693,6 @@ function getDeleteMenuItem(figureId, onFigureDeleted, env) {
31663
31693
  sheetId: env.model.getters.getActiveSheetId(),
31664
31694
  figureId,
31665
31695
  });
31666
- onFigureDeleted();
31667
31696
  },
31668
31697
  icon: "o-spreadsheet-Icon.TRASH",
31669
31698
  };
@@ -32476,7 +32505,7 @@ class ChartDashboardMenu extends owl.Component {
32476
32505
  this.menuState.isOpen = true;
32477
32506
  this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32478
32507
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32479
- this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32508
+ this.menuState.menuItems = getChartMenuActions(figureId, this.env);
32480
32509
  }
32481
32510
  get fullScreenMenuItem() {
32482
32511
  if (!this.props.hasFullScreenButton) {
@@ -32503,7 +32532,6 @@ class CarouselFigure extends owl.Component {
32503
32532
  static template = "o-spreadsheet-CarouselFigure";
32504
32533
  static props = {
32505
32534
  figureUI: Object,
32506
- onFigureDeleted: Function,
32507
32535
  editFigureStyle: { type: Function, optional: true },
32508
32536
  isFullScreen: { type: Boolean, optional: true },
32509
32537
  openContextMenu: { type: Function, optional: true },
@@ -32652,7 +32680,6 @@ class ChartFigure extends owl.Component {
32652
32680
  static template = "o-spreadsheet-ChartFigure";
32653
32681
  static props = {
32654
32682
  figureUI: Object,
32655
- onFigureDeleted: Function,
32656
32683
  editFigureStyle: { type: Function, optional: true },
32657
32684
  isFullScreen: { type: Boolean, optional: true },
32658
32685
  openContextMenu: { type: Function, optional: true },
@@ -32686,7 +32713,6 @@ class ImageFigure extends owl.Component {
32686
32713
  static template = "o-spreadsheet-ImageFigure";
32687
32714
  static props = {
32688
32715
  figureUI: Object,
32689
- onFigureDeleted: Function,
32690
32716
  editFigureStyle: { type: Function, optional: true },
32691
32717
  openContextMenu: { type: Function, optional: true },
32692
32718
  };
@@ -32805,13 +32831,11 @@ class FigureComponent extends owl.Component {
32805
32831
  figureUI: Object,
32806
32832
  style: { type: String, optional: true },
32807
32833
  class: { type: String, optional: true },
32808
- onFigureDeleted: { type: Function, optional: true },
32809
32834
  onMouseDown: { type: Function, optional: true },
32810
32835
  onClickAnchor: { type: Function, optional: true },
32811
32836
  };
32812
32837
  static components = { MenuPopover };
32813
32838
  static defaultProps = {
32814
- onFigureDeleted: () => { },
32815
32839
  onMouseDown: () => { },
32816
32840
  onClickAnchor: () => { },
32817
32841
  };
@@ -32889,9 +32913,6 @@ class FigureComponent extends owl.Component {
32889
32913
  this.props.figureUI.id,
32890
32914
  this.figureRef.el,
32891
32915
  ]);
32892
- owl.onWillUnmount(() => {
32893
- this.props.onFigureDeleted();
32894
- });
32895
32916
  }
32896
32917
  clickAnchor(dirX, dirY, ev) {
32897
32918
  this.props.onClickAnchor(dirX, dirY, ev);
@@ -32915,7 +32936,6 @@ class FigureComponent extends owl.Component {
32915
32936
  sheetId: this.env.model.getters.getActiveSheetId(),
32916
32937
  figureId: this.props.figureUI.id,
32917
32938
  });
32918
- this.props.onFigureDeleted();
32919
32939
  ev.preventDefault();
32920
32940
  ev.stopPropagation();
32921
32941
  break;
@@ -33008,7 +33028,7 @@ class FigureComponent extends owl.Component {
33008
33028
  this.menuState.anchorRect = anchorRect;
33009
33029
  this.menuState.menuItems = figureRegistry
33010
33030
  .get(this.props.figureUI.tag)
33011
- .menuBuilder(this.props.figureUI.id, this.props.onFigureDeleted, this.env);
33031
+ .menuBuilder(this.props.figureUI.id, this.env);
33012
33032
  }
33013
33033
  editWrapperStyle(properties) {
33014
33034
  if (this.figureWrapperRef.el) {
@@ -33380,7 +33400,7 @@ criterionEvaluatorRegistry.add("dateIs", {
33380
33400
  return false;
33381
33401
  }
33382
33402
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
33383
- const today = jsDateToRoundNumber(DateTime.now());
33403
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
33384
33404
  return isDateBetween(dateValue, today, criterionValue);
33385
33405
  }
33386
33406
  return areDatesSameDay(dateValue, criterionValue);
@@ -37494,6 +37514,12 @@ class SelectionInput extends owl.Component {
37494
37514
  }
37495
37515
  setup() {
37496
37516
  owl.useEffect(() => this.focusedInput.el?.focus(), () => [this.focusedInput.el]);
37517
+ owl.useEffect(() => {
37518
+ // Check the offsetParent to know if the input or an ancestor is `display: none` (eg. when changing side panel tab)
37519
+ if (this.store.hasFocus && this.selectionRef.el?.offsetParent === null) {
37520
+ this.reset();
37521
+ }
37522
+ });
37497
37523
  this.store = useLocalStore(SelectionInputStore, this.props.ranges, this.props.hasSingleRange || false, this.props.colors, this.props.disabledRanges);
37498
37524
  owl.onWillUpdateProps((nextProps) => {
37499
37525
  if (nextProps.ranges.join() !== this.store.selectionInputValues.join()) {
@@ -45277,7 +45303,7 @@ class FormulaFingerprintStore extends SpreadsheetStore {
45277
45303
  const leftOffset = isLeftUnbounded || left?.colFixed ? 0 : colCellOffset;
45278
45304
  const topOffset = isTopUnbounded || left?.rowFixed ? 0 : rowCellOffset;
45279
45305
  const isRightFixed = (!right && left?.colFixed) || right?.colFixed;
45280
- const isBottomFixed = (!right && left.rowFixed) || right?.rowFixed;
45306
+ const isBottomFixed = (!right && left?.rowFixed) || right?.rowFixed;
45281
45307
  const isRightUnbounded = range.unboundedZone.right === undefined;
45282
45308
  const isBottomUnbounded = range.unboundedZone.bottom === undefined;
45283
45309
  const rightOffset = isRightUnbounded || isRightFixed ? 0 : colCellOffset;
@@ -47841,7 +47867,37 @@ pivotRegistry.add("SPREADSHEET", {
47841
47867
  isMeasureCandidate: (field) => field.type !== "boolean",
47842
47868
  isGroupable: () => true,
47843
47869
  canHaveCustomGroup: (field) => field.type === "char" && !field.isCustomField,
47870
+ adaptRanges: (getters, definition, applyChange) => {
47871
+ if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
47872
+ return definition;
47873
+ }
47874
+ const { sheetId, zone } = definition.dataSet;
47875
+ const range = getters.getRangeFromZone(sheetId, zone);
47876
+ const adaptedRange = adaptPivotRange(range, applyChange);
47877
+ if (adaptedRange === range) {
47878
+ return definition;
47879
+ }
47880
+ const dataSet = adaptedRange && {
47881
+ sheetId: adaptedRange.sheetId,
47882
+ zone: adaptedRange.zone,
47883
+ };
47884
+ return { ...definition, dataSet };
47885
+ },
47844
47886
  });
47887
+ function adaptPivotRange(range, applyChange) {
47888
+ if (!range) {
47889
+ return undefined;
47890
+ }
47891
+ const change = applyChange(range);
47892
+ switch (change.changeType) {
47893
+ case "NONE":
47894
+ return range;
47895
+ case "REMOVE":
47896
+ return undefined;
47897
+ default:
47898
+ return change.range;
47899
+ }
47900
+ }
47845
47901
 
47846
47902
  const pivotProperties = {
47847
47903
  name: _t("See pivot properties"),
@@ -49112,7 +49168,6 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
49112
49168
  }
49113
49169
  get highlights() {
49114
49170
  const position = this.model.getters.getActivePosition();
49115
- const cell = this.getters.getEvaluatedCell(position);
49116
49171
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49117
49172
  const zone = spreader
49118
49173
  ? this.model.getters.getSpreadZone(spreader, { ignoreSpillError: true })
@@ -49120,10 +49175,11 @@ class ArrayFormulaHighlight extends SpreadsheetStore {
49120
49175
  if (!zone) {
49121
49176
  return [];
49122
49177
  }
49178
+ const isArrayFormulaBlocked = this.model.getters.isArrayFormulaSpillBlocked(spreader ?? position);
49123
49179
  return [
49124
49180
  {
49125
49181
  range: this.model.getters.getRangeFromZone(position.sheetId, zone),
49126
- dashed: cell.value === CellErrorType.SpilledBlocked,
49182
+ dashed: isArrayFormulaBlocked,
49127
49183
  color: "#17A2B8",
49128
49184
  noFill: true,
49129
49185
  thinLine: true,
@@ -50530,9 +50586,7 @@ css /*SCSS*/ `
50530
50586
  */
50531
50587
  class FiguresContainer extends owl.Component {
50532
50588
  static template = "o-spreadsheet-FiguresContainer";
50533
- static props = {
50534
- onFigureDeleted: Function,
50535
- };
50589
+ static props = {};
50536
50590
  static components = { FigureComponent };
50537
50591
  dnd = owl.useState({
50538
50592
  draggedFigure: undefined,
@@ -50742,7 +50796,6 @@ class FiguresContainer extends owl.Component {
50742
50796
  carouselFigureId: this.dnd.overlappingCarousel.id,
50743
50797
  chartFigureId: figureUI.id,
50744
50798
  });
50745
- this.props.onFigureDeleted();
50746
50799
  }
50747
50800
  this.dnd.draggedFigure = undefined;
50748
50801
  this.dnd.horizontalSnap = undefined;
@@ -50968,16 +51021,16 @@ css /* scss */ `
50968
51021
  `;
50969
51022
  class GridAddRowsFooter extends owl.Component {
50970
51023
  static template = "o-spreadsheet-GridAddRowsFooter";
50971
- static props = {
50972
- focusGrid: Function,
50973
- };
51024
+ static props = {};
50974
51025
  static components = { ValidationMessages };
51026
+ DOMFocusableElementStore;
50975
51027
  inputRef = owl.useRef("inputRef");
50976
51028
  state = owl.useState({
50977
51029
  inputValue: "100",
50978
51030
  errorFlag: false,
50979
51031
  });
50980
51032
  setup() {
51033
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
50981
51034
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
50982
51035
  }
50983
51036
  get addRowsPosition() {
@@ -50995,7 +51048,7 @@ class GridAddRowsFooter extends owl.Component {
50995
51048
  }
50996
51049
  onKeydown(ev) {
50997
51050
  if (ev.key.toUpperCase() === "ESCAPE") {
50998
- this.props.focusGrid();
51051
+ this.focusDefaultElement();
50999
51052
  }
51000
51053
  else if (ev.key.toUpperCase() === "ENTER") {
51001
51054
  this.onConfirm();
@@ -51022,7 +51075,7 @@ class GridAddRowsFooter extends owl.Component {
51022
51075
  quantity,
51023
51076
  dimension: "ROW",
51024
51077
  });
51025
- this.props.focusGrid();
51078
+ this.focusDefaultElement();
51026
51079
  // After adding new rows, scroll down to the new last row
51027
51080
  const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
51028
51081
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
@@ -51035,7 +51088,12 @@ class GridAddRowsFooter extends owl.Component {
51035
51088
  if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) {
51036
51089
  return;
51037
51090
  }
51038
- this.props.focusGrid();
51091
+ this.focusDefaultElement();
51092
+ }
51093
+ focusDefaultElement() {
51094
+ if (document.activeElement === this.inputRef.el) {
51095
+ this.DOMFocusableElementStore.focus();
51096
+ }
51039
51097
  }
51040
51098
  }
51041
51099
 
@@ -51310,7 +51368,6 @@ class GridOverlay extends owl.Component {
51310
51368
  onCellClicked: { type: Function, optional: true },
51311
51369
  onCellRightClicked: { type: Function, optional: true },
51312
51370
  onGridResized: { type: Function, optional: true },
51313
- onFigureDeleted: { type: Function, optional: true },
51314
51371
  onGridMoved: Function,
51315
51372
  gridOverlayDimensions: String,
51316
51373
  slots: { type: Object, optional: true },
@@ -51325,7 +51382,6 @@ class GridOverlay extends owl.Component {
51325
51382
  onCellClicked: () => { },
51326
51383
  onCellRightClicked: () => { },
51327
51384
  onGridResized: () => { },
51328
- onFigureDeleted: () => { },
51329
51385
  };
51330
51386
  gridOverlay = owl.useRef("gridOverlay");
51331
51387
  cellPopovers;
@@ -56591,7 +56647,7 @@ function useHighlights(highlightProvider) {
56591
56647
  }
56592
56648
 
56593
56649
  css /* scss */ `
56594
- .o-cf-preview {
56650
+ .o-spreadsheet .o-cf-preview {
56595
56651
  &.o-cf-cursor-ptr {
56596
56652
  cursor: pointer;
56597
56653
  }
@@ -56599,6 +56655,7 @@ css /* scss */ `
56599
56655
  border-bottom: 1px solid ${GRAY_300};
56600
56656
  height: 80px;
56601
56657
  padding: 10px;
56658
+ box-sizing: border-box;
56602
56659
  position: relative;
56603
56660
  cursor: pointer;
56604
56661
  &:hover,
@@ -56612,7 +56669,6 @@ css /* scss */ `
56612
56669
  .o-cf-preview-icon {
56613
56670
  border: 1px solid ${GRAY_300};
56614
56671
  background-color: #fff;
56615
- position: absolute;
56616
56672
  height: 50px;
56617
56673
  width: 50px;
56618
56674
  .o-icon {
@@ -56621,12 +56677,6 @@ css /* scss */ `
56621
56677
  }
56622
56678
  }
56623
56679
  .o-cf-preview-description {
56624
- left: 65px;
56625
- margin-bottom: auto;
56626
- margin-right: 8px;
56627
- margin-top: auto;
56628
- position: relative;
56629
- width: 142px;
56630
56680
  .o-cf-preview-description-rule {
56631
56681
  margin-bottom: 4px;
56632
56682
  max-height: 2.8em;
@@ -56636,16 +56686,11 @@ css /* scss */ `
56636
56686
  font-size: 12px;
56637
56687
  }
56638
56688
  }
56639
- .o-cf-delete {
56640
- left: 90%;
56641
- top: 39%;
56642
- position: absolute;
56643
- }
56644
56689
  &:not(:hover):not(.o-cf-dragging) .o-cf-drag-handle {
56645
56690
  display: none !important;
56646
56691
  }
56647
56692
  .o-cf-drag-handle {
56648
- left: -8px;
56693
+ left: 2px;
56649
56694
  cursor: move;
56650
56695
  .o-icon {
56651
56696
  width: 6px;
@@ -62450,10 +62495,10 @@ class BordersPlugin extends CorePlugin {
62450
62495
  // existingBorderSideToClear[side] = true means we should clear the border on that
62451
62496
  // side of the existing adjacent zone before adding the new border.
62452
62497
  const existingBorderSideToClear = {
62453
- left: force || !!newBorder?.right,
62454
- right: force || !!newBorder?.left,
62455
- top: force || !!newBorder?.bottom,
62456
- bottom: force || !!newBorder?.top,
62498
+ left: !!newBorder?.right,
62499
+ right: !!newBorder?.left,
62500
+ top: !!newBorder?.bottom,
62501
+ bottom: !!newBorder?.top,
62457
62502
  };
62458
62503
  let editingZone = [zone];
62459
62504
  for (const existingBorder of this.borders[sheetId] ?? []) {
@@ -65446,6 +65491,7 @@ function rangeToMerge(mergeId, range) {
65446
65491
  class RangeAdapter {
65447
65492
  getters;
65448
65493
  providers = [];
65494
+ isAdaptingRanges = false;
65449
65495
  constructor(getters) {
65450
65496
  this.getters = getters;
65451
65497
  }
@@ -65477,6 +65523,9 @@ class RangeAdapter {
65477
65523
  }
65478
65524
  beforeHandle(command) { }
65479
65525
  handle(cmd) {
65526
+ if (this.isAdaptingRanges) {
65527
+ throw new Error("Plugins cannot dispatch commands during adaptRanges phase");
65528
+ }
65480
65529
  const rangeAdapter = getRangeAdapter(cmd);
65481
65530
  if (rangeAdapter?.applyChange) {
65482
65531
  this.executeOnAllRanges(rangeAdapter.applyChange, rangeAdapter.sheetId, rangeAdapter.sheetName);
@@ -65499,10 +65548,12 @@ class RangeAdapter {
65499
65548
  };
65500
65549
  }
65501
65550
  executeOnAllRanges(adaptRange, sheetId, sheetName) {
65551
+ this.isAdaptingRanges = true;
65502
65552
  const func = this.verifyRangeRemoved(adaptRange);
65503
65553
  for (const provider of this.providers) {
65504
65554
  provider(func, sheetId, sheetName);
65505
65555
  }
65556
+ this.isAdaptingRanges = false;
65506
65557
  }
65507
65558
  /**
65508
65559
  * Stores the functions bound to each plugin to be able to iterate over all ranges of the application,
@@ -65819,7 +65870,7 @@ class SheetPlugin extends CorePlugin {
65819
65870
  break;
65820
65871
  case "CREATE_SHEET":
65821
65872
  const sheet = this.createSheet(cmd.sheetId, cmd.name || this.getNextSheetName(), cmd.cols || 26, cmd.rows || 100, cmd.position);
65822
- this.history.update("sheetIdsMapName", sheet.name, sheet.id);
65873
+ this.history.update("sheetIdsMapName", toStandardizedSheetName(sheet.name), sheet.id);
65823
65874
  break;
65824
65875
  case "MOVE_SHEET":
65825
65876
  this.moveSheet(cmd.sheetId, cmd.delta);
@@ -65886,7 +65937,7 @@ class SheetPlugin extends CorePlugin {
65886
65937
  // that depends on a sheet not already imported will not be able to be
65887
65938
  // compiled
65888
65939
  for (const sheet of data.sheets) {
65889
- this.sheetIdsMapName[sheet.name] = sheet.id;
65940
+ this.sheetIdsMapName[toStandardizedSheetName(sheet.name)] = sheet.id;
65890
65941
  }
65891
65942
  for (const sheetData of data.sheets) {
65892
65943
  const name = sheetData.name || "Sheet" + (Object.keys(this.sheets).length + 1);
@@ -65976,12 +66027,7 @@ class SheetPlugin extends CorePlugin {
65976
66027
  }
65977
66028
  getSheetIdByName(name) {
65978
66029
  if (name) {
65979
- const unquotedName = getUnquotedSheetName(name);
65980
- for (const key in this.sheetIdsMapName) {
65981
- if (isSheetNameEqual(key, unquotedName)) {
65982
- return this.sheetIdsMapName[key];
65983
- }
65984
- }
66030
+ return this.sheetIdsMapName[toStandardizedSheetName(name)];
65985
66031
  }
65986
66032
  return undefined;
65987
66033
  }
@@ -66281,8 +66327,8 @@ class SheetPlugin extends CorePlugin {
66281
66327
  const oldName = sheet.name;
66282
66328
  this.history.update("sheets", sheet.id, "name", name.trim());
66283
66329
  const sheetIdsMapName = Object.assign({}, this.sheetIdsMapName);
66284
- delete sheetIdsMapName[oldName];
66285
- sheetIdsMapName[name] = sheet.id;
66330
+ delete sheetIdsMapName[toStandardizedSheetName(oldName)];
66331
+ sheetIdsMapName[toStandardizedSheetName(name)] = sheet.id;
66286
66332
  this.history.update("sheetIdsMapName", sheetIdsMapName);
66287
66333
  }
66288
66334
  hideSheet(sheetId) {
@@ -66320,7 +66366,7 @@ class SheetPlugin extends CorePlugin {
66320
66366
  });
66321
66367
  }
66322
66368
  const sheetIdsMapName = Object.assign({}, this.sheetIdsMapName);
66323
- sheetIdsMapName[newSheet.name] = newSheet.id;
66369
+ sheetIdsMapName[toStandardizedSheetName(newSheet.name)] = newSheet.id;
66324
66370
  this.history.update("sheetIdsMapName", sheetIdsMapName);
66325
66371
  }
66326
66372
  getDuplicateSheetName(sheetName) {
@@ -66337,7 +66383,7 @@ class SheetPlugin extends CorePlugin {
66337
66383
  orderedSheetIds.splice(currentIndex, 1);
66338
66384
  this.history.update("orderedSheetIds", orderedSheetIds);
66339
66385
  const sheetIdsMapName = Object.assign({}, this.sheetIdsMapName);
66340
- delete sheetIdsMapName[name];
66386
+ delete sheetIdsMapName[toStandardizedSheetName(name)];
66341
66387
  this.history.update("sheetIdsMapName", sheetIdsMapName);
66342
66388
  }
66343
66389
  /**
@@ -67657,6 +67703,18 @@ class PivotCorePlugin extends CorePlugin {
67657
67703
  }
67658
67704
  }
67659
67705
  adaptRanges(applyChange) {
67706
+ for (const pivotId in this.pivots) {
67707
+ const definition = deepCopy(this.pivots[pivotId]?.definition);
67708
+ if (!definition) {
67709
+ continue;
67710
+ }
67711
+ const newDefinition = pivotRegistry
67712
+ .get(definition.type)
67713
+ ?.adaptRanges?.(this.getters, definition, applyChange);
67714
+ if (newDefinition && !deepEquals(definition, newDefinition)) {
67715
+ this.history.update("pivots", pivotId, "definition", newDefinition);
67716
+ }
67717
+ }
67660
67718
  for (const sheetId in this.compiledMeasureFormulas) {
67661
67719
  for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
67662
67720
  const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
@@ -67801,17 +67859,10 @@ class PivotCorePlugin extends CorePlugin {
67801
67859
  if (!pivot) {
67802
67860
  continue;
67803
67861
  }
67804
- const def = deepCopy(pivot.definition);
67805
- for (const measure of def.measures) {
67862
+ for (const measure of pivot.definition.measures) {
67806
67863
  if (measure.computedBy?.formula === formulaString) {
67807
- const measureIndex = def.measures.indexOf(measure);
67808
- if (measureIndex !== -1) {
67809
- def.measures[measureIndex].computedBy = {
67810
- formula: newFormulaString,
67811
- sheetId,
67812
- };
67813
- }
67814
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
67864
+ const measureIndex = pivot.definition.measures.indexOf(measure);
67865
+ this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
67815
67866
  }
67816
67867
  }
67817
67868
  }
@@ -67938,20 +67989,6 @@ class SettingsPlugin extends CorePlugin {
67938
67989
  }
67939
67990
  }
67940
67991
 
67941
- function adaptPivotRange(range, applyChange) {
67942
- if (!range) {
67943
- return undefined;
67944
- }
67945
- const change = applyChange(range);
67946
- switch (change.changeType) {
67947
- case "NONE":
67948
- return range;
67949
- case "REMOVE":
67950
- return undefined;
67951
- default:
67952
- return change.range;
67953
- }
67954
- }
67955
67992
  class SpreadsheetPivotCorePlugin extends CorePlugin {
67956
67993
  allowDispatch(cmd) {
67957
67994
  switch (cmd.type) {
@@ -67962,27 +67999,6 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
67962
67999
  }
67963
68000
  return "Success" /* CommandResult.Success */;
67964
68001
  }
67965
- adaptRanges(applyChange) {
67966
- for (const pivotId of this.getters.getPivotIds()) {
67967
- const definition = this.getters.getPivotCoreDefinition(pivotId);
67968
- if (definition.type !== "SPREADSHEET") {
67969
- continue;
67970
- }
67971
- if (definition.dataSet) {
67972
- const { sheetId, zone } = definition.dataSet;
67973
- const range = this.getters.getRangeFromZone(sheetId, zone);
67974
- const adaptedRange = adaptPivotRange(range, applyChange);
67975
- if (adaptedRange === range) {
67976
- return;
67977
- }
67978
- const dataSet = adaptedRange && {
67979
- sheetId: adaptedRange.sheetId,
67980
- zone: adaptedRange.zone,
67981
- };
67982
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
67983
- }
67984
- }
67985
- }
67986
68002
  checkDataSetValidity(definition) {
67987
68003
  if (definition.type === "SPREADSHEET" && definition.dataSet) {
67988
68004
  const { zone, sheetId } = definition.dataSet;
@@ -68981,13 +68997,6 @@ class ZoneSet {
68981
68997
  }
68982
68998
  return result;
68983
68999
  }
68984
- size() {
68985
- let size = 0;
68986
- for (const profile of this.profiles.values()) {
68987
- size += profile.length;
68988
- }
68989
- return size / 2;
68990
- }
68991
69000
  /**
68992
69001
  * iterator of all the zones in the ZoneSet
68993
69002
  */
@@ -69069,13 +69078,6 @@ class RangeSet {
69069
69078
  clear() {
69070
69079
  this.setsBySheetId = {};
69071
69080
  }
69072
- size() {
69073
- let size = 0;
69074
- for (const sheetId in this.setsBySheetId) {
69075
- size += this.setsBySheetId[sheetId].size();
69076
- }
69077
- return size;
69078
- }
69079
69081
  isEmpty() {
69080
69082
  for (const sheetId in this.setsBySheetId) {
69081
69083
  if (!this.setsBySheetId[sheetId].isEmpty()) {
@@ -69579,6 +69581,9 @@ class Evaluator {
69579
69581
  const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
69580
69582
  return arrayFormulas.find((position) => !this.blockedArrayFormulas.has(position));
69581
69583
  }
69584
+ isArrayFormulaSpillBlocked(position) {
69585
+ return this.blockedArrayFormulas.has(position);
69586
+ }
69582
69587
  updateDependencies(position) {
69583
69588
  // removing dependencies is slow because it requires
69584
69589
  // to traverse the entire r-tree.
@@ -69590,13 +69595,8 @@ class Evaluator {
69590
69595
  addDependencies(position, dependencies) {
69591
69596
  this.formulaDependencies().addDependencies(position, dependencies);
69592
69597
  for (const range of dependencies) {
69593
- const sheetId = range.sheetId;
69594
- const { left, bottom, right, top } = range.zone;
69595
- for (let col = left; col <= right; col++) {
69596
- for (let row = top; row <= bottom; row++) {
69597
- this.computeAndSave({ sheetId, col, row });
69598
- }
69599
- }
69598
+ // ensure that all ranges are computed
69599
+ this.compilationParams.ensureRange(range, false);
69600
69600
  }
69601
69601
  }
69602
69602
  updateCompilationParameters() {
@@ -69824,6 +69824,10 @@ class Evaluator {
69824
69824
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
69825
69825
  const nbColumns = formulaReturn.length;
69826
69826
  const nbRows = formulaReturn[0].length;
69827
+ if (nbRows === 0) {
69828
+ // empty matrix
69829
+ return createEvaluatedCell({ value: 0 }, this.getters.getLocale(), cellData);
69830
+ }
69827
69831
  const resultZone = {
69828
69832
  top: formulaPosition.row,
69829
69833
  bottom: formulaPosition.row + nbRows - 1,
@@ -70094,6 +70098,7 @@ class EvaluationPlugin extends CoreViewPlugin {
70094
70098
  "getEvaluatedCellsPositions",
70095
70099
  "getSpreadZone",
70096
70100
  "getArrayFormulaSpreadingOn",
70101
+ "isArrayFormulaSpillBlocked",
70097
70102
  "isEmpty",
70098
70103
  ];
70099
70104
  shouldRebuildDependenciesGraph = true;
@@ -70213,6 +70218,9 @@ class EvaluationPlugin extends CoreViewPlugin {
70213
70218
  getArrayFormulaSpreadingOn(position) {
70214
70219
  return this.evaluator.getArrayFormulaSpreadingOn(position);
70215
70220
  }
70221
+ isArrayFormulaSpillBlocked(position) {
70222
+ return this.evaluator.isArrayFormulaSpillBlocked(position);
70223
+ }
70216
70224
  /**
70217
70225
  * Check if a zone only contains empty cells
70218
70226
  */
@@ -84318,7 +84326,7 @@ class Spreadsheet extends owl.Component {
84318
84326
  document.activeElement?.contains(this.spreadsheetRef.el)) {
84319
84327
  this.focusGrid();
84320
84328
  }
84321
- }, () => [this.env.model.getters.getActiveSheetId()]);
84329
+ });
84322
84330
  owl.useExternalListener(window, "resize", () => this.render(true));
84323
84331
  // For some reason, the wheel event is not properly registered inside templates
84324
84332
  // in Chromium-based browsers based on chromium 125
@@ -89108,6 +89116,6 @@ exports.tokenColors = tokenColors;
89108
89116
  exports.tokenize = tokenize;
89109
89117
 
89110
89118
 
89111
- __info__.version = "19.0.12";
89112
- __info__.date = "2025-12-02T05:34:17.495Z";
89113
- __info__.hash = "32203f1";
89119
+ __info__.version = "19.0.15";
89120
+ __info__.date = "2025-12-26T10:19:23.408Z";
89121
+ __info__.hash = "fe625c9";