@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.
@@ -620,13 +620,6 @@ function getYearFrac(startDate, endDate, _dayCountConvention) {
620
620
  }
621
621
 
622
622
  //------------------------------------------------------------------------------
623
- /**
624
- * Stringify an object, like JSON.stringify, except that the first level of keys
625
- * is ordered.
626
- */
627
- function stringify(obj) {
628
- return JSON.stringify(obj, Object.keys(obj).sort());
629
- }
630
623
  /**
631
624
  * Remove quotes from a quoted string
632
625
  * ```js
@@ -928,7 +921,7 @@ function isObjectEmptyRecursive(argument) {
928
921
  */
929
922
  function getItemId(item, itemsDic) {
930
923
  for (let [key, value] of Object.entries(itemsDic)) {
931
- if (stringify(value) === stringify(item)) {
924
+ if (deepEquals(value, item)) {
932
925
  return parseInt(key, 10);
933
926
  }
934
927
  }
@@ -1064,6 +1057,22 @@ function transpose2dArray(matrix) {
1064
1057
  if (!matrix.length)
1065
1058
  return matrix;
1066
1059
  return matrix[0].map((_, i) => matrix.map((row) => row[i]));
1060
+ }
1061
+ /**
1062
+ * Creates a version of the function that's memoized on the value of its first
1063
+ * argument, if any.
1064
+ */
1065
+ function memoize(func) {
1066
+ const cache = new Map();
1067
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1068
+ return {
1069
+ [funcName](...args) {
1070
+ if (!cache.has(args[0])) {
1071
+ cache.set(args[0], func(...args));
1072
+ }
1073
+ return cache.get(args[0]);
1074
+ },
1075
+ }[funcName];
1067
1076
  }
1068
1077
 
1069
1078
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -1343,8 +1352,10 @@ function isSameColor(color1, color2) {
1343
1352
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
1344
1353
  // Same as above, but matches the exact string (nothing before or after)
1345
1354
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
1346
- /** Reference of a column header (eg. A, AB) */
1347
- const colHeader = new RegExp(/^([A-Z]{1,3})+$/, "i");
1355
+ /** Reference of a column header (eg. A, AB, $A) */
1356
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
1357
+ /** Reference of a row header (eg. 1, $1) */
1358
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
1348
1359
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
1349
1360
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
1350
1361
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -1374,6 +1385,9 @@ function isRowReference(xc) {
1374
1385
  function isColHeader(str) {
1375
1386
  return colHeader.test(str);
1376
1387
  }
1388
+ function isRowHeader(str) {
1389
+ return rowHeader.test(str);
1390
+ }
1377
1391
  /**
1378
1392
  * Return true if the given xc is the reference of a single cell,
1379
1393
  * without any specified sheet (e.g. A1)
@@ -2074,18 +2088,10 @@ class RangeImpl {
2074
2088
  if (isFullCol) {
2075
2089
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2076
2090
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2077
- if (zone.left === zone.right) {
2078
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2079
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2080
- }
2081
2091
  }
2082
2092
  if (isFullRow) {
2083
2093
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
2084
2094
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
2085
- if (zone.top === zone.bottom) {
2086
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2087
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
2088
- }
2089
2095
  }
2090
2096
  return parts;
2091
2097
  }
@@ -3301,6 +3307,7 @@ var CommandResult;
3301
3307
  CommandResult[CommandResult["ChartDoesNotExist"] = 84] = "ChartDoesNotExist";
3302
3308
  CommandResult[CommandResult["InvalidHeaderIndex"] = 85] = "InvalidHeaderIndex";
3303
3309
  CommandResult[CommandResult["InvalidQuantity"] = 86] = "InvalidQuantity";
3310
+ CommandResult[CommandResult["NoChanges"] = 87] = "NoChanges";
3304
3311
  })(CommandResult || (CommandResult = {}));
3305
3312
 
3306
3313
  function isMatrix(x) {
@@ -3876,6 +3883,7 @@ function getMenuDescription(node) {
3876
3883
  return node.description ? node.description : "";
3877
3884
  }
3878
3885
 
3886
+ const macRegex = /Mac/i;
3879
3887
  /**
3880
3888
  * Return true if the event was triggered from
3881
3889
  * a child element.
@@ -3893,6 +3901,17 @@ function gridOverlayPosition() {
3893
3901
  }
3894
3902
  function getOpenedMenus() {
3895
3903
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
3904
+ }
3905
+ function isMacOS() {
3906
+ return Boolean(macRegex.test(navigator.userAgent));
3907
+ }
3908
+ /**
3909
+ * @param {KeyboardEvent | MouseEvent} ev
3910
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
3911
+ * On Mac, this is the "meta" or "command" key.
3912
+ */
3913
+ function isCtrlKey(ev) {
3914
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
3896
3915
  }
3897
3916
 
3898
3917
  /**
@@ -10976,12 +10995,12 @@ function toString(value) {
10976
10995
  }
10977
10996
  }
10978
10997
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
10979
- function normalizeString(str) {
10998
+ const normalizeString = memoize(function normalizeString(str) {
10980
10999
  return str
10981
11000
  .toLowerCase()
10982
11001
  .normalize("NFD")
10983
11002
  .replace(/[\u0300-\u036f]/g, "");
10984
- }
11003
+ });
10985
11004
  /**
10986
11005
  * Normalize a value.
10987
11006
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -18556,7 +18575,7 @@ const machine = {
18556
18575
  SPACE: goTo(State.RightRef),
18557
18576
  NUMBER: goTo(State.Found),
18558
18577
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
18559
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
18578
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
18560
18579
  },
18561
18580
  [State.RightColumnRef]: {
18562
18581
  SPACE: goTo(State.RightColumnRef),
@@ -18567,6 +18586,7 @@ const machine = {
18567
18586
  SPACE: goTo(State.RightRowRef),
18568
18587
  NUMBER: goTo(State.Found),
18569
18588
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
18589
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
18570
18590
  },
18571
18591
  [State.Found]: {},
18572
18592
  };
@@ -19076,6 +19096,10 @@ function setXcToReferenceType(xc, referenceType) {
19076
19096
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
19077
19097
  case "colrow":
19078
19098
  indexOfNumber = xc.search(/[0-9]/);
19099
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
19100
+ // no row number (eg. A) or no column (eg. 1)
19101
+ return "$" + xc;
19102
+ }
19079
19103
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
19080
19104
  return "$" + xc;
19081
19105
  case "none":
@@ -21457,7 +21481,10 @@ class GridOverlay extends Component {
21457
21481
  return;
21458
21482
  }
21459
21483
  const [col, row] = this.getCartesianCoordinates(ev);
21460
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
21484
+ this.props.onCellClicked(col, row, {
21485
+ expandZone: ev.shiftKey,
21486
+ addZone: isCtrlKey(ev),
21487
+ });
21461
21488
  }
21462
21489
  onDoubleClick(ev) {
21463
21490
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -21529,6 +21556,7 @@ class AbstractResizer extends Component {
21529
21556
  draggerShadowThickness: 0,
21530
21557
  delta: 0,
21531
21558
  base: 0,
21559
+ position: "before",
21532
21560
  });
21533
21561
  }
21534
21562
  _computeHandleDisplay(ev) {
@@ -21654,11 +21682,13 @@ class AbstractResizer extends Component {
21654
21682
  this.state.draggerLinePosition = dimensions.start;
21655
21683
  this.state.draggerShadowPosition = dimensions.start;
21656
21684
  this.state.base = elementIndex;
21685
+ this.state.position = "before";
21657
21686
  }
21658
21687
  else if (this._getSelectedZoneEnd() < elementIndex) {
21659
21688
  this.state.draggerLinePosition = dimensions.end;
21660
21689
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
21661
- this.state.base = elementIndex + 1;
21690
+ this.state.base = elementIndex;
21691
+ this.state.position = "after";
21662
21692
  }
21663
21693
  else {
21664
21694
  this.state.draggerLinePosition = startDimensions.start;
@@ -21682,7 +21712,7 @@ class AbstractResizer extends Component {
21682
21712
  this._increaseSelection(index);
21683
21713
  }
21684
21714
  else {
21685
- this._selectElement(index, ev.ctrlKey);
21715
+ this._selectElement(index, isCtrlKey(ev));
21686
21716
  }
21687
21717
  this.lastSelectedElementIndex = index;
21688
21718
  const mouseMoveSelect = (col, row) => {
@@ -21695,7 +21725,7 @@ class AbstractResizer extends Component {
21695
21725
  const mouseUpSelect = () => {
21696
21726
  this.state.isSelecting = false;
21697
21727
  this.lastSelectedElementIndex = null;
21698
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21728
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21699
21729
  this._computeGrabDisplay(ev);
21700
21730
  };
21701
21731
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -21836,13 +21866,14 @@ class ColResizer extends AbstractResizer {
21836
21866
  dimension: "COL",
21837
21867
  base: this.state.base,
21838
21868
  elements,
21869
+ position: this.state.position,
21839
21870
  });
21840
21871
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
21841
21872
  this.env.raiseError(MergeErrorMessage);
21842
21873
  }
21843
21874
  }
21844
- _selectElement(index, ctrlKey) {
21845
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
21875
+ _selectElement(index, addDistinctHeader) {
21876
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21846
21877
  }
21847
21878
  _increaseSelection(index) {
21848
21879
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -22005,13 +22036,14 @@ class RowResizer extends AbstractResizer {
22005
22036
  dimension: "ROW",
22006
22037
  base: this.state.base,
22007
22038
  elements,
22039
+ position: this.state.position,
22008
22040
  });
22009
22041
  if (!result.isSuccessful && result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
22010
22042
  this.env.raiseError(MergeErrorMessage);
22011
22043
  }
22012
22044
  }
22013
- _selectElement(index, ctrlKey) {
22014
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
22045
+ _selectElement(index, addDistinctHeader) {
22046
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
22015
22047
  }
22016
22048
  _increaseSelection(index) {
22017
22049
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -22107,8 +22139,8 @@ function useWheelHandler(handler) {
22107
22139
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
22108
22140
  }
22109
22141
  const onMouseWheel = (ev) => {
22110
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
22111
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
22142
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
22143
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
22112
22144
  handler(deltaX, deltaY);
22113
22145
  };
22114
22146
  return onMouseWheel;
@@ -22702,18 +22734,18 @@ class Grid extends Component {
22702
22734
  // ---------------------------------------------------------------------------
22703
22735
  // Zone selection with mouse
22704
22736
  // ---------------------------------------------------------------------------
22705
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
22706
- if (ctrlKey) {
22737
+ onCellClicked(col, row, { addZone, expandZone }) {
22738
+ if (addZone) {
22707
22739
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
22708
22740
  }
22709
22741
  this.closeOpenedPopover();
22710
22742
  if (this.env.model.getters.getEditionMode() === "editing") {
22711
22743
  this.env.model.dispatch("STOP_EDITION");
22712
22744
  }
22713
- if (shiftKey) {
22745
+ if (expandZone) {
22714
22746
  this.env.model.selection.setAnchorCorner(col, row);
22715
22747
  }
22716
- else if (ctrlKey) {
22748
+ else if (addZone) {
22717
22749
  this.env.model.selection.addCellToSelection(col, row);
22718
22750
  }
22719
22751
  else {
@@ -22767,10 +22799,10 @@ class Grid extends Component {
22767
22799
  };
22768
22800
  const direction = arrowMap[ev.key];
22769
22801
  if (ev.shiftKey) {
22770
- this.env.model.selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
22802
+ this.env.model.selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
22771
22803
  }
22772
22804
  else {
22773
- this.env.model.selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
22805
+ this.env.model.selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
22774
22806
  }
22775
22807
  if (this.env.model.getters.isPaintingFormat()) {
22776
22808
  this.env.model.dispatch("PASTE", {
@@ -22784,9 +22816,7 @@ class Grid extends Component {
22784
22816
  return;
22785
22817
  }
22786
22818
  let keyDownString = "";
22787
- if (ev.ctrlKey)
22788
- keyDownString += "CTRL+";
22789
- if (ev.metaKey)
22819
+ if (isCtrlKey(ev))
22790
22820
  keyDownString += "CTRL+";
22791
22821
  if (ev.altKey)
22792
22822
  keyDownString += "ALT+";
@@ -27101,6 +27131,14 @@ class BordersPlugin extends CorePlugin {
27101
27131
  // ---------------------------------------------------------------------------
27102
27132
  // Command Handling
27103
27133
  // ---------------------------------------------------------------------------
27134
+ allowDispatch(cmd) {
27135
+ switch (cmd.type) {
27136
+ case "SET_BORDER":
27137
+ return this.checkBordersUnchanged(cmd);
27138
+ default:
27139
+ return 0 /* CommandResult.Success */;
27140
+ }
27141
+ }
27104
27142
  handle(cmd) {
27105
27143
  switch (cmd.type) {
27106
27144
  case "ADD_MERGE":
@@ -27496,6 +27534,15 @@ class BordersPlugin extends CorePlugin {
27496
27534
  this.setBorders(sheetId, [{ ...zone, left: right }], "right");
27497
27535
  }
27498
27536
  }
27537
+ checkBordersUnchanged(cmd) {
27538
+ var _a, _b, _c, _d;
27539
+ const currentBorder = this.getCellBorder(cmd.sheetId, cmd.col, cmd.row);
27540
+ 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);
27541
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
27542
+ return 87 /* CommandResult.NoChanges */;
27543
+ }
27544
+ return 0 /* CommandResult.Success */;
27545
+ }
27499
27546
  // ---------------------------------------------------------------------------
27500
27547
  // Import/Export
27501
27548
  // ---------------------------------------------------------------------------
@@ -27531,7 +27578,7 @@ class BordersPlugin extends CorePlugin {
27531
27578
  */
27532
27579
  function getBorderId(border) {
27533
27580
  for (let [key, value] of Object.entries(borders)) {
27534
- if (stringify(value) === stringify(border)) {
27581
+ if (deepEquals(value, border)) {
27535
27582
  return parseInt(key, 10);
27536
27583
  }
27537
27584
  }
@@ -27574,6 +27621,7 @@ const nbspRegexp = new RegExp(String.fromCharCode(160), "g");
27574
27621
  class CellPlugin extends CorePlugin {
27575
27622
  constructor() {
27576
27623
  super(...arguments);
27624
+ this.nextId = 1;
27577
27625
  this.cells = {};
27578
27626
  this.createCell = cellFactory(this.getters);
27579
27627
  }
@@ -27599,8 +27647,9 @@ class CellPlugin extends CorePlugin {
27599
27647
  allowDispatch(cmd) {
27600
27648
  switch (cmd.type) {
27601
27649
  case "UPDATE_CELL":
27650
+ return this.checkCellOutOfSheet(cmd);
27602
27651
  case "CLEAR_CELL":
27603
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
27652
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
27604
27653
  default:
27605
27654
  return 0 /* CommandResult.Success */;
27606
27655
  }
@@ -27741,7 +27790,7 @@ class CellPlugin extends CorePlugin {
27741
27790
  importCell(sheetId, cellData, normalizedStyles, normalizedFormats) {
27742
27791
  const style = (cellData.style && normalizedStyles[cellData.style]) || undefined;
27743
27792
  const format = (cellData.format && normalizedFormats[cellData.format]) || undefined;
27744
- const cellId = this.uuidGenerator.uuidv4();
27793
+ const cellId = this.getNextUid();
27745
27794
  const properties = { format, style };
27746
27795
  return this.createCell(cellId, (cellData === null || cellData === void 0 ? void 0 : cellData.content) || "", properties, sheetId);
27747
27796
  }
@@ -27772,14 +27821,9 @@ class CellPlugin extends CorePlugin {
27772
27821
  */
27773
27822
  getCellById(cellId) {
27774
27823
  // this must be as fast as possible
27775
- for (const sheetId in this.cells) {
27776
- const sheet = this.cells[sheetId];
27777
- const cell = sheet[cellId];
27778
- if (cell) {
27779
- return cell;
27780
- }
27781
- }
27782
- return undefined;
27824
+ const position = this.getters.getCellPosition(cellId);
27825
+ const sheet = this.cells[position.sheetId];
27826
+ return sheet[cellId];
27783
27827
  }
27784
27828
  /*
27785
27829
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -27887,6 +27931,11 @@ class CellPlugin extends CorePlugin {
27887
27931
  }
27888
27932
  return format;
27889
27933
  }
27934
+ getNextUid() {
27935
+ const id = this.nextId.toString();
27936
+ this.history.update("nextId", this.nextId + 1);
27937
+ return id;
27938
+ }
27890
27939
  updateCell(sheetId, col, row, after) {
27891
27940
  var _a;
27892
27941
  const before = this.getters.getCell(sheetId, col, row);
@@ -27925,7 +27974,7 @@ class CellPlugin extends CorePlugin {
27925
27974
  }
27926
27975
  return;
27927
27976
  }
27928
- const cellId = (before === null || before === void 0 ? void 0 : before.id) || this.uuidGenerator.uuidv4();
27977
+ const cellId = (before === null || before === void 0 ? void 0 : before.id) || this.getNextUid();
27929
27978
  const didContentChange = hasContent;
27930
27979
  const properties = { format, style };
27931
27980
  const cell = this.createCell(cellId, afterContent, properties, sheetId);
@@ -27939,13 +27988,23 @@ class CellPlugin extends CorePlugin {
27939
27988
  this.history.update("cells", sheetId, cell.id, cell);
27940
27989
  this.dispatch("UPDATE_CELL_POSITION", { cellId: cell.id, col, row, sheetId });
27941
27990
  }
27942
- checkCellOutOfSheet(sheetId, col, row) {
27991
+ checkCellOutOfSheet(cmd) {
27992
+ const { sheetId, col, row } = cmd;
27943
27993
  const sheet = this.getters.tryGetSheet(sheetId);
27944
27994
  if (!sheet)
27945
27995
  return 27 /* CommandResult.InvalidSheetId */;
27946
27996
  const sheetZone = this.getters.getSheetZone(sheetId);
27947
27997
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
27948
27998
  }
27999
+ checkUselessClearCell(cmd) {
28000
+ const cell = this.getters.getCell(cmd.sheetId, cmd.col, cmd.row);
28001
+ if (!cell)
28002
+ return 87 /* CommandResult.NoChanges */;
28003
+ if (!cell.content && !cell.style && !cell.format) {
28004
+ return 87 /* CommandResult.NoChanges */;
28005
+ }
28006
+ return 0 /* CommandResult.Success */;
28007
+ }
27949
28008
  }
27950
28009
  CellPlugin.getters = [
27951
28010
  "zoneToXC",
@@ -29897,7 +29956,7 @@ class SheetPlugin extends CorePlugin {
29897
29956
  const elements = cmd.dimension === "COL"
29898
29957
  ? this.getNumberCols(cmd.sheetId)
29899
29958
  : this.getNumberRows(cmd.sheetId);
29900
- if (cmd.base < 0 || cmd.base > elements) {
29959
+ if (cmd.base < 0 || cmd.base >= elements) {
29901
29960
  return 85 /* CommandResult.InvalidHeaderIndex */;
29902
29961
  }
29903
29962
  else if (cmd.quantity <= 0) {
@@ -30510,10 +30569,13 @@ class SheetPlugin extends CorePlugin {
30510
30569
  // begin with the end.
30511
30570
  rows.sort((a, b) => b - a);
30512
30571
  for (let group of groupConsecutive(rows)) {
30572
+ // indexes are sorted in the descending order
30573
+ const from = group[group.length - 1];
30574
+ const to = group[0];
30513
30575
  // Move the cells.
30514
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
30515
- // Effectively delete the element and recompute the left-right/top-bottom.
30516
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
30576
+ this.moveCellOnRowsDeletion(sheet, from, to);
30577
+ // Effectively delete the rows
30578
+ this.updateRowsStructureOnDeletion(sheet, from, to);
30517
30579
  }
30518
30580
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
30519
30581
  if (count) {
@@ -30535,8 +30597,6 @@ class SheetPlugin extends CorePlugin {
30535
30597
  this.addEmptyRows(sheet, quantity);
30536
30598
  // Move the cells.
30537
30599
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
30538
- // Recompute the left-right/top-bottom.
30539
- this.updateRowsStructureOnAddition(sheet, row, quantity);
30540
30600
  if (index < sheet.panes.ySplit) {
30541
30601
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
30542
30602
  }
@@ -30637,35 +30697,20 @@ class SheetPlugin extends CorePlugin {
30637
30697
  }
30638
30698
  }
30639
30699
  }
30640
- updateRowsStructureOnDeletion(index, sheet) {
30700
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
30641
30701
  const rows = [];
30642
- const cellsQueue = sheet.rows.map((row) => row.cells);
30702
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
30643
30703
  for (let i in sheet.rows) {
30644
- if (Number(i) === index) {
30704
+ const row = Number(i);
30705
+ if (row >= deleteFromRow && row <= deleteToRow) {
30645
30706
  continue;
30646
30707
  }
30647
30708
  rows.push({
30648
- cells: cellsQueue.shift(),
30709
+ cells: cellsQueue.pop(),
30649
30710
  });
30650
30711
  }
30651
30712
  this.history.update("sheets", sheet.id, "rows", rows);
30652
30713
  }
30653
- /**
30654
- * Update the rows of the sheet after an addition:
30655
- * - Rename the rows
30656
- *
30657
- * @param sheet Sheet on which the deletion occurs
30658
- * @param addedRow Index of the added row
30659
- * @param rowsToAdd Number of the rows to add
30660
- */
30661
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
30662
- const rows = [];
30663
- const cellsQueue = sheet.rows.map((row) => row.cells);
30664
- sheet.rows.forEach(() => rows.push({
30665
- cells: cellsQueue.shift(),
30666
- }));
30667
- this.history.update("sheets", sheet.id, "rows", rows);
30668
- }
30669
30714
  /**
30670
30715
  * Add empty rows at the end of the rows
30671
30716
  *
@@ -32532,13 +32577,18 @@ class EvaluationPlugin extends UIPlugin {
32532
32577
  // Getters
32533
32578
  // ---------------------------------------------------------------------------
32534
32579
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
32535
- const compiledFormula = compile(formulaString);
32536
- const params = this.getCompilationParameters(() => { });
32537
- const ranges = [];
32538
- for (let xc of compiledFormula.dependencies) {
32539
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32580
+ try {
32581
+ const compiledFormula = compile(formulaString);
32582
+ const params = this.getCompilationParameters(() => { });
32583
+ const ranges = [];
32584
+ for (let xc of compiledFormula.dependencies) {
32585
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32586
+ }
32587
+ return compiledFormula.execute(ranges, ...params).value;
32588
+ }
32589
+ catch (error) {
32590
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
32540
32591
  }
32541
- return compiledFormula.execute(ranges, ...params).value;
32542
32592
  }
32543
32593
  /**
32544
32594
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32654,6 +32704,7 @@ class EvaluationPlugin extends UIPlugin {
32654
32704
  }
32655
32705
  return cell.evaluated;
32656
32706
  }
32707
+ const rangeCache = {};
32657
32708
  /**
32658
32709
  * Return the values of the cell(s) used in reference, but always in the format of a range even
32659
32710
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -32670,21 +32721,26 @@ class EvaluationPlugin extends UIPlugin {
32670
32721
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
32671
32722
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
32672
32723
  const sheetZone = getters.getSheetZone(sheetId);
32673
- const result = [];
32674
32724
  const zone = intersection(range.zone, sheetZone);
32675
32725
  if (!zone) {
32676
- result.push([]);
32677
- return result;
32726
+ return [[]];
32727
+ }
32728
+ const { top, left, bottom, right } = zone;
32729
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
32730
+ if (cacheKey in rangeCache) {
32731
+ return rangeCache[cacheKey];
32678
32732
  }
32733
+ const result = new Array(right - left + 1);
32679
32734
  // Performance issue: nested loop is faster than a map here
32680
- for (let col = zone.left; col <= zone.right; col++) {
32681
- const rowValues = [];
32682
- for (let row = zone.top; row <= zone.bottom; row++) {
32735
+ for (let col = left; col <= right; col++) {
32736
+ const rowValues = new Array(bottom - top + 1);
32737
+ for (let row = top; row <= bottom; row++) {
32683
32738
  const cell = evalContext.getters.getCell(range.sheetId, col, row);
32684
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
32739
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
32685
32740
  }
32686
- result.push(rowValues);
32741
+ result[col - left] = rowValues;
32687
32742
  }
32743
+ rangeCache[cacheKey] = result;
32688
32744
  return result;
32689
32745
  }
32690
32746
  /**
@@ -32934,39 +32990,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32934
32990
  this.computedIcons[activeSheetId] = {};
32935
32991
  const computedStyle = this.computedStyles[activeSheetId];
32936
32992
  for (let cf of this.getters.getConditionalFormats(activeSheetId).reverse()) {
32937
- try {
32938
- switch (cf.rule.type) {
32939
- case "ColorScaleRule":
32940
- for (let range of cf.ranges) {
32941
- this.applyColorScale(range, cf.rule);
32942
- }
32943
- break;
32944
- case "IconSetRule":
32945
- for (let range of cf.ranges) {
32946
- this.applyIcon(range, cf.rule);
32947
- }
32948
- break;
32949
- default:
32950
- for (let ref of cf.ranges) {
32951
- const zone = this.getters.getRangeFromSheetXC(activeSheetId, ref).zone;
32952
- for (let row = zone.top; row <= zone.bottom; row++) {
32953
- for (let col = zone.left; col <= zone.right; col++) {
32954
- const pr = this.rulePredicate[cf.rule.type];
32955
- let cell = this.getters.getCell(activeSheetId, col, row);
32956
- if (pr && pr(cell, cf.rule)) {
32957
- if (!computedStyle[col])
32958
- computedStyle[col] = [];
32959
- // we must combine all the properties of all the CF rules applied to the given cell
32960
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32961
- }
32993
+ switch (cf.rule.type) {
32994
+ case "ColorScaleRule":
32995
+ for (let range of cf.ranges) {
32996
+ this.applyColorScale(range, cf.rule);
32997
+ }
32998
+ break;
32999
+ case "IconSetRule":
33000
+ for (let range of cf.ranges) {
33001
+ this.applyIcon(range, cf.rule);
33002
+ }
33003
+ break;
33004
+ default:
33005
+ for (let ref of cf.ranges) {
33006
+ const zone = this.getters.getRangeFromSheetXC(activeSheetId, ref).zone;
33007
+ for (let row = zone.top; row <= zone.bottom; row++) {
33008
+ for (let col = zone.left; col <= zone.right; col++) {
33009
+ const pr = this.rulePredicate[cf.rule.type];
33010
+ let cell = this.getters.getCell(activeSheetId, col, row);
33011
+ if (pr && pr(cell, cf.rule)) {
33012
+ if (!computedStyle[col])
33013
+ computedStyle[col] = [];
33014
+ // we must combine all the properties of all the CF rules applied to the given cell
33015
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32962
33016
  }
32963
33017
  }
32964
33018
  }
32965
- break;
32966
- }
32967
- }
32968
- catch (_) {
32969
- // we don't care about the errors within the evaluation of a rule
33019
+ }
33020
+ break;
32970
33021
  }
32971
33022
  }
32972
33023
  }
@@ -32990,7 +33041,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32990
33041
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32991
33042
  case "formula":
32992
33043
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32993
- return !(value instanceof Promise) ? value : null;
33044
+ return typeof value === "number" ? value : null;
32994
33045
  default:
32995
33046
  return null;
32996
33047
  }
@@ -34938,7 +34989,7 @@ class GridSelectionPlugin extends UIPlugin {
34938
34989
  sheetId: cmd.sheetId,
34939
34990
  base: cmd.base,
34940
34991
  quantity: thickness,
34941
- position: "before",
34992
+ position: cmd.position,
34942
34993
  });
34943
34994
  const isCol = cmd.dimension === "COL";
34944
34995
  const start = cmd.elements[0];
@@ -34955,12 +35006,13 @@ class GridSelectionPlugin extends UIPlugin {
34955
35006
  },
34956
35007
  ];
34957
35008
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
35009
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
34958
35010
  const pasteTarget = [
34959
35011
  {
34960
- left: isCol ? cmd.base : 0,
34961
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
34962
- top: !isCol ? cmd.base : 0,
34963
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
35012
+ left: isCol ? base : 0,
35013
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
35014
+ top: !isCol ? base : 0,
35015
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
34964
35016
  },
34965
35017
  ];
34966
35018
  state.paste(pasteTarget, { selectTarget: true });
@@ -34997,6 +35049,11 @@ class GridSelectionPlugin extends UIPlugin {
34997
35049
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
34998
35050
  return 2 /* CommandResult.WillRemoveExistingMerge */;
34999
35051
  }
35052
+ const headers = [cmd.base, ...cmd.elements];
35053
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
35054
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
35055
+ return 85 /* CommandResult.InvalidHeaderIndex */;
35056
+ }
35000
35057
  return 0 /* CommandResult.Success */;
35001
35058
  }
35002
35059
  //-------------------------------------------
@@ -39063,7 +39120,7 @@ class Spreadsheet extends Component {
39063
39120
  }
39064
39121
  onKeydown(ev) {
39065
39122
  let keyDownString = "";
39066
- if (ev.ctrlKey || ev.metaKey) {
39123
+ if (isCtrlKey(ev)) {
39067
39124
  keyDownString += "CTRL+";
39068
39125
  }
39069
39126
  keyDownString += ev.key.toUpperCase();
@@ -39907,9 +39964,9 @@ function revertChanges(revisions) {
39907
39964
  * Apply the changes of the given HistoryChange to the state
39908
39965
  */
39909
39966
  function applyChange(change, target) {
39910
- let val = change.root;
39911
- let key = change.path[change.path.length - 1];
39912
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
39967
+ let val = change.path[0];
39968
+ const key = change.path[change.path.length - 1];
39969
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
39913
39970
  const p = change.path[pathIndex];
39914
39971
  if (val[p] === undefined) {
39915
39972
  const nextPath = change.path[pathIndex + 1];
@@ -41039,13 +41096,14 @@ class StateObserver {
41039
41096
  }
41040
41097
  addChange(...args) {
41041
41098
  const val = args.pop();
41042
- const [root, ...path] = args;
41099
+ const root = args[0];
41043
41100
  let value = root;
41044
- let key = path[path.length - 1];
41045
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
41046
- const p = path[pathIndex];
41101
+ let key = args[args.length - 1];
41102
+ const pathLength = args.length - 2;
41103
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
41104
+ const p = args[pathIndex];
41047
41105
  if (value[p] === undefined) {
41048
- const nextPath = path[pathIndex + 1];
41106
+ const nextPath = args[pathIndex + 1];
41049
41107
  value[p] = createEmptyStructure(nextPath);
41050
41108
  }
41051
41109
  value = value[p];
@@ -41054,8 +41112,7 @@ class StateObserver {
41054
41112
  return;
41055
41113
  }
41056
41114
  this.changes.push({
41057
- root,
41058
- path,
41115
+ path: args,
41059
41116
  before: value[key],
41060
41117
  after: val,
41061
41118
  });
@@ -42789,7 +42846,10 @@ class Model extends EventBus {
42789
42846
  */
42790
42847
  checkDispatchAllowed(command) {
42791
42848
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
42792
- return new DispatchResult(results.flat());
42849
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
42850
+ return new DispatchResult(results.flat());
42851
+ }
42852
+ return DispatchResult.Success;
42793
42853
  }
42794
42854
  finalize() {
42795
42855
  this.status = 3 /* Status.Finalizing */;
@@ -42979,6 +43039,6 @@ function addFunction(functionName, functionDescription) {
42979
43039
  export { AbstractChart, CommandResult, CorePlugin, DATETIME_FORMAT, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, cellTypes, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, load, parse, readonlyAllowedCommands, registries, setTranslationMethod, tokenize };
42980
43040
 
42981
43041
 
42982
- __info__.version = '16.0.24';
42983
- __info__.date = '2023-11-16T11:56:55.435Z';
42984
- __info__.hash = 'e0d7313';
43042
+ __info__.version = '16.0.27';
43043
+ __info__.date = '2024-01-04T12:19:11.487Z';
43044
+ __info__.hash = '54006f6';