@odoo/o-spreadsheet 16.0.24 → 16.0.27

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.
@@ -624,13 +624,6 @@ function getYearFrac(startDate, endDate, _dayCountConvention) {
624
624
  }
625
625
 
626
626
  //------------------------------------------------------------------------------
627
- /**
628
- * Stringify an object, like JSON.stringify, except that the first level of keys
629
- * is ordered.
630
- */
631
- function stringify(obj) {
632
- return JSON.stringify(obj, Object.keys(obj).sort());
633
- }
634
627
  /**
635
628
  * Remove quotes from a quoted string
636
629
  * ```js
@@ -932,7 +925,7 @@ function isObjectEmptyRecursive(argument) {
932
925
  */
933
926
  function getItemId(item, itemsDic) {
934
927
  for (let [key, value] of Object.entries(itemsDic)) {
935
- if (stringify(value) === stringify(item)) {
928
+ if (deepEquals(value, item)) {
936
929
  return parseInt(key, 10);
937
930
  }
938
931
  }
@@ -1068,6 +1061,22 @@ function transpose2dArray(matrix) {
1068
1061
  if (!matrix.length)
1069
1062
  return matrix;
1070
1063
  return matrix[0].map((_, i) => matrix.map((row) => row[i]));
1064
+ }
1065
+ /**
1066
+ * Creates a version of the function that's memoized on the value of its first
1067
+ * argument, if any.
1068
+ */
1069
+ function memoize(func) {
1070
+ const cache = new Map();
1071
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1072
+ return {
1073
+ [funcName](...args) {
1074
+ if (!cache.has(args[0])) {
1075
+ cache.set(args[0], func(...args));
1076
+ }
1077
+ return cache.get(args[0]);
1078
+ },
1079
+ }[funcName];
1071
1080
  }
1072
1081
 
1073
1082
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -1347,8 +1356,10 @@ function isSameColor(color1, color2) {
1347
1356
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
1348
1357
  // Same as above, but matches the exact string (nothing before or after)
1349
1358
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
1350
- /** Reference of a column header (eg. A, AB) */
1351
- const colHeader = new RegExp(/^([A-Z]{1,3})+$/, "i");
1359
+ /** Reference of a column header (eg. A, AB, $A) */
1360
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
1361
+ /** Reference of a row header (eg. 1, $1) */
1362
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
1352
1363
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
1353
1364
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
1354
1365
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -1378,6 +1389,9 @@ function isRowReference(xc) {
1378
1389
  function isColHeader(str) {
1379
1390
  return colHeader.test(str);
1380
1391
  }
1392
+ function isRowHeader(str) {
1393
+ return rowHeader.test(str);
1394
+ }
1381
1395
  /**
1382
1396
  * Return true if the given xc is the reference of a single cell,
1383
1397
  * without any specified sheet (e.g. A1)
@@ -2078,18 +2092,10 @@ class RangeImpl {
2078
2092
  if (isFullCol) {
2079
2093
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2080
2094
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2081
- if (zone.left === zone.right) {
2082
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2083
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2084
- }
2085
2095
  }
2086
2096
  if (isFullRow) {
2087
2097
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2088
2098
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2089
- if (zone.top === zone.bottom) {
2090
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2091
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2092
- }
2093
2099
  }
2094
2100
  return parts;
2095
2101
  }
@@ -3305,6 +3311,7 @@ exports.CommandResult = void 0;
3305
3311
  CommandResult[CommandResult["ChartDoesNotExist"] = 84] = "ChartDoesNotExist";
3306
3312
  CommandResult[CommandResult["InvalidHeaderIndex"] = 85] = "InvalidHeaderIndex";
3307
3313
  CommandResult[CommandResult["InvalidQuantity"] = 86] = "InvalidQuantity";
3314
+ CommandResult[CommandResult["NoChanges"] = 87] = "NoChanges";
3308
3315
  })(exports.CommandResult || (exports.CommandResult = {}));
3309
3316
 
3310
3317
  function isMatrix(x) {
@@ -3880,6 +3887,7 @@ function getMenuDescription(node) {
3880
3887
  return node.description ? node.description : "";
3881
3888
  }
3882
3889
 
3890
+ const macRegex = /Mac/i;
3883
3891
  /**
3884
3892
  * Return true if the event was triggered from
3885
3893
  * a child element.
@@ -3897,6 +3905,17 @@ function gridOverlayPosition() {
3897
3905
  }
3898
3906
  function getOpenedMenus() {
3899
3907
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
3908
+ }
3909
+ function isMacOS() {
3910
+ return Boolean(macRegex.test(navigator.userAgent));
3911
+ }
3912
+ /**
3913
+ * @param {KeyboardEvent | MouseEvent} ev
3914
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
3915
+ * On Mac, this is the "meta" or "command" key.
3916
+ */
3917
+ function isCtrlKey(ev) {
3918
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
3900
3919
  }
3901
3920
 
3902
3921
  /**
@@ -10980,12 +10999,12 @@ function toString(value) {
10980
10999
  }
10981
11000
  }
10982
11001
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
10983
- function normalizeString(str) {
11002
+ const normalizeString = memoize(function normalizeString(str) {
10984
11003
  return str
10985
11004
  .toLowerCase()
10986
11005
  .normalize("NFD")
10987
11006
  .replace(/[\u0300-\u036f]/g, "");
10988
- }
11007
+ });
10989
11008
  /**
10990
11009
  * Normalize a value.
10991
11010
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -18560,7 +18579,7 @@ const machine = {
18560
18579
  SPACE: goTo(State.RightRef),
18561
18580
  NUMBER: goTo(State.Found),
18562
18581
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
18563
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
18582
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
18564
18583
  },
18565
18584
  [State.RightColumnRef]: {
18566
18585
  SPACE: goTo(State.RightColumnRef),
@@ -18571,6 +18590,7 @@ const machine = {
18571
18590
  SPACE: goTo(State.RightRowRef),
18572
18591
  NUMBER: goTo(State.Found),
18573
18592
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
18593
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
18574
18594
  },
18575
18595
  [State.Found]: {},
18576
18596
  };
@@ -19080,6 +19100,10 @@ function setXcToReferenceType(xc, referenceType) {
19080
19100
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
19081
19101
  case "colrow":
19082
19102
  indexOfNumber = xc.search(/[0-9]/);
19103
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
19104
+ // no row number (eg. A) or no column (eg. 1)
19105
+ return "$" + xc;
19106
+ }
19083
19107
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
19084
19108
  return "$" + xc;
19085
19109
  case "none":
@@ -21461,7 +21485,10 @@ class GridOverlay extends owl.Component {
21461
21485
  return;
21462
21486
  }
21463
21487
  const [col, row] = this.getCartesianCoordinates(ev);
21464
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
21488
+ this.props.onCellClicked(col, row, {
21489
+ expandZone: ev.shiftKey,
21490
+ addZone: isCtrlKey(ev),
21491
+ });
21465
21492
  }
21466
21493
  onDoubleClick(ev) {
21467
21494
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -21533,6 +21560,7 @@ class AbstractResizer extends owl.Component {
21533
21560
  draggerShadowThickness: 0,
21534
21561
  delta: 0,
21535
21562
  base: 0,
21563
+ position: "before",
21536
21564
  });
21537
21565
  }
21538
21566
  _computeHandleDisplay(ev) {
@@ -21658,11 +21686,13 @@ class AbstractResizer extends owl.Component {
21658
21686
  this.state.draggerLinePosition = dimensions.start;
21659
21687
  this.state.draggerShadowPosition = dimensions.start;
21660
21688
  this.state.base = elementIndex;
21689
+ this.state.position = "before";
21661
21690
  }
21662
21691
  else if (this._getSelectedZoneEnd() < elementIndex) {
21663
21692
  this.state.draggerLinePosition = dimensions.end;
21664
21693
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
21665
- this.state.base = elementIndex + 1;
21694
+ this.state.base = elementIndex;
21695
+ this.state.position = "after";
21666
21696
  }
21667
21697
  else {
21668
21698
  this.state.draggerLinePosition = startDimensions.start;
@@ -21686,7 +21716,7 @@ class AbstractResizer extends owl.Component {
21686
21716
  this._increaseSelection(index);
21687
21717
  }
21688
21718
  else {
21689
- this._selectElement(index, ev.ctrlKey);
21719
+ this._selectElement(index, isCtrlKey(ev));
21690
21720
  }
21691
21721
  this.lastSelectedElementIndex = index;
21692
21722
  const mouseMoveSelect = (col, row) => {
@@ -21699,7 +21729,7 @@ class AbstractResizer extends owl.Component {
21699
21729
  const mouseUpSelect = () => {
21700
21730
  this.state.isSelecting = false;
21701
21731
  this.lastSelectedElementIndex = null;
21702
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21732
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21703
21733
  this._computeGrabDisplay(ev);
21704
21734
  };
21705
21735
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -21840,13 +21870,14 @@ class ColResizer extends AbstractResizer {
21840
21870
  dimension: "COL",
21841
21871
  base: this.state.base,
21842
21872
  elements,
21873
+ position: this.state.position,
21843
21874
  });
21844
21875
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
21845
21876
  this.env.raiseError(MergeErrorMessage);
21846
21877
  }
21847
21878
  }
21848
- _selectElement(index, ctrlKey) {
21849
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
21879
+ _selectElement(index, addDistinctHeader) {
21880
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21850
21881
  }
21851
21882
  _increaseSelection(index) {
21852
21883
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -22009,13 +22040,14 @@ class RowResizer extends AbstractResizer {
22009
22040
  dimension: "ROW",
22010
22041
  base: this.state.base,
22011
22042
  elements,
22043
+ position: this.state.position,
22012
22044
  });
22013
22045
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
22014
22046
  this.env.raiseError(MergeErrorMessage);
22015
22047
  }
22016
22048
  }
22017
- _selectElement(index, ctrlKey) {
22018
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
22049
+ _selectElement(index, addDistinctHeader) {
22050
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
22019
22051
  }
22020
22052
  _increaseSelection(index) {
22021
22053
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -22111,8 +22143,8 @@ function useWheelHandler(handler) {
22111
22143
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
22112
22144
  }
22113
22145
  const onMouseWheel = (ev) => {
22114
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
22115
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
22146
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
22147
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
22116
22148
  handler(deltaX, deltaY);
22117
22149
  };
22118
22150
  return onMouseWheel;
@@ -22706,18 +22738,18 @@ class Grid extends owl.Component {
22706
22738
  // ---------------------------------------------------------------------------
22707
22739
  // Zone selection with mouse
22708
22740
  // ---------------------------------------------------------------------------
22709
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
22710
- if (ctrlKey) {
22741
+ onCellClicked(col, row, { addZone, expandZone }) {
22742
+ if (addZone) {
22711
22743
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
22712
22744
  }
22713
22745
  this.closeOpenedPopover();
22714
22746
  if (this.env.model.getters.getEditionMode() === "editing") {
22715
22747
  this.env.model.dispatch("STOP_EDITION");
22716
22748
  }
22717
- if (shiftKey) {
22749
+ if (expandZone) {
22718
22750
  this.env.model.selection.setAnchorCorner(col, row);
22719
22751
  }
22720
- else if (ctrlKey) {
22752
+ else if (addZone) {
22721
22753
  this.env.model.selection.addCellToSelection(col, row);
22722
22754
  }
22723
22755
  else {
@@ -22771,10 +22803,10 @@ class Grid extends owl.Component {
22771
22803
  };
22772
22804
  const direction = arrowMap[ev.key];
22773
22805
  if (ev.shiftKey) {
22774
- this.env.model.selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
22806
+ this.env.model.selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
22775
22807
  }
22776
22808
  else {
22777
- this.env.model.selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
22809
+ this.env.model.selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
22778
22810
  }
22779
22811
  if (this.env.model.getters.isPaintingFormat()) {
22780
22812
  this.env.model.dispatch("PASTE", {
@@ -22788,9 +22820,7 @@ class Grid extends owl.Component {
22788
22820
  return;
22789
22821
  }
22790
22822
  let keyDownString = "";
22791
- if (ev.ctrlKey)
22792
- keyDownString += "CTRL+";
22793
- if (ev.metaKey)
22823
+ if (isCtrlKey(ev))
22794
22824
  keyDownString += "CTRL+";
22795
22825
  if (ev.altKey)
22796
22826
  keyDownString += "ALT+";
@@ -27105,6 +27135,14 @@ class BordersPlugin extends CorePlugin {
27105
27135
  // ---------------------------------------------------------------------------
27106
27136
  // Command Handling
27107
27137
  // ---------------------------------------------------------------------------
27138
+ allowDispatch(cmd) {
27139
+ switch (cmd.type) {
27140
+ case "SET_BORDER":
27141
+ return this.checkBordersUnchanged(cmd);
27142
+ default:
27143
+ return 0 /* CommandResult.Success */;
27144
+ }
27145
+ }
27108
27146
  handle(cmd) {
27109
27147
  switch (cmd.type) {
27110
27148
  case "ADD_MERGE":
@@ -27500,6 +27538,15 @@ class BordersPlugin extends CorePlugin {
27500
27538
  this.setBorders(sheetId, [{ ...zone, left: right }], "right");
27501
27539
  }
27502
27540
  }
27541
+ checkBordersUnchanged(cmd) {
27542
+ var _a, _b, _c, _d;
27543
+ const currentBorder = this.getCellBorder(cmd.sheetId, cmd.col, cmd.row);
27544
+ const areAllNewBordersUndefined = !((_a = cmd.border) === null || _a === void 0 ? void 0 : _a.bottom) && !((_b = cmd.border) === null || _b === void 0 ? void 0 : _b.left) && !((_c = cmd.border) === null || _c === void 0 ? void 0 : _c.right) && !((_d = cmd.border) === null || _d === void 0 ? void 0 : _d.top);
27545
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
27546
+ return 87 /* CommandResult.NoChanges */;
27547
+ }
27548
+ return 0 /* CommandResult.Success */;
27549
+ }
27503
27550
  // ---------------------------------------------------------------------------
27504
27551
  // Import/Export
27505
27552
  // ---------------------------------------------------------------------------
@@ -27535,7 +27582,7 @@ class BordersPlugin extends CorePlugin {
27535
27582
  */
27536
27583
  function getBorderId(border) {
27537
27584
  for (let [key, value] of Object.entries(borders)) {
27538
- if (stringify(value) === stringify(border)) {
27585
+ if (deepEquals(value, border)) {
27539
27586
  return parseInt(key, 10);
27540
27587
  }
27541
27588
  }
@@ -27578,6 +27625,7 @@ const nbspRegexp = new RegExp(String.fromCharCode(160), "g");
27578
27625
  class CellPlugin extends CorePlugin {
27579
27626
  constructor() {
27580
27627
  super(...arguments);
27628
+ this.nextId = 1;
27581
27629
  this.cells = {};
27582
27630
  this.createCell = cellFactory(this.getters);
27583
27631
  }
@@ -27603,8 +27651,9 @@ class CellPlugin extends CorePlugin {
27603
27651
  allowDispatch(cmd) {
27604
27652
  switch (cmd.type) {
27605
27653
  case "UPDATE_CELL":
27654
+ return this.checkCellOutOfSheet(cmd);
27606
27655
  case "CLEAR_CELL":
27607
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
27656
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
27608
27657
  default:
27609
27658
  return 0 /* CommandResult.Success */;
27610
27659
  }
@@ -27745,7 +27794,7 @@ class CellPlugin extends CorePlugin {
27745
27794
  importCell(sheetId, cellData, normalizedStyles, normalizedFormats) {
27746
27795
  const style = (cellData.style && normalizedStyles[cellData.style]) || undefined;
27747
27796
  const format = (cellData.format && normalizedFormats[cellData.format]) || undefined;
27748
- const cellId = this.uuidGenerator.uuidv4();
27797
+ const cellId = this.getNextUid();
27749
27798
  const properties = { format, style };
27750
27799
  return this.createCell(cellId, (cellData === null || cellData === void 0 ? void 0 : cellData.content) || "", properties, sheetId);
27751
27800
  }
@@ -27776,14 +27825,9 @@ class CellPlugin extends CorePlugin {
27776
27825
  */
27777
27826
  getCellById(cellId) {
27778
27827
  // this must be as fast as possible
27779
- for (const sheetId in this.cells) {
27780
- const sheet = this.cells[sheetId];
27781
- const cell = sheet[cellId];
27782
- if (cell) {
27783
- return cell;
27784
- }
27785
- }
27786
- return undefined;
27828
+ const position = this.getters.getCellPosition(cellId);
27829
+ const sheet = this.cells[position.sheetId];
27830
+ return sheet[cellId];
27787
27831
  }
27788
27832
  /*
27789
27833
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -27891,6 +27935,11 @@ class CellPlugin extends CorePlugin {
27891
27935
  }
27892
27936
  return format;
27893
27937
  }
27938
+ getNextUid() {
27939
+ const id = this.nextId.toString();
27940
+ this.history.update("nextId", this.nextId + 1);
27941
+ return id;
27942
+ }
27894
27943
  updateCell(sheetId, col, row, after) {
27895
27944
  var _a;
27896
27945
  const before = this.getters.getCell(sheetId, col, row);
@@ -27929,7 +27978,7 @@ class CellPlugin extends CorePlugin {
27929
27978
  }
27930
27979
  return;
27931
27980
  }
27932
- const cellId = (before === null || before === void 0 ? void 0 : before.id) || this.uuidGenerator.uuidv4();
27981
+ const cellId = (before === null || before === void 0 ? void 0 : before.id) || this.getNextUid();
27933
27982
  const didContentChange = hasContent;
27934
27983
  const properties = { format, style };
27935
27984
  const cell = this.createCell(cellId, afterContent, properties, sheetId);
@@ -27943,13 +27992,23 @@ class CellPlugin extends CorePlugin {
27943
27992
  this.history.update("cells", sheetId, cell.id, cell);
27944
27993
  this.dispatch("UPDATE_CELL_POSITION", { cellId: cell.id, col, row, sheetId });
27945
27994
  }
27946
- checkCellOutOfSheet(sheetId, col, row) {
27995
+ checkCellOutOfSheet(cmd) {
27996
+ const { sheetId, col, row } = cmd;
27947
27997
  const sheet = this.getters.tryGetSheet(sheetId);
27948
27998
  if (!sheet)
27949
27999
  return 27 /* CommandResult.InvalidSheetId */;
27950
28000
  const sheetZone = this.getters.getSheetZone(sheetId);
27951
28001
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
27952
28002
  }
28003
+ checkUselessClearCell(cmd) {
28004
+ const cell = this.getters.getCell(cmd.sheetId, cmd.col, cmd.row);
28005
+ if (!cell)
28006
+ return 87 /* CommandResult.NoChanges */;
28007
+ if (!cell.content && !cell.style && !cell.format) {
28008
+ return 87 /* CommandResult.NoChanges */;
28009
+ }
28010
+ return 0 /* CommandResult.Success */;
28011
+ }
27953
28012
  }
27954
28013
  CellPlugin.getters = [
27955
28014
  "zoneToXC",
@@ -29901,7 +29960,7 @@ class SheetPlugin extends CorePlugin {
29901
29960
  const elements = cmd.dimension === "COL"
29902
29961
  ? this.getNumberCols(cmd.sheetId)
29903
29962
  : this.getNumberRows(cmd.sheetId);
29904
- if (cmd.base < 0 || cmd.base > elements) {
29963
+ if (cmd.base < 0 || cmd.base >= elements) {
29905
29964
  return 85 /* CommandResult.InvalidHeaderIndex */;
29906
29965
  }
29907
29966
  else if (cmd.quantity <= 0) {
@@ -30514,10 +30573,13 @@ class SheetPlugin extends CorePlugin {
30514
30573
  // begin with the end.
30515
30574
  rows.sort((a, b) => b - a);
30516
30575
  for (let group of groupConsecutive(rows)) {
30576
+ // indexes are sorted in the descending order
30577
+ const from = group[group.length - 1];
30578
+ const to = group[0];
30517
30579
  // Move the cells.
30518
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
30519
- // Effectively delete the element and recompute the left-right/top-bottom.
30520
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
30580
+ this.moveCellOnRowsDeletion(sheet, from, to);
30581
+ // Effectively delete the rows
30582
+ this.updateRowsStructureOnDeletion(sheet, from, to);
30521
30583
  }
30522
30584
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
30523
30585
  if (count) {
@@ -30539,8 +30601,6 @@ class SheetPlugin extends CorePlugin {
30539
30601
  this.addEmptyRows(sheet, quantity);
30540
30602
  // Move the cells.
30541
30603
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
30542
- // Recompute the left-right/top-bottom.
30543
- this.updateRowsStructureOnAddition(sheet, row, quantity);
30544
30604
  if (index < sheet.panes.ySplit) {
30545
30605
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
30546
30606
  }
@@ -30641,35 +30701,20 @@ class SheetPlugin extends CorePlugin {
30641
30701
  }
30642
30702
  }
30643
30703
  }
30644
- updateRowsStructureOnDeletion(index, sheet) {
30704
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
30645
30705
  const rows = [];
30646
- const cellsQueue = sheet.rows.map((row) => row.cells);
30706
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
30647
30707
  for (let i in sheet.rows) {
30648
- if (Number(i) === index) {
30708
+ const row = Number(i);
30709
+ if (row >= deleteFromRow && row <= deleteToRow) {
30649
30710
  continue;
30650
30711
  }
30651
30712
  rows.push({
30652
- cells: cellsQueue.shift(),
30713
+ cells: cellsQueue.pop(),
30653
30714
  });
30654
30715
  }
30655
30716
  this.history.update("sheets", sheet.id, "rows", rows);
30656
30717
  }
30657
- /**
30658
- * Update the rows of the sheet after an addition:
30659
- * - Rename the rows
30660
- *
30661
- * @param sheet Sheet on which the deletion occurs
30662
- * @param addedRow Index of the added row
30663
- * @param rowsToAdd Number of the rows to add
30664
- */
30665
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
30666
- const rows = [];
30667
- const cellsQueue = sheet.rows.map((row) => row.cells);
30668
- sheet.rows.forEach(() => rows.push({
30669
- cells: cellsQueue.shift(),
30670
- }));
30671
- this.history.update("sheets", sheet.id, "rows", rows);
30672
- }
30673
30718
  /**
30674
30719
  * Add empty rows at the end of the rows
30675
30720
  *
@@ -32536,13 +32581,18 @@ class EvaluationPlugin extends UIPlugin {
32536
32581
  // Getters
32537
32582
  // ---------------------------------------------------------------------------
32538
32583
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
32539
- const compiledFormula = compile(formulaString);
32540
- const params = this.getCompilationParameters(() => { });
32541
- const ranges = [];
32542
- for (let xc of compiledFormula.dependencies) {
32543
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32584
+ try {
32585
+ const compiledFormula = compile(formulaString);
32586
+ const params = this.getCompilationParameters(() => { });
32587
+ const ranges = [];
32588
+ for (let xc of compiledFormula.dependencies) {
32589
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32590
+ }
32591
+ return compiledFormula.execute(ranges, ...params).value;
32592
+ }
32593
+ catch (error) {
32594
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
32544
32595
  }
32545
- return compiledFormula.execute(ranges, ...params).value;
32546
32596
  }
32547
32597
  /**
32548
32598
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32658,6 +32708,7 @@ class EvaluationPlugin extends UIPlugin {
32658
32708
  }
32659
32709
  return cell.evaluated;
32660
32710
  }
32711
+ const rangeCache = {};
32661
32712
  /**
32662
32713
  * Return the values of the cell(s) used in reference, but always in the format of a range even
32663
32714
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -32674,21 +32725,26 @@ class EvaluationPlugin extends UIPlugin {
32674
32725
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
32675
32726
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
32676
32727
  const sheetZone = getters.getSheetZone(sheetId);
32677
- const result = [];
32678
32728
  const zone = intersection(range.zone, sheetZone);
32679
32729
  if (!zone) {
32680
- result.push([]);
32681
- return result;
32730
+ return [[]];
32731
+ }
32732
+ const { top, left, bottom, right } = zone;
32733
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
32734
+ if (cacheKey in rangeCache) {
32735
+ return rangeCache[cacheKey];
32682
32736
  }
32737
+ const result = new Array(right - left + 1);
32683
32738
  // Performance issue: nested loop is faster than a map here
32684
- for (let col = zone.left; col <= zone.right; col++) {
32685
- const rowValues = [];
32686
- for (let row = zone.top; row <= zone.bottom; row++) {
32739
+ for (let col = left; col <= right; col++) {
32740
+ const rowValues = new Array(bottom - top + 1);
32741
+ for (let row = top; row <= bottom; row++) {
32687
32742
  const cell = evalContext.getters.getCell(range.sheetId, col, row);
32688
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
32743
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
32689
32744
  }
32690
- result.push(rowValues);
32745
+ result[col - left] = rowValues;
32691
32746
  }
32747
+ rangeCache[cacheKey] = result;
32692
32748
  return result;
32693
32749
  }
32694
32750
  /**
@@ -32938,39 +32994,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32938
32994
  this.computedIcons[activeSheetId] = {};
32939
32995
  const computedStyle = this.computedStyles[activeSheetId];
32940
32996
  for (let cf of this.getters.getConditionalFormats(activeSheetId).reverse()) {
32941
- try {
32942
- switch (cf.rule.type) {
32943
- case "ColorScaleRule":
32944
- for (let range of cf.ranges) {
32945
- this.applyColorScale(range, cf.rule);
32946
- }
32947
- break;
32948
- case "IconSetRule":
32949
- for (let range of cf.ranges) {
32950
- this.applyIcon(range, cf.rule);
32951
- }
32952
- break;
32953
- default:
32954
- for (let ref of cf.ranges) {
32955
- const zone = this.getters.getRangeFromSheetXC(activeSheetId, ref).zone;
32956
- for (let row = zone.top; row <= zone.bottom; row++) {
32957
- for (let col = zone.left; col <= zone.right; col++) {
32958
- const pr = this.rulePredicate[cf.rule.type];
32959
- let cell = this.getters.getCell(activeSheetId, col, row);
32960
- if (pr && pr(cell, cf.rule)) {
32961
- if (!computedStyle[col])
32962
- computedStyle[col] = [];
32963
- // we must combine all the properties of all the CF rules applied to the given cell
32964
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32965
- }
32997
+ switch (cf.rule.type) {
32998
+ case "ColorScaleRule":
32999
+ for (let range of cf.ranges) {
33000
+ this.applyColorScale(range, cf.rule);
33001
+ }
33002
+ break;
33003
+ case "IconSetRule":
33004
+ for (let range of cf.ranges) {
33005
+ this.applyIcon(range, cf.rule);
33006
+ }
33007
+ break;
33008
+ default:
33009
+ for (let ref of cf.ranges) {
33010
+ const zone = this.getters.getRangeFromSheetXC(activeSheetId, ref).zone;
33011
+ for (let row = zone.top; row <= zone.bottom; row++) {
33012
+ for (let col = zone.left; col <= zone.right; col++) {
33013
+ const pr = this.rulePredicate[cf.rule.type];
33014
+ let cell = this.getters.getCell(activeSheetId, col, row);
33015
+ if (pr && pr(cell, cf.rule)) {
33016
+ if (!computedStyle[col])
33017
+ computedStyle[col] = [];
33018
+ // we must combine all the properties of all the CF rules applied to the given cell
33019
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32966
33020
  }
32967
33021
  }
32968
33022
  }
32969
- break;
32970
- }
32971
- }
32972
- catch (_) {
32973
- // we don't care about the errors within the evaluation of a rule
33023
+ }
33024
+ break;
32974
33025
  }
32975
33026
  }
32976
33027
  }
@@ -32994,7 +33045,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32994
33045
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32995
33046
  case "formula":
32996
33047
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32997
- return !(value instanceof Promise) ? value : null;
33048
+ return typeof value === "number" ? value : null;
32998
33049
  default:
32999
33050
  return null;
33000
33051
  }
@@ -34942,7 +34993,7 @@ class GridSelectionPlugin extends UIPlugin {
34942
34993
  sheetId: cmd.sheetId,
34943
34994
  base: cmd.base,
34944
34995
  quantity: thickness,
34945
- position: "before",
34996
+ position: cmd.position,
34946
34997
  });
34947
34998
  const isCol = cmd.dimension === "COL";
34948
34999
  const start = cmd.elements[0];
@@ -34959,12 +35010,13 @@ class GridSelectionPlugin extends UIPlugin {
34959
35010
  },
34960
35011
  ];
34961
35012
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
35013
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
34962
35014
  const pasteTarget = [
34963
35015
  {
34964
- left: isCol ? cmd.base : 0,
34965
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
34966
- top: !isCol ? cmd.base : 0,
34967
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
35016
+ left: isCol ? base : 0,
35017
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
35018
+ top: !isCol ? base : 0,
35019
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
34968
35020
  },
34969
35021
  ];
34970
35022
  state.paste(pasteTarget, { selectTarget: true });
@@ -35001,6 +35053,11 @@ class GridSelectionPlugin extends UIPlugin {
35001
35053
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
35002
35054
  return 2 /* CommandResult.WillRemoveExistingMerge */;
35003
35055
  }
35056
+ const headers = [cmd.base, ...cmd.elements];
35057
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
35058
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
35059
+ return 85 /* CommandResult.InvalidHeaderIndex */;
35060
+ }
35004
35061
  return 0 /* CommandResult.Success */;
35005
35062
  }
35006
35063
  //-------------------------------------------
@@ -39067,7 +39124,7 @@ class Spreadsheet extends owl.Component {
39067
39124
  }
39068
39125
  onKeydown(ev) {
39069
39126
  let keyDownString = "";
39070
- if (ev.ctrlKey || ev.metaKey) {
39127
+ if (isCtrlKey(ev)) {
39071
39128
  keyDownString += "CTRL+";
39072
39129
  }
39073
39130
  keyDownString += ev.key.toUpperCase();
@@ -39911,9 +39968,9 @@ function revertChanges(revisions) {
39911
39968
  * Apply the changes of the given HistoryChange to the state
39912
39969
  */
39913
39970
  function applyChange(change, target) {
39914
- let val = change.root;
39915
- let key = change.path[change.path.length - 1];
39916
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
39971
+ let val = change.path[0];
39972
+ const key = change.path[change.path.length - 1];
39973
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
39917
39974
  const p = change.path[pathIndex];
39918
39975
  if (val[p] === undefined) {
39919
39976
  const nextPath = change.path[pathIndex + 1];
@@ -41043,13 +41100,14 @@ class StateObserver {
41043
41100
  }
41044
41101
  addChange(...args) {
41045
41102
  const val = args.pop();
41046
- const [root, ...path] = args;
41103
+ const root = args[0];
41047
41104
  let value = root;
41048
- let key = path[path.length - 1];
41049
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
41050
- const p = path[pathIndex];
41105
+ let key = args[args.length - 1];
41106
+ const pathLength = args.length - 2;
41107
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
41108
+ const p = args[pathIndex];
41051
41109
  if (value[p] === undefined) {
41052
- const nextPath = path[pathIndex + 1];
41110
+ const nextPath = args[pathIndex + 1];
41053
41111
  value[p] = createEmptyStructure(nextPath);
41054
41112
  }
41055
41113
  value = value[p];
@@ -41058,8 +41116,7 @@ class StateObserver {
41058
41116
  return;
41059
41117
  }
41060
41118
  this.changes.push({
41061
- root,
41062
- path,
41119
+ path: args,
41063
41120
  before: value[key],
41064
41121
  after: val,
41065
41122
  });
@@ -42793,7 +42850,10 @@ class Model extends EventBus {
42793
42850
  */
42794
42851
  checkDispatchAllowed(command) {
42795
42852
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
42796
- return new DispatchResult(results.flat());
42853
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
42854
+ return new DispatchResult(results.flat());
42855
+ }
42856
+ return DispatchResult.Success;
42797
42857
  }
42798
42858
  finalize() {
42799
42859
  this.status = 3 /* Status.Finalizing */;
@@ -43011,6 +43071,6 @@ exports.setTranslationMethod = setTranslationMethod;
43011
43071
  exports.tokenize = tokenize;
43012
43072
 
43013
43073
 
43014
- __info__.version = '16.0.24';
43015
- __info__.date = '2023-11-16T11:56:55.435Z';
43016
- __info__.hash = 'e0d7313';
43074
+ __info__.version = '16.0.27';
43075
+ __info__.date = '2024-01-04T12:19:11.487Z';
43076
+ __info__.hash = '54006f6';