@odoo/o-spreadsheet 17.0.3 → 17.0.5

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 17.0.3
6
- * @date 2023-11-16T11:59:49.524Z
7
- * @hash 8bd0fb2
5
+ * @version 17.0.5
6
+ * @date 2023-12-05T09:51:44.657Z
7
+ * @hash 2480a5d
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -692,8 +692,7 @@
692
692
  *
693
693
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
694
694
  */
695
- const whiteSpaceCharacters = [
696
- " ",
695
+ const whiteSpaceSpecialCharacters = [
697
696
  "\t",
698
697
  "\f",
699
698
  "\v",
@@ -708,8 +707,7 @@
708
707
  String.fromCharCode(parseInt("3000", 16)),
709
708
  String.fromCharCode(parseInt("feff", 16)),
710
709
  ];
711
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
712
- const newLineRegex = /\r\n|\r|\n/g;
710
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
713
711
  /**
714
712
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
715
713
  * different newlines types by \n.
@@ -717,9 +715,9 @@
717
715
  function replaceSpecialSpaces(text) {
718
716
  if (!text)
719
717
  return "";
720
- text = text.replace(whiteSpaceRegexp, " ");
721
- text = text.replace(newLineRegex, NEWLINE);
722
- return text;
718
+ if (!whiteSpaceRegexp.test(text))
719
+ return text;
720
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
723
721
  }
724
722
  /**
725
723
  * Determine if the numbers are consecutive.
@@ -2061,6 +2059,7 @@
2061
2059
  CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
2062
2060
  CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
2063
2061
  CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
2062
+ CommandResult["NoChanges"] = "NoChanges";
2064
2063
  })(exports.CommandResult || (exports.CommandResult = {}));
2065
2064
 
2066
2065
  const DEFAULT_LOCALES = [
@@ -2190,12 +2189,12 @@
2190
2189
  }
2191
2190
  }
2192
2191
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2193
- function normalizeString(str) {
2192
+ const normalizeString = memoize(function normalizeString(str) {
2194
2193
  return str
2195
2194
  .toLowerCase()
2196
2195
  .normalize("NFD")
2197
2196
  .replace(/[\u0300-\u036f]/g, "");
2198
- }
2197
+ });
2199
2198
  const expectBooleanValueError = (value) => _t("The function [[FUNCTION_NAME]] expects a boolean value, but '%s' is a text, and cannot be coerced to a number.", value);
2200
2199
  function toBoolean(value) {
2201
2200
  switch (typeof value) {
@@ -3389,8 +3388,10 @@
3389
3388
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
3390
3389
  // Same as above, but matches the exact string (nothing before or after)
3391
3390
  const singleCellReference = new RegExp(/^\$?([A-Z]{1,3})\$?([0-9]{1,7})$/, "i");
3392
- /** Reference of a column header (eg. A, AB) */
3393
- const colHeader = new RegExp(/^([A-Z]{1,3})+$/, "i");
3391
+ /** Reference of a column header (eg. A, AB, $A) */
3392
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
3393
+ /** Reference of a row header (eg. 1, $1) */
3394
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
3394
3395
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
3395
3396
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
3396
3397
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -3420,6 +3421,9 @@
3420
3421
  function isColHeader(str) {
3421
3422
  return colHeader.test(str);
3422
3423
  }
3424
+ function isRowHeader(str) {
3425
+ return rowHeader.test(str);
3426
+ }
3423
3427
  /**
3424
3428
  * Return true if the given xc is the reference of a single cell,
3425
3429
  * without any specified sheet (e.g. A1)
@@ -4103,18 +4107,10 @@
4103
4107
  if (isFullCol) {
4104
4108
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
4105
4109
  parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
4106
- if (zone.left === zone.right) {
4107
- parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
4108
- parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
4109
- }
4110
4110
  }
4111
4111
  if (isFullRow) {
4112
4112
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
4113
4113
  parts[1].colFixed = parts[0].colFixed || parts[1].colFixed;
4114
- if (zone.top === zone.bottom) {
4115
- parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
4116
- parts[1].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
4117
- }
4118
4114
  }
4119
4115
  return parts;
4120
4116
  }
@@ -4356,7 +4352,7 @@
4356
4352
  function getDefaultCellHeight(ctx, cell, colSize) {
4357
4353
  if (!cell || !cell.content)
4358
4354
  return DEFAULT_CELL_HEIGHT;
4359
- const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4355
+ const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4360
4356
  const numberOfLines = cell.isFormula
4361
4357
  ? 1
4362
4358
  : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
@@ -6251,10 +6247,6 @@
6251
6247
 
6252
6248
  .o-error-tooltip-message {
6253
6249
  overflow: hidden;
6254
- display: -webkit-box; /* Limit to 3 lines */
6255
- -webkit-line-clamp: 3;
6256
- line-clamp: 3;
6257
- -webkit-box-orient: vertical;
6258
6250
  }
6259
6251
  }
6260
6252
  `;
@@ -6637,6 +6629,9 @@
6637
6629
  function getOpenedMenus() {
6638
6630
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
6639
6631
  }
6632
+ function isMacOS() {
6633
+ return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
6634
+ }
6640
6635
 
6641
6636
  /**
6642
6637
  * Return the o-spreadsheet element position relative
@@ -7330,7 +7325,7 @@
7330
7325
  return result;
7331
7326
  }
7332
7327
  function tokenizeDebugger(chars) {
7333
- if (chars.current() === "?") {
7328
+ if (chars.current === "?") {
7334
7329
  chars.shift();
7335
7330
  return { type: "DEBUGGER", value: "?" };
7336
7331
  }
@@ -7341,7 +7336,7 @@
7341
7336
  ")": "RIGHT_PAREN",
7342
7337
  };
7343
7338
  function tokenizeMisc(chars) {
7344
- if (chars.current() in misc$1) {
7339
+ if (chars.current in misc$1) {
7345
7340
  const value = chars.shift();
7346
7341
  const type = misc$1[value];
7347
7342
  return { type, value };
@@ -7349,7 +7344,7 @@
7349
7344
  return null;
7350
7345
  }
7351
7346
  function tokenizeArgsSeparator(chars, locale) {
7352
- if (chars.current() === locale.formulaArgSeparator) {
7347
+ if (chars.current === locale.formulaArgSeparator) {
7353
7348
  const value = chars.shift();
7354
7349
  const type = "ARG_SEPARATOR";
7355
7350
  return { type, value };
@@ -7374,14 +7369,13 @@
7374
7369
  return null;
7375
7370
  }
7376
7371
  function tokenizeString(chars) {
7377
- if (chars.current() === '"') {
7372
+ if (chars.current === '"') {
7378
7373
  const startChar = chars.shift();
7379
7374
  let letters = startChar;
7380
- while (chars.current() &&
7381
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
7375
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
7382
7376
  letters += chars.shift();
7383
7377
  }
7384
- if (chars.current() === '"') {
7378
+ if (chars.current === '"') {
7385
7379
  letters += chars.shift();
7386
7380
  }
7387
7381
  return {
@@ -7408,14 +7402,14 @@
7408
7402
  let result = "";
7409
7403
  // there are two main cases to manage: either something which starts with
7410
7404
  // a ', like 'Sheet 2'A2, or a word-like element.
7411
- if (chars.current() === "'") {
7405
+ if (chars.current === "'") {
7412
7406
  let lastChar = chars.shift();
7413
7407
  result += lastChar;
7414
- while (chars.current()) {
7408
+ while (chars.current) {
7415
7409
  lastChar = chars.shift();
7416
7410
  result += lastChar;
7417
7411
  if (lastChar === "'") {
7418
- if (chars.current() && chars.current() === "'") {
7412
+ if (chars.current && chars.current === "'") {
7419
7413
  lastChar = chars.shift();
7420
7414
  result += lastChar;
7421
7415
  }
@@ -7431,7 +7425,7 @@
7431
7425
  };
7432
7426
  }
7433
7427
  }
7434
- while (chars.current() && separatorRegexp.test(chars.current())) {
7428
+ while (chars.current && separatorRegexp.test(chars.current)) {
7435
7429
  result += chars.shift();
7436
7430
  }
7437
7431
  if (result.length) {
@@ -7446,14 +7440,14 @@
7446
7440
  }
7447
7441
  function tokenizeSpace(chars) {
7448
7442
  let length = 0;
7449
- while (chars.current() === NEWLINE) {
7443
+ while (chars.current === NEWLINE) {
7450
7444
  length++;
7451
7445
  chars.shift();
7452
7446
  }
7453
7447
  if (length) {
7454
7448
  return { type: "SPACE", value: NEWLINE.repeat(length) };
7455
7449
  }
7456
- while (chars.current() === " ") {
7450
+ while (chars.current === " ") {
7457
7451
  length++;
7458
7452
  chars.shift();
7459
7453
  }
@@ -7472,17 +7466,20 @@
7472
7466
  class TokenizingChars {
7473
7467
  text;
7474
7468
  currentIndex = 0;
7469
+ current;
7475
7470
  constructor(text) {
7476
7471
  this.text = text;
7477
- }
7478
- current() {
7479
- return this.text[this.currentIndex];
7472
+ this.current = text[0];
7480
7473
  }
7481
7474
  shift() {
7482
- return this.text[this.currentIndex++];
7475
+ const current = this.current;
7476
+ const next = this.text[++this.currentIndex];
7477
+ this.current = next;
7478
+ return current;
7483
7479
  }
7484
7480
  advanceBy(length) {
7485
7481
  this.currentIndex += length;
7482
+ this.current = this.text[this.currentIndex];
7486
7483
  }
7487
7484
  isOver() {
7488
7485
  return this.currentIndex >= this.text.length;
@@ -28279,6 +28276,7 @@
28279
28276
  draggerShadowThickness: 0,
28280
28277
  delta: 0,
28281
28278
  base: 0,
28279
+ position: "before",
28282
28280
  });
28283
28281
  _computeHandleDisplay(ev) {
28284
28282
  const position = this._getEvOffset(ev);
@@ -28403,11 +28401,13 @@
28403
28401
  this.state.draggerLinePosition = dimensions.start;
28404
28402
  this.state.draggerShadowPosition = dimensions.start;
28405
28403
  this.state.base = elementIndex;
28404
+ this.state.position = "before";
28406
28405
  }
28407
28406
  else if (this._getSelectedZoneEnd() < elementIndex) {
28408
28407
  this.state.draggerLinePosition = dimensions.end;
28409
28408
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
28410
- this.state.base = elementIndex + 1;
28409
+ this.state.base = elementIndex;
28410
+ this.state.position = "after";
28411
28411
  }
28412
28412
  else {
28413
28413
  this.state.draggerLinePosition = startDimensions.start;
@@ -28589,6 +28589,7 @@
28589
28589
  dimension: "COL",
28590
28590
  base: this.state.base,
28591
28591
  elements,
28592
+ position: this.state.position,
28592
28593
  });
28593
28594
  if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
28594
28595
  this.env.raiseError(MergeErrorMessage);
@@ -28762,6 +28763,7 @@
28762
28763
  dimension: "ROW",
28763
28764
  base: this.state.base,
28764
28765
  elements,
28766
+ position: this.state.position,
28765
28767
  });
28766
28768
  if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
28767
28769
  this.env.raiseError(MergeErrorMessage);
@@ -28869,8 +28871,8 @@
28869
28871
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
28870
28872
  }
28871
28873
  const onMouseWheel = (ev) => {
28872
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
28873
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
28874
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
28875
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
28874
28876
  handler(deltaX, deltaY);
28875
28877
  };
28876
28878
  return onMouseWheel;
@@ -29758,6 +29760,7 @@
29758
29760
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29759
29761
  const target = this.env.model.getters.getSelectedZones();
29760
29762
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29763
+ const isCutOperation = this.env.model.getters.isCutOperation();
29761
29764
  if (clipboardString === content) {
29762
29765
  // the paste actually comes from o-spreadsheet itself
29763
29766
  interactivePaste(this.env, target);
@@ -29765,7 +29768,7 @@
29765
29768
  else {
29766
29769
  interactivePasteFromOS(this.env, target, content);
29767
29770
  }
29768
- if (this.env.model.getters.isCutOperation()) {
29771
+ if (isCutOperation) {
29769
29772
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29770
29773
  }
29771
29774
  }
@@ -33323,7 +33326,7 @@
33323
33326
  SPACE: goTo(State.RightRef),
33324
33327
  NUMBER: goTo(State.Found),
33325
33328
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
33326
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
33329
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
33327
33330
  },
33328
33331
  [State.RightColumnRef]: {
33329
33332
  SPACE: goTo(State.RightColumnRef),
@@ -33334,6 +33337,7 @@
33334
33337
  SPACE: goTo(State.RightRowRef),
33335
33338
  NUMBER: goTo(State.Found),
33336
33339
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
33340
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
33337
33341
  },
33338
33342
  [State.Found]: {},
33339
33343
  };
@@ -34059,6 +34063,14 @@
34059
34063
  // ---------------------------------------------------------------------------
34060
34064
  // Command Handling
34061
34065
  // ---------------------------------------------------------------------------
34066
+ allowDispatch(cmd) {
34067
+ switch (cmd.type) {
34068
+ case "SET_BORDER":
34069
+ return this.checkBordersUnchanged(cmd);
34070
+ default:
34071
+ return "Success" /* CommandResult.Success */;
34072
+ }
34073
+ }
34062
34074
  handle(cmd) {
34063
34075
  switch (cmd.type) {
34064
34076
  case "ADD_MERGE":
@@ -34478,6 +34490,14 @@
34478
34490
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34479
34491
  }
34480
34492
  }
34493
+ checkBordersUnchanged(cmd) {
34494
+ const currentBorder = this.getCellBorder(cmd);
34495
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
34496
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
34497
+ return "NoChanges" /* CommandResult.NoChanges */;
34498
+ }
34499
+ return "Success" /* CommandResult.Success */;
34500
+ }
34481
34501
  // ---------------------------------------------------------------------------
34482
34502
  // Import/Export
34483
34503
  // ---------------------------------------------------------------------------
@@ -35333,8 +35353,9 @@
35333
35353
  allowDispatch(cmd) {
35334
35354
  switch (cmd.type) {
35335
35355
  case "UPDATE_CELL":
35356
+ return this.checkCellOutOfSheet(cmd);
35336
35357
  case "CLEAR_CELL":
35337
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35358
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35338
35359
  default:
35339
35360
  return "Success" /* CommandResult.Success */;
35340
35361
  }
@@ -35503,14 +35524,9 @@
35503
35524
  */
35504
35525
  getCellById(cellId) {
35505
35526
  // this must be as fast as possible
35506
- for (const sheetId in this.cells) {
35507
- const sheet = this.cells[sheetId];
35508
- const cell = sheet[cellId];
35509
- if (cell) {
35510
- return cell;
35511
- }
35512
- }
35513
- return undefined;
35527
+ const position = this.getters.getCellPosition(cellId);
35528
+ const sheet = this.cells[position.sheetId];
35529
+ return sheet[cellId];
35514
35530
  }
35515
35531
  /*
35516
35532
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -35741,13 +35757,23 @@
35741
35757
  },
35742
35758
  };
35743
35759
  }
35744
- checkCellOutOfSheet(sheetId, col, row) {
35760
+ checkCellOutOfSheet(cmd) {
35761
+ const { sheetId, col, row } = cmd;
35745
35762
  const sheet = this.getters.tryGetSheet(sheetId);
35746
35763
  if (!sheet)
35747
35764
  return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
35748
35765
  const sheetZone = this.getters.getSheetZone(sheetId);
35749
35766
  return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */;
35750
35767
  }
35768
+ checkUselessClearCell(cmd) {
35769
+ const cell = this.getters.getCell(cmd);
35770
+ if (!cell)
35771
+ return "NoChanges" /* CommandResult.NoChanges */;
35772
+ if (!cell.content && !cell.style && !cell.format) {
35773
+ return "NoChanges" /* CommandResult.NoChanges */;
35774
+ }
35775
+ return "Success" /* CommandResult.Success */;
35776
+ }
35751
35777
  }
35752
35778
  class FormulaCellWithDependencies {
35753
35779
  id;
@@ -36437,6 +36463,9 @@
36437
36463
  return this.rules[sheetId].find((rule) => rule.id === id);
36438
36464
  }
36439
36465
  getValidationRuleForCell({ sheetId, col, row }) {
36466
+ if (!this.rules[sheetId]) {
36467
+ return undefined;
36468
+ }
36440
36469
  for (const rule of this.rules[sheetId]) {
36441
36470
  for (const range of rule.ranges) {
36442
36471
  if (isInside(col, row, range.zone)) {
@@ -37290,7 +37319,16 @@
37290
37319
  return consecutiveIndexes;
37291
37320
  }
37292
37321
  getAllVisibleHeaders(sheetId, dimension) {
37293
- return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
37322
+ const headers = range(0, this.getters.getNumberHeaders(sheetId, dimension));
37323
+ const foldedHeaders = [];
37324
+ this.getters.getHeaderGroups(sheetId, dimension).forEach((group) => {
37325
+ if (group.isFolded) {
37326
+ foldedHeaders.push(...range(group.start, group.end + 1));
37327
+ }
37328
+ });
37329
+ return headers.filter((i) => {
37330
+ return !this.hiddenHeaders[sheetId][dimension][i] && !foldedHeaders.includes(i);
37331
+ });
37294
37332
  }
37295
37333
  import(data) {
37296
37334
  for (let sheet of data.sheets) {
@@ -39034,10 +39072,13 @@
39034
39072
  // begin with the end.
39035
39073
  rows.sort((a, b) => b - a);
39036
39074
  for (let group of groupConsecutive(rows)) {
39075
+ // indexes are sorted in the descending order
39076
+ const from = group[group.length - 1];
39077
+ const to = group[0];
39037
39078
  // Move the cells.
39038
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
39039
- // Effectively delete the element and recompute the left-right/top-bottom.
39040
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
39079
+ this.moveCellOnRowsDeletion(sheet, from, to);
39080
+ // Effectively delete the rows
39081
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39041
39082
  }
39042
39083
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39043
39084
  if (count) {
@@ -39059,8 +39100,6 @@
39059
39100
  this.addEmptyRows(sheet, quantity);
39060
39101
  // Move the cells.
39061
39102
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39062
- // Recompute the left-right/top-bottom.
39063
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39064
39103
  if (index < sheet.panes.ySplit) {
39065
39104
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39066
39105
  }
@@ -39161,35 +39200,20 @@
39161
39200
  }
39162
39201
  }
39163
39202
  }
39164
- updateRowsStructureOnDeletion(index, sheet) {
39203
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39165
39204
  const rows = [];
39166
- const cellsQueue = sheet.rows.map((row) => row.cells);
39205
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39167
39206
  for (let i in sheet.rows) {
39168
- if (Number(i) === index) {
39207
+ const row = Number(i);
39208
+ if (row >= deleteFromRow && row <= deleteToRow) {
39169
39209
  continue;
39170
39210
  }
39171
39211
  rows.push({
39172
- cells: cellsQueue.shift(),
39212
+ cells: cellsQueue.pop(),
39173
39213
  });
39174
39214
  }
39175
39215
  this.history.update("sheets", sheet.id, "rows", rows);
39176
39216
  }
39177
- /**
39178
- * Update the rows of the sheet after an addition:
39179
- * - Rename the rows
39180
- *
39181
- * @param sheet Sheet on which the deletion occurs
39182
- * @param addedRow Index of the added row
39183
- * @param rowsToAdd Number of the rows to add
39184
- */
39185
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
39186
- const rows = [];
39187
- const cellsQueue = sheet.rows.map((row) => row.cells);
39188
- sheet.rows.forEach(() => rows.push({
39189
- cells: cellsQueue.shift(),
39190
- }));
39191
- this.history.update("sheets", sheet.id, "rows", rows);
39192
- }
39193
39217
  /**
39194
39218
  * Add empty rows at the end of the rows
39195
39219
  *
@@ -39716,6 +39740,7 @@
39716
39740
  getters;
39717
39741
  computeCell;
39718
39742
  evalContext;
39743
+ rangeCache = {};
39719
39744
  constructor(context, getters, computeCell) {
39720
39745
  this.getters = getters;
39721
39746
  this.computeCell = computeCell;
@@ -39763,10 +39788,13 @@
39763
39788
  if (evaluatedCell === undefined) {
39764
39789
  return { value: null, format: this.getters.getCell(position)?.format };
39765
39790
  }
39791
+ if (evaluatedCell.type === CellValueType.error) {
39792
+ throw evaluatedCell.error;
39793
+ }
39766
39794
  return evaluatedCell;
39767
39795
  }
39768
39796
  getEvaluatedCellIfNotEmpty(position) {
39769
- const evaluatedCell = this.getEvaluatedCell(position);
39797
+ const evaluatedCell = this.computeCell(position);
39770
39798
  if (evaluatedCell.type === CellValueType.empty) {
39771
39799
  const cell = this.getters.getCell(position);
39772
39800
  if (!cell || (!cell.isFormula && cell.content === "")) {
@@ -39775,13 +39803,6 @@
39775
39803
  }
39776
39804
  return evaluatedCell;
39777
39805
  }
39778
- getEvaluatedCell(position) {
39779
- const evaluatedCell = this.computeCell(position);
39780
- if (evaluatedCell.type === CellValueType.error) {
39781
- throw evaluatedCell.error;
39782
- }
39783
- return evaluatedCell;
39784
- }
39785
39806
  /**
39786
39807
  * Return the values of the cell(s) used in reference, but always in the format of a range even
39787
39808
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -39801,17 +39822,33 @@
39801
39822
  if (!_zone) {
39802
39823
  return [[]];
39803
39824
  }
39825
+ const { top, left, bottom, right } = zone;
39826
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
39827
+ if (cacheKey in this.rangeCache) {
39828
+ const result = this.rangeCache[cacheKey];
39829
+ if (result instanceof EvaluationError) {
39830
+ throw result;
39831
+ }
39832
+ return result;
39833
+ }
39804
39834
  const height = _zone.bottom - _zone.top + 1;
39805
39835
  const width = _zone.right - _zone.left + 1;
39806
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
39836
+ const matrix = new Array(width);
39807
39837
  // Performance issue: nested loop is faster than a map here
39808
39838
  for (let col = _zone.left; col <= _zone.right; col++) {
39839
+ const colIndex = col - _zone.left;
39840
+ matrix[colIndex] = new Array(height);
39809
39841
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39810
- const colIndex = col - _zone.left;
39842
+ const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
39843
+ if (evaluatedCell?.type === CellValueType.error) {
39844
+ this.rangeCache[cacheKey] = evaluatedCell.error;
39845
+ throw evaluatedCell.error;
39846
+ }
39811
39847
  const rowIndex = row - _zone.top;
39812
39848
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39813
39849
  }
39814
39850
  }
39851
+ this.rangeCache[cacheKey] = matrix;
39815
39852
  return matrix;
39816
39853
  }
39817
39854
  }
@@ -40017,6 +40054,7 @@
40017
40054
  this.formulaDependencies().addDependencies(positionId, dependencies);
40018
40055
  }
40019
40056
  updateCompilationParameters() {
40057
+ // rebuild the compilation parameters (with a clean cache)
40020
40058
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40021
40059
  }
40022
40060
  evaluateCells(positions) {
@@ -40060,6 +40098,16 @@
40060
40098
  this.evaluatedCells = new Map();
40061
40099
  this.evaluate(this.getAllCells());
40062
40100
  }
40101
+ evaluateFormula(sheetId, formulaString) {
40102
+ const compiledFormula = compile(formulaString);
40103
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
40104
+ this.updateCompilationParameters();
40105
+ const array = compiledFormula.execute(ranges, ...this.compilationParams);
40106
+ if (isMatrix(array)) {
40107
+ return matrixMap(array, (cell) => cell.value);
40108
+ }
40109
+ return array.value;
40110
+ }
40063
40111
  getAllCells() {
40064
40112
  const positionIds = new JetSet();
40065
40113
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40086,6 +40134,7 @@
40086
40134
  this.nextPositionsToUpdate = cells;
40087
40135
  let currentIteration = 0;
40088
40136
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40137
+ this.updateCompilationParameters();
40089
40138
  const positionIds = Array.from(this.nextPositionsToUpdate);
40090
40139
  this.nextPositionsToUpdate.clear();
40091
40140
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40449,7 +40498,6 @@
40449
40498
  // of other cells depending on it, at the next iteration.
40450
40499
  //#endregion
40451
40500
  class EvaluationPlugin extends UIPlugin {
40452
- config;
40453
40501
  static getters = [
40454
40502
  "evaluateFormula",
40455
40503
  "getCorrespondingFormulaCell",
@@ -40460,15 +40508,13 @@
40460
40508
  "getEvaluatedCells",
40461
40509
  "getEvaluatedCellsInZone",
40462
40510
  "getSpreadPositionsOf",
40511
+ "getArrayFormulaSpreadingOn",
40463
40512
  ];
40464
40513
  shouldRebuildDependenciesGraph = true;
40465
40514
  evaluator;
40466
- compilationParams;
40467
40515
  positionsToUpdate = [];
40468
40516
  constructor(config) {
40469
40517
  super(config);
40470
- this.config = config;
40471
- this.compilationParams = this.getCompilationParameters();
40472
40518
  this.evaluator = new Evaluator(config.custom, this.getters);
40473
40519
  }
40474
40520
  // ---------------------------------------------------------------------------
@@ -40493,10 +40539,6 @@
40493
40539
  case "EVALUATE_CELLS":
40494
40540
  this.evaluator.evaluateAllCells();
40495
40541
  break;
40496
- case "UPDATE_LOCALE":
40497
- this.compilationParams = this.getCompilationParameters();
40498
- this.evaluator.updateCompilationParameters();
40499
- break;
40500
40542
  }
40501
40543
  }
40502
40544
  finalize() {
@@ -40514,16 +40556,7 @@
40514
40556
  // Getters
40515
40557
  // ---------------------------------------------------------------------------
40516
40558
  evaluateFormula(sheetId, formulaString) {
40517
- const compiledFormula = compile(formulaString);
40518
- const ranges = [];
40519
- for (let xc of compiledFormula.dependencies) {
40520
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
40521
- }
40522
- const array = compiledFormula.execute(ranges, ...this.compilationParams);
40523
- if (isMatrix(array)) {
40524
- return array.map((col) => col.map((row) => row.value));
40525
- }
40526
- return array.value;
40559
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40527
40560
  }
40528
40561
  /**
40529
40562
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40572,6 +40605,9 @@
40572
40605
  getSpreadPositionsOf(position) {
40573
40606
  return this.evaluator.getSpreadPositionsOf(position);
40574
40607
  }
40608
+ getArrayFormulaSpreadingOn(position) {
40609
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40610
+ }
40575
40611
  // ---------------------------------------------------------------------------
40576
40612
  // Export
40577
40613
  // ---------------------------------------------------------------------------
@@ -40615,7 +40651,7 @@
40615
40651
  else if (cell && cell.content) {
40616
40652
  return undefined;
40617
40653
  }
40618
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40654
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40619
40655
  if (spreadingFormulaPosition === undefined) {
40620
40656
  return undefined;
40621
40657
  }
@@ -40625,9 +40661,6 @@
40625
40661
  }
40626
40662
  return undefined;
40627
40663
  }
40628
- getCompilationParameters() {
40629
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40630
- }
40631
40664
  }
40632
40665
  function isBadExpression(tokens) {
40633
40666
  try {
@@ -41768,19 +41801,21 @@
41768
41801
  let col = zone.left;
41769
41802
  let row = zone.bottom;
41770
41803
  if (col > 0) {
41771
- let left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
41772
- while (left.type !== CellValueType.empty) {
41804
+ let leftPosition = { sheetId, col: col - 1, row };
41805
+ while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
41806
+ this.getters.getCell(leftPosition)?.content) {
41773
41807
  row += 1;
41774
- left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
41808
+ leftPosition = { sheetId, col: col - 1, row };
41775
41809
  }
41776
41810
  }
41777
41811
  if (row === zone.bottom) {
41778
41812
  col = zone.right;
41779
41813
  if (col <= this.getters.getNumberCols(sheetId)) {
41780
- let right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
41781
- while (right.type !== CellValueType.empty) {
41814
+ let rightPosition = { sheetId, col: col + 1, row };
41815
+ while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
41816
+ this.getters.getCell(rightPosition)?.content) {
41782
41817
  row += 1;
41783
- right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
41818
+ rightPosition = { sheetId, col: col + 1, row };
41784
41819
  }
41785
41820
  }
41786
41821
  }
@@ -42772,6 +42807,7 @@
42772
42807
  isReplayingInitialRevisions = false;
42773
42808
  processedRevisions = new Set();
42774
42809
  uuidGenerator = new UuidGenerator();
42810
+ lastLocalOperation;
42775
42811
  /**
42776
42812
  * Manages the collaboration between multiple users on the same spreadsheet.
42777
42813
  * It can forward local state changes to other users to ensure they all eventually
@@ -42804,6 +42840,11 @@
42804
42840
  return;
42805
42841
  const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
42806
42842
  this.revisions.append(revision.id, revision);
42843
+ // REQUEST_REDO just repeats the last operation, the
42844
+ // last operation is still the same and should not change.
42845
+ if (rootCommand.type !== "REQUEST_REDO") {
42846
+ this.lastLocalOperation = revision;
42847
+ }
42807
42848
  this.trigger("new-local-state-update", { id: revision.id });
42808
42849
  this.sendUpdateMessage({
42809
42850
  type: "REMOTE_REVISION",
@@ -42900,19 +42941,10 @@
42900
42941
  return this.pendingMessages.length === 0;
42901
42942
  }
42902
42943
  /**
42903
- * Get the last local revision whose root command isn't in the given list of ignored commands
42944
+ * Get the last local revision
42904
42945
  * */
42905
- getLastLocalNonEmptyRevision(ignoredRootCommands) {
42906
- const revisions = this.revisions.getRevertedExecution();
42907
- for (const rev of revisions) {
42908
- if (rev.rootCommand === "SNAPSHOT")
42909
- return undefined;
42910
- if (!rev.rootCommand || rev.rootCommand === "REMOTE")
42911
- continue;
42912
- if (!ignoredRootCommands.includes(rev.rootCommand?.type) && rev.commands.length)
42913
- return rev;
42914
- }
42915
- return undefined;
42946
+ getLastLocalNonEmptyRevision() {
42947
+ return this.lastLocalOperation;
42916
42948
  }
42917
42949
  _move(position) {
42918
42950
  // this method is debounced and might be called after the client
@@ -42972,7 +43004,7 @@
42972
43004
  break;
42973
43005
  case "REMOTE_REVISION":
42974
43006
  const { clientId, commands, timestamp } = message;
42975
- const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE", undefined, timestamp);
43007
+ const revision = new Revision(message.nextRevisionId, clientId, commands, undefined, undefined, timestamp);
42976
43008
  if (revision.clientId !== this.clientId) {
42977
43009
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
42978
43010
  const pendingCommands = this.pendingMessages
@@ -42985,10 +43017,11 @@
42985
43017
  }
42986
43018
  break;
42987
43019
  case "SNAPSHOT_CREATED": {
42988
- const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT", undefined, Date.now());
43020
+ const revision = new Revision(message.nextRevisionId, "server", [], undefined, undefined, Date.now());
42989
43021
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
42990
43022
  this.dropPendingHistoryMessages();
42991
43023
  this.trigger("snapshot");
43024
+ this.lastLocalOperation = undefined;
42992
43025
  break;
42993
43026
  }
42994
43027
  }
@@ -43326,11 +43359,26 @@
43326
43359
  let cellsInRow = [];
43327
43360
  for (let col of columnsIndex) {
43328
43361
  const position = { col, row, sheetId };
43362
+ const spreader = getters.getArrayFormulaSpreadingOn(position);
43363
+ let cell = getters.getCell(position);
43364
+ const evaluatedCell = getters.getEvaluatedCell(position);
43365
+ if (spreader) {
43366
+ const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
43367
+ const content = isSpreaderCopied
43368
+ ? ""
43369
+ : formatValue(evaluatedCell.value, { locale: getters.getLocale() });
43370
+ cell = {
43371
+ id: cell?.id || "",
43372
+ style: cell?.style,
43373
+ format: evaluatedCell.format,
43374
+ content,
43375
+ isFormula: false,
43376
+ };
43377
+ }
43329
43378
  cellsInRow.push({
43330
- cell: getters.getCell(position),
43331
- style: getters.getCellComputedStyle(position),
43332
- evaluatedCell: getters.getEvaluatedCell(position),
43379
+ cell,
43333
43380
  border: getters.getCellBorder(position) || undefined,
43381
+ evaluatedCell,
43334
43382
  position,
43335
43383
  });
43336
43384
  }
@@ -43452,13 +43500,6 @@
43452
43500
  });
43453
43501
  }
43454
43502
  this.pasteCopiedTables(target);
43455
- this.cells.forEach((row) => {
43456
- row.forEach((c) => {
43457
- if (c.cell) {
43458
- c.cell = undefined;
43459
- }
43460
- });
43461
- });
43462
43503
  }
43463
43504
  /**
43464
43505
  * The clipped zone is copied as many times as it fits in the target.
@@ -43530,7 +43571,7 @@
43530
43571
  clearClippedZones() {
43531
43572
  for (const row of this.cells) {
43532
43573
  for (const cell of row) {
43533
- if (cell.cell) {
43574
+ if (cell?.cell) {
43534
43575
  this.dispatch("CLEAR_CELL", cell.position);
43535
43576
  }
43536
43577
  }
@@ -43556,6 +43597,9 @@
43556
43597
  const rowCells = this.cells[r];
43557
43598
  for (let c = 0; c < width; c++) {
43558
43599
  const origin = rowCells[c];
43600
+ if (!origin) {
43601
+ continue;
43602
+ }
43559
43603
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43560
43604
  // TODO: refactor this part. the "Paste merge" action is also executed with
43561
43605
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43579,53 +43623,42 @@
43579
43623
  pasteCell(origin, target, operation, clipboardOption) {
43580
43624
  const { sheetId, col, row } = target;
43581
43625
  const targetCell = this.getters.getEvaluatedCell(target);
43582
- if (clipboardOption?.pasteOption !== "onlyValue") {
43583
- const targetBorders = this.getters.getCellBorder(target);
43584
- const originBorders = origin.border;
43585
- const border = {
43586
- top: targetBorders?.top || originBorders?.top,
43587
- bottom: targetBorders?.bottom || originBorders?.bottom,
43588
- left: targetBorders?.left || originBorders?.left,
43589
- right: targetBorders?.right || originBorders?.right,
43590
- };
43591
- this.dispatch("SET_BORDER", { sheetId, col, row, border });
43626
+ if (clipboardOption?.pasteOption === "onlyValue") {
43627
+ const locale = this.getters.getLocale();
43628
+ const content = formatValue(origin.evaluatedCell.value, { locale });
43629
+ this.dispatch("UPDATE_CELL", { ...target, content });
43630
+ return;
43592
43631
  }
43593
- if (origin.cell) {
43594
- if (clipboardOption?.pasteOption === "onlyFormat") {
43595
- this.dispatch("UPDATE_CELL", {
43596
- ...target,
43597
- style: origin.cell.style,
43598
- format: origin.evaluatedCell.format,
43599
- });
43600
- return;
43601
- }
43602
- if (clipboardOption?.pasteOption === "onlyValue") {
43603
- const locale = this.getters.getLocale();
43604
- const content = formatValue(origin.evaluatedCell.value, { locale });
43605
- this.dispatch("UPDATE_CELL", { ...target, content });
43606
- return;
43607
- }
43608
- let content = origin.cell.content;
43609
- if (origin.cell.isFormula && operation === "COPY") {
43610
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula);
43611
- }
43632
+ const targetBorders = this.getters.getCellBorder(target);
43633
+ const originBorders = origin.border;
43634
+ const border = {
43635
+ top: targetBorders?.top || originBorders?.top,
43636
+ bottom: targetBorders?.bottom || originBorders?.bottom,
43637
+ left: targetBorders?.left || originBorders?.left,
43638
+ right: targetBorders?.right || originBorders?.right,
43639
+ };
43640
+ this.dispatch("SET_BORDER", { sheetId, col, row, border });
43641
+ if (clipboardOption?.pasteOption === "onlyFormat") {
43642
+ this.dispatch("UPDATE_CELL", {
43643
+ ...target,
43644
+ style: origin.cell?.style ?? null,
43645
+ format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
43646
+ });
43647
+ return;
43648
+ }
43649
+ const content = origin.cell && origin.cell.isFormula && operation === "COPY"
43650
+ ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula)
43651
+ : origin.cell?.content;
43652
+ if (content !== "" || origin.cell?.format || origin.cell?.style) {
43612
43653
  this.dispatch("UPDATE_CELL", {
43613
43654
  ...target,
43614
43655
  content,
43615
- style: origin.cell.style || null,
43616
- format: origin.cell.format,
43656
+ style: origin.cell?.style || null,
43657
+ format: origin.cell?.format,
43617
43658
  });
43618
43659
  }
43619
43660
  else if (targetCell) {
43620
- if (clipboardOption?.pasteOption === "onlyValue") {
43621
- this.dispatch("UPDATE_CELL", { ...target, content: "" });
43622
- }
43623
- else if (clipboardOption?.pasteOption === "onlyFormat") {
43624
- this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" });
43625
- }
43626
- else {
43627
- this.dispatch("CLEAR_CELL", target);
43628
- }
43661
+ this.dispatch("CLEAR_CELL", target);
43629
43662
  }
43630
43663
  }
43631
43664
  /**
@@ -43687,22 +43720,28 @@
43687
43720
  return (this.cells
43688
43721
  .map((cells) => {
43689
43722
  return cells
43690
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43723
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43691
43724
  ? c.cell?.content || ""
43692
- : c.evaluatedCell?.formattedValue || "")
43725
+ : c?.evaluatedCell?.formattedValue || "")
43693
43726
  .join("\t");
43694
43727
  })
43695
43728
  .join("\n") || "\t");
43696
43729
  }
43697
43730
  getHTMLContent() {
43698
43731
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43732
+ if (!this.cells[0][0]) {
43733
+ return "";
43734
+ }
43699
43735
  return this.getters.getCellText(this.cells[0][0].position);
43700
43736
  }
43701
43737
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43702
43738
  for (const row of this.cells) {
43703
43739
  htmlTable += "<tr>";
43704
43740
  for (const cell of row) {
43705
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43741
+ if (!cell) {
43742
+ continue;
43743
+ }
43744
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43706
43745
  const cellText = this.getters.getCellText(cell.position);
43707
43746
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43708
43747
  }
@@ -45537,6 +45576,9 @@
45537
45576
  // Other
45538
45577
  // ---------------------------------------------------------------------------
45539
45578
  initInput(id, initialRanges, inputHasSingleRange = false) {
45579
+ if (this.inputs[id]) {
45580
+ this.unfocus();
45581
+ }
45540
45582
  this.inputs[id] = new SelectionInputPlugin(this.config, initialRanges, inputHasSingleRange);
45541
45583
  if (initialRanges.length === 0) {
45542
45584
  const input = this.inputs[id];
@@ -46335,7 +46377,7 @@
46335
46377
  * Ignore standard undo/redo revisions (that are empty)
46336
46378
  */
46337
46379
  getPossibleRevisionToRepeat() {
46338
- return this.session.getLastLocalNonEmptyRevision(["REQUEST_REDO"]);
46380
+ return this.session.getLastLocalNonEmptyRevision();
46339
46381
  }
46340
46382
  }
46341
46383
 
@@ -46832,11 +46874,14 @@
46832
46874
  }
46833
46875
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46834
46876
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
46877
+ if (this.state.operation === "CUT") {
46878
+ this.state = undefined;
46879
+ }
46835
46880
  this.lastPasteState = this.state;
46836
46881
  if (this.paintFormatStatus === "oneOff") {
46837
46882
  this.paintFormatStatus = "inactive";
46838
- this.status = "invisible";
46839
46883
  }
46884
+ this.status = "invisible";
46840
46885
  break;
46841
46886
  case "COPY_PASTE_CELLS_ABOVE":
46842
46887
  {
@@ -47117,6 +47162,10 @@
47117
47162
  return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
47118
47163
  case "colrow":
47119
47164
  indexOfNumber = xc.search(/[0-9]/);
47165
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
47166
+ // no row number (eg. A) or no column (eg. 1)
47167
+ return "$" + xc;
47168
+ }
47120
47169
  xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
47121
47170
  return "$" + xc;
47122
47171
  case "none":
@@ -47887,6 +47936,12 @@
47887
47936
  break;
47888
47937
  case "HIDE_COLUMNS_ROWS":
47889
47938
  case "UNHIDE_COLUMNS_ROWS":
47939
+ case "GROUP_HEADERS":
47940
+ case "UNGROUP_HEADERS":
47941
+ case "FOLD_HEADER_GROUP":
47942
+ case "UNFOLD_HEADER_GROUP":
47943
+ case "FOLD_ALL_HEADER_GROUPS":
47944
+ case "UNFOLD_ALL_HEADER_GROUPS":
47890
47945
  this.updateHiddenRows();
47891
47946
  break;
47892
47947
  case "UPDATE_FILTER":
@@ -48588,7 +48643,7 @@
48588
48643
  sheetId: cmd.sheetId,
48589
48644
  base: cmd.base,
48590
48645
  quantity: thickness,
48591
- position: "before",
48646
+ position: cmd.position,
48592
48647
  });
48593
48648
  const isCol = cmd.dimension === "COL";
48594
48649
  const start = cmd.elements[0];
@@ -48605,12 +48660,13 @@
48605
48660
  },
48606
48661
  ];
48607
48662
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
48663
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
48608
48664
  const pasteTarget = [
48609
48665
  {
48610
- left: isCol ? cmd.base : 0,
48611
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
48612
- top: !isCol ? cmd.base : 0,
48613
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
48666
+ left: isCol ? base : 0,
48667
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
48668
+ top: !isCol ? base : 0,
48669
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
48614
48670
  },
48615
48671
  ];
48616
48672
  state.paste(pasteTarget, { selectTarget: true });
@@ -48645,6 +48701,11 @@
48645
48701
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
48646
48702
  return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
48647
48703
  }
48704
+ const headers = [cmd.base, ...cmd.elements];
48705
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
48706
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
48707
+ return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
48708
+ }
48648
48709
  return "Success" /* CommandResult.Success */;
48649
48710
  }
48650
48711
  //-------------------------------------------
@@ -49690,10 +49751,13 @@
49690
49751
  }
49691
49752
  break;
49692
49753
  case "UPDATE_CELL":
49693
- if ("content" in cmd || "format" in cmd || cmd.style?.fontSize !== undefined) {
49754
+ if ("content" in cmd || "format" in cmd) {
49694
49755
  this.headerPositions = {};
49695
49756
  this.isDirty = true;
49696
49757
  }
49758
+ else {
49759
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
49760
+ }
49697
49761
  break;
49698
49762
  case "UPDATE_FILTER":
49699
49763
  case "REMOVE_FILTER_TABLE":
@@ -51850,6 +51914,8 @@
51850
51914
  *:before,
51851
51915
  *:after {
51852
51916
  box-sizing: content-box;
51917
+ /** rtl not supported ATM */
51918
+ direction: ltr;
51853
51919
  }
51854
51920
  .o-separator {
51855
51921
  border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
@@ -55560,19 +55626,22 @@
55560
55626
  * Check if the given command is allowed by all the plugins and the history.
55561
55627
  */
55562
55628
  checkDispatchAllowed(command) {
55563
- if (isCoreCommand(command)) {
55564
- return this.checkDispatchAllowedCoreCommand(command);
55629
+ const results = isCoreCommand(command)
55630
+ ? this.checkDispatchAllowedCoreCommand(command)
55631
+ : this.checkDispatchAllowedLocalCommand(command);
55632
+ if (results.some((r) => r !== "Success" /* CommandResult.Success */)) {
55633
+ return new DispatchResult(results.flat());
55565
55634
  }
55566
- return this.checkDispatchAllowedLocalCommand(command);
55635
+ return DispatchResult.Success;
55567
55636
  }
55568
55637
  checkDispatchAllowedCoreCommand(command) {
55569
55638
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55570
55639
  results.push(this.range.allowDispatch(command));
55571
- return new DispatchResult(results.flat());
55640
+ return results;
55572
55641
  }
55573
55642
  checkDispatchAllowedLocalCommand(command) {
55574
55643
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55575
- return new DispatchResult(results.flat());
55644
+ return results;
55576
55645
  }
55577
55646
  finalize() {
55578
55647
  this.status = 3 /* Status.Finalizing */;
@@ -55919,9 +55988,9 @@
55919
55988
  exports.tokenize = tokenize;
55920
55989
 
55921
55990
 
55922
- __info__.version = "17.0.3";
55923
- __info__.date = "2023-11-16T11:59:49.524Z";
55924
- __info__.hash = "8bd0fb2";
55991
+ __info__.version = "17.0.5";
55992
+ __info__.date = "2023-12-05T09:51:44.657Z";
55993
+ __info__.hash = "2480a5d";
55925
55994
 
55926
55995
 
55927
55996
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);