@odoo/o-spreadsheet 17.1.0-alpha.3 → 17.1.0-alpha.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.1.0-alpha.3
6
- * @date 2023-11-16T12:04:09.636Z
7
- * @hash 2ef5b1a
5
+ * @version 17.1.0-alpha.5
6
+ * @date 2023-12-05T09:51:40.034Z
7
+ * @hash c2823eb
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -228,7 +228,6 @@
228
228
  const SCROLLBAR_WIDTH = 15;
229
229
  const AUTOFILL_EDGE_LENGTH = 8;
230
230
  const ICON_EDGE_LENGTH = 18;
231
- const UNHIDE_ICON_EDGE_LENGTH = 14;
232
231
  const MIN_CF_ICON_MARGIN = 4;
233
232
  const MIN_CELL_TEXT_MARGIN = 4;
234
233
  const CF_ICON_EDGE_LENGTH = 15;
@@ -692,8 +691,7 @@
692
691
  *
693
692
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
694
693
  */
695
- const whiteSpaceCharacters = [
696
- " ",
694
+ const whiteSpaceSpecialCharacters = [
697
695
  "\t",
698
696
  "\f",
699
697
  "\v",
@@ -708,8 +706,7 @@
708
706
  String.fromCharCode(parseInt("3000", 16)),
709
707
  String.fromCharCode(parseInt("feff", 16)),
710
708
  ];
711
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
712
- const newLineRegex = /\r\n|\r|\n/g;
709
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
713
710
  /**
714
711
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
715
712
  * different newlines types by \n.
@@ -717,9 +714,9 @@
717
714
  function replaceSpecialSpaces(text) {
718
715
  if (!text)
719
716
  return "";
720
- text = text.replace(whiteSpaceRegexp, " ");
721
- text = text.replace(newLineRegex, NEWLINE);
722
- return text;
717
+ if (!whiteSpaceRegexp.test(text))
718
+ return text;
719
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
723
720
  }
724
721
  /**
725
722
  * Determine if the numbers are consecutive.
@@ -2061,6 +2058,7 @@
2061
2058
  CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
2062
2059
  CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
2063
2060
  CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
2061
+ CommandResult["NoChanges"] = "NoChanges";
2064
2062
  })(exports.CommandResult || (exports.CommandResult = {}));
2065
2063
 
2066
2064
  const DEFAULT_LOCALES = [
@@ -2190,12 +2188,12 @@
2190
2188
  }
2191
2189
  }
2192
2190
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2193
- function normalizeString(str) {
2191
+ const normalizeString = memoize(function normalizeString(str) {
2194
2192
  return str
2195
2193
  .toLowerCase()
2196
2194
  .normalize("NFD")
2197
2195
  .replace(/[\u0300-\u036f]/g, "");
2198
- }
2196
+ });
2199
2197
  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
2198
  function toBoolean(value) {
2201
2199
  switch (typeof value) {
@@ -3389,8 +3387,10 @@
3389
3387
  const cellReference = new RegExp(/\$?([A-Z]{1,3})\$?([0-9]{1,7})/, "i");
3390
3388
  // Same as above, but matches the exact string (nothing before or after)
3391
3389
  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");
3390
+ /** Reference of a column header (eg. A, AB, $A) */
3391
+ const colHeader = new RegExp(/^\$?([A-Z]{1,3})+$/, "i");
3392
+ /** Reference of a row header (eg. 1, $1) */
3393
+ const rowHeader = new RegExp(/^\$?([0-9]{1,7})+$/, "i");
3394
3394
  /** Reference of a column (eg. A, $CA, Sheet1!B) */
3395
3395
  const colReference = new RegExp(/^\s*('.+'!|[^']+!)?\$?([A-Z]{1,3})$/, "i");
3396
3396
  /** Reference of a row (eg. 1, 59, Sheet1!9) */
@@ -3420,6 +3420,9 @@
3420
3420
  function isColHeader(str) {
3421
3421
  return colHeader.test(str);
3422
3422
  }
3423
+ function isRowHeader(str) {
3424
+ return rowHeader.test(str);
3425
+ }
3423
3426
  /**
3424
3427
  * Return true if the given xc is the reference of a single cell,
3425
3428
  * without any specified sheet (e.g. A1)
@@ -3428,11 +3431,18 @@
3428
3431
  return singleCellReference.test(xc);
3429
3432
  }
3430
3433
  function splitReference(ref) {
3434
+ if (!ref.includes("!")) {
3435
+ return { xc: ref };
3436
+ }
3431
3437
  const parts = ref.split("!");
3432
3438
  const xc = parts.pop();
3433
3439
  const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
3434
3440
  return { sheetName, xc };
3435
3441
  }
3442
+ /** Return a reference SheetName!xc from the given arguments */
3443
+ function getFullReference(sheetName, xc) {
3444
+ return sheetName !== undefined ? `${getCanonicalSheetName(sheetName)}!${xc}` : xc;
3445
+ }
3436
3446
 
3437
3447
  /**
3438
3448
  * Convert from a cartesian reference to a Zone
@@ -4103,18 +4113,10 @@
4103
4113
  if (isFullCol) {
4104
4114
  parts[0].rowFixed = parts[0].rowFixed || parts[1].rowFixed;
4105
4115
  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
4116
  }
4111
4117
  if (isFullRow) {
4112
4118
  parts[0].colFixed = parts[0].colFixed || parts[1].colFixed;
4113
4119
  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
4120
  }
4119
4121
  return parts;
4120
4122
  }
@@ -4356,7 +4358,7 @@
4356
4358
  function getDefaultCellHeight(ctx, cell, colSize) {
4357
4359
  if (!cell || !cell.content)
4358
4360
  return DEFAULT_CELL_HEIGHT;
4359
- const maxWidth = cell.style?.wrapping ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4361
+ const maxWidth = cell.style?.wrapping === "wrap" ? colSize - 2 * MIN_CELL_TEXT_MARGIN : undefined;
4360
4362
  const numberOfLines = cell.isFormula
4361
4363
  ? 1
4362
4364
  : splitTextToWidth(ctx, cell.content, cell.style, maxWidth).length;
@@ -4541,14 +4543,14 @@
4541
4543
  switch (context.textBaseline) {
4542
4544
  case "top":
4543
4545
  underlineY += boxHeight - 2 * strokeWidth;
4544
- strikeY += boxHeight - textHeight;
4546
+ strikeY += boxHeight / 2 - strokeWidth;
4545
4547
  break;
4546
4548
  case "middle":
4547
4549
  underlineY += boxHeight / 2 - strokeWidth;
4548
4550
  break;
4549
4551
  case "alphabetic":
4550
4552
  underlineY += 2 * strokeWidth;
4551
- strikeY -= textHeight / 2 - strokeWidth / 2;
4553
+ strikeY -= 3 * strokeWidth;
4552
4554
  break;
4553
4555
  case "bottom":
4554
4556
  underlineY = y;
@@ -4611,6 +4613,7 @@
4611
4613
  const children = item.children;
4612
4614
  const description = item.description;
4613
4615
  const icon = item.icon;
4616
+ const secondaryIcon = item.secondaryIcon;
4614
4617
  return {
4615
4618
  id: item.id || uuidGenerator$2.uuidv4(),
4616
4619
  name: typeof name === "function" ? name : () => name,
@@ -4629,6 +4632,7 @@
4629
4632
  isReadonlyAllowed: item.isReadonlyAllowed || false,
4630
4633
  separator: item.separator || false,
4631
4634
  icon: typeof icon === "function" ? icon : () => icon || "",
4635
+ secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
4632
4636
  description: typeof description === "function" ? description : () => description || "",
4633
4637
  textColor: item.textColor,
4634
4638
  sequence: item.sequence || 0,
@@ -6251,10 +6255,6 @@
6251
6255
 
6252
6256
  .o-error-tooltip-message {
6253
6257
  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
6258
  }
6259
6259
  }
6260
6260
  `;
@@ -6662,6 +6662,9 @@
6662
6662
  keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
6663
6663
  return keyDownString;
6664
6664
  }
6665
+ function isMacOS() {
6666
+ return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
6667
+ }
6665
6668
 
6666
6669
  /**
6667
6670
  * Return the o-spreadsheet element position relative
@@ -6984,11 +6987,9 @@
6984
6987
  padding: ${MENU_VERTICAL_PADDING}px 0px;
6985
6988
  width: ${MENU_WIDTH}px;
6986
6989
  box-sizing: border-box !important;
6990
+ user-select: none;
6987
6991
 
6988
6992
  .o-menu-item {
6989
- display: flex;
6990
- justify-content: space-between;
6991
- align-items: center;
6992
6993
  box-sizing: border-box;
6993
6994
  height: ${MENU_ITEM_HEIGHT}px;
6994
6995
  padding: ${MENU_ITEM_PADDING_VERTICAL}px ${MENU_ITEM_PADDING_HORIZONTAL}px;
@@ -6999,20 +7000,12 @@
6999
7000
  min-width: 40%;
7000
7001
  }
7001
7002
 
7002
- &.o-menu-root {
7003
- display: flex;
7004
- justify-content: space-between;
7005
- }
7006
-
7007
7003
  .o-menu-item-icon {
7008
7004
  display: inline-block;
7009
7005
  margin: 0px 8px 0px 0px;
7010
7006
  width: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
7011
7007
  line-height: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
7012
7008
  }
7013
- .o-menu-item-root {
7014
- width: 10px;
7015
- }
7016
7009
 
7017
7010
  &:not(.disabled) {
7018
7011
  &:hover,
@@ -7355,7 +7348,7 @@
7355
7348
  return result;
7356
7349
  }
7357
7350
  function tokenizeDebugger(chars) {
7358
- if (chars.current() === "?") {
7351
+ if (chars.current === "?") {
7359
7352
  chars.shift();
7360
7353
  return { type: "DEBUGGER", value: "?" };
7361
7354
  }
@@ -7366,7 +7359,7 @@
7366
7359
  ")": "RIGHT_PAREN",
7367
7360
  };
7368
7361
  function tokenizeMisc(chars) {
7369
- if (chars.current() in misc$1) {
7362
+ if (chars.current in misc$1) {
7370
7363
  const value = chars.shift();
7371
7364
  const type = misc$1[value];
7372
7365
  return { type, value };
@@ -7374,7 +7367,7 @@
7374
7367
  return null;
7375
7368
  }
7376
7369
  function tokenizeArgsSeparator(chars, locale) {
7377
- if (chars.current() === locale.formulaArgSeparator) {
7370
+ if (chars.current === locale.formulaArgSeparator) {
7378
7371
  const value = chars.shift();
7379
7372
  const type = "ARG_SEPARATOR";
7380
7373
  return { type, value };
@@ -7399,14 +7392,13 @@
7399
7392
  return null;
7400
7393
  }
7401
7394
  function tokenizeString(chars) {
7402
- if (chars.current() === '"') {
7395
+ if (chars.current === '"') {
7403
7396
  const startChar = chars.shift();
7404
7397
  let letters = startChar;
7405
- while (chars.current() &&
7406
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
7398
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
7407
7399
  letters += chars.shift();
7408
7400
  }
7409
- if (chars.current() === '"') {
7401
+ if (chars.current === '"') {
7410
7402
  letters += chars.shift();
7411
7403
  }
7412
7404
  return {
@@ -7433,14 +7425,14 @@
7433
7425
  let result = "";
7434
7426
  // there are two main cases to manage: either something which starts with
7435
7427
  // a ', like 'Sheet 2'A2, or a word-like element.
7436
- if (chars.current() === "'") {
7428
+ if (chars.current === "'") {
7437
7429
  let lastChar = chars.shift();
7438
7430
  result += lastChar;
7439
- while (chars.current()) {
7431
+ while (chars.current) {
7440
7432
  lastChar = chars.shift();
7441
7433
  result += lastChar;
7442
7434
  if (lastChar === "'") {
7443
- if (chars.current() && chars.current() === "'") {
7435
+ if (chars.current && chars.current === "'") {
7444
7436
  lastChar = chars.shift();
7445
7437
  result += lastChar;
7446
7438
  }
@@ -7456,7 +7448,7 @@
7456
7448
  };
7457
7449
  }
7458
7450
  }
7459
- while (chars.current() && separatorRegexp.test(chars.current())) {
7451
+ while (chars.current && separatorRegexp.test(chars.current)) {
7460
7452
  result += chars.shift();
7461
7453
  }
7462
7454
  if (result.length) {
@@ -7471,14 +7463,14 @@
7471
7463
  }
7472
7464
  function tokenizeSpace(chars) {
7473
7465
  let length = 0;
7474
- while (chars.current() === NEWLINE) {
7466
+ while (chars.current === NEWLINE) {
7475
7467
  length++;
7476
7468
  chars.shift();
7477
7469
  }
7478
7470
  if (length) {
7479
7471
  return { type: "SPACE", value: NEWLINE.repeat(length) };
7480
7472
  }
7481
- while (chars.current() === " ") {
7473
+ while (chars.current === " ") {
7482
7474
  length++;
7483
7475
  chars.shift();
7484
7476
  }
@@ -7497,17 +7489,20 @@
7497
7489
  class TokenizingChars {
7498
7490
  text;
7499
7491
  currentIndex = 0;
7492
+ current;
7500
7493
  constructor(text) {
7501
7494
  this.text = text;
7502
- }
7503
- current() {
7504
- return this.text[this.currentIndex];
7495
+ this.current = text[0];
7505
7496
  }
7506
7497
  shift() {
7507
- return this.text[this.currentIndex++];
7498
+ const current = this.current;
7499
+ const next = this.text[++this.currentIndex];
7500
+ this.current = next;
7501
+ return current;
7508
7502
  }
7509
7503
  advanceBy(length) {
7510
7504
  this.currentIndex += length;
7505
+ this.current = this.text[this.currentIndex];
7511
7506
  }
7512
7507
  isOver() {
7513
7508
  return this.currentIndex >= this.text.length;
@@ -11311,7 +11306,7 @@
11311
11306
  ) {
11312
11307
  const _array = toMatrix(arg);
11313
11308
  const _nbRows = toInteger(rows?.value, this.locale);
11314
- const _nbColumns = columns !== undefined ? toInteger(columns.value, this.local) : _array.length;
11309
+ const _nbColumns = columns !== undefined ? toInteger(columns.value, this.locale) : _array.length;
11315
11310
  assert(() => _nbRows >= _array[0].length, _t("The rows arguments (%s) must be greater or equal than the number of rows of the array.", _nbRows.toString()));
11316
11311
  assert(() => _nbColumns >= _array.length, _t("The columns arguments (%s) must be greater or equal than the number of columns of the array.", _nbColumns.toString()));
11317
11312
  return generateMatrix(_nbColumns, _nbRows, (col, row) => col >= _array.length || row >= _array[col].length ? padWith : _array[col][row]);
@@ -11800,6 +11795,10 @@
11800
11795
  FORMAT_LARGE_NUMBER: FORMAT_LARGE_NUMBER
11801
11796
  });
11802
11797
 
11798
+ function sum(values, locale) {
11799
+ return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
11800
+ }
11801
+
11803
11802
  const DEFAULT_FACTOR = 1;
11804
11803
  const DEFAULT_MODE = 0;
11805
11804
  const DEFAULT_PLACES = 0;
@@ -12751,7 +12750,7 @@
12751
12750
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
12752
12751
  },
12753
12752
  compute: function (...values) {
12754
- return reduceNumbers(values, (acc, a) => acc + a, 0, this.locale);
12753
+ return sum(values, this.locale);
12755
12754
  },
12756
12755
  isExported: true,
12757
12756
  };
@@ -12920,6 +12919,44 @@
12920
12919
  const dims = args[0].length;
12921
12920
  args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
12922
12921
  }
12922
+ function average(values, locale) {
12923
+ let count = 0;
12924
+ const sum = reduceNumbers(values, (acc, a) => {
12925
+ count += 1;
12926
+ return acc + a;
12927
+ }, 0, locale);
12928
+ assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
12929
+ return sum / count;
12930
+ }
12931
+ function countNumbers(values, locale) {
12932
+ let count = 0;
12933
+ for (let n of values) {
12934
+ if (isMatrix(n)) {
12935
+ for (let i of n) {
12936
+ for (let j of i) {
12937
+ if (typeof j === "number") {
12938
+ count += 1;
12939
+ }
12940
+ }
12941
+ }
12942
+ }
12943
+ else if (typeof n !== "string" || isNumber(n, locale) || parseDateTime(n, locale)) {
12944
+ count += 1;
12945
+ }
12946
+ }
12947
+ return count;
12948
+ }
12949
+ function countAny(values) {
12950
+ return reduceAny(values, (acc, a) => (a !== undefined && a !== null ? acc + 1 : acc), 0);
12951
+ }
12952
+ function max(values, locale) {
12953
+ const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
12954
+ return result === -Infinity ? 0 : result;
12955
+ }
12956
+ function min(values, locale) {
12957
+ const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
12958
+ return result === Infinity ? 0 : result;
12959
+ }
12923
12960
 
12924
12961
  function filterAndFlatData(dataY, dataX) {
12925
12962
  const _flatDataY = [];
@@ -13190,13 +13227,7 @@
13190
13227
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13191
13228
  },
13192
13229
  compute: function (...values) {
13193
- let count = 0;
13194
- const sum = reduceNumbers(values, (acc, a) => {
13195
- count += 1;
13196
- return acc + a;
13197
- }, 0, this.locale);
13198
- assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
13199
- return sum / count;
13230
+ return average(values, this.locale);
13200
13231
  },
13201
13232
  isExported: true,
13202
13233
  };
@@ -13354,24 +13385,7 @@
13354
13385
  ],
13355
13386
  returns: ["NUMBER"],
13356
13387
  compute: function (...values) {
13357
- let count = 0;
13358
- for (let n of values) {
13359
- if (isMatrix(n)) {
13360
- for (let i of n) {
13361
- for (let j of i) {
13362
- if (typeof j === "number") {
13363
- count += 1;
13364
- }
13365
- }
13366
- }
13367
- }
13368
- else if (typeof n !== "string" ||
13369
- isNumber(n, this.locale) ||
13370
- parseDateTime(n, this.locale)) {
13371
- count += 1;
13372
- }
13373
- }
13374
- return count;
13388
+ return countNumbers(values, this.locale);
13375
13389
  },
13376
13390
  isExported: true,
13377
13391
  };
@@ -13386,7 +13400,7 @@
13386
13400
  ],
13387
13401
  returns: ["NUMBER"],
13388
13402
  compute: function (...values) {
13389
- return reduceAny(values, (acc, a) => (a !== undefined && a !== null ? acc + 1 : acc), 0);
13403
+ return countAny(values);
13390
13404
  },
13391
13405
  isExported: true,
13392
13406
  };
@@ -13616,8 +13630,7 @@
13616
13630
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13617
13631
  },
13618
13632
  compute: function (...values) {
13619
- const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, this.locale);
13620
- return result === -Infinity ? 0 : result;
13633
+ return max(values, this.locale);
13621
13634
  },
13622
13635
  isExported: true,
13623
13636
  };
@@ -13704,8 +13717,7 @@
13704
13717
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13705
13718
  },
13706
13719
  compute: function (...values) {
13707
- const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, this.locale);
13708
- return result === Infinity ? 0 : result;
13720
+ return min(values, this.locale);
13709
13721
  },
13710
13722
  isExported: true,
13711
13723
  };
@@ -17876,6 +17888,153 @@
17876
17888
  YIELDMAT: YIELDMAT
17877
17889
  });
17878
17890
 
17891
+ /**
17892
+ * Change the reference types inside the given token, if the token represent a range or a cell
17893
+ *
17894
+ * Eg. :
17895
+ * A1 => $A$1 => A$1 => $A1 => A1
17896
+ * A1:$B$1 => $A$1:B$1 => A$1:$B1 => $A1:B1 => A1:$B$1
17897
+ */
17898
+ function loopThroughReferenceType(token) {
17899
+ if (token.type !== "REFERENCE")
17900
+ return token;
17901
+ const { xc, sheetName } = splitReference(token.value);
17902
+ const [left, right] = xc.split(":");
17903
+ const updatedLeft = getTokenNextReferenceType(left);
17904
+ const updatedRight = right ? `:${getTokenNextReferenceType(right)}` : "";
17905
+ return { ...token, value: getFullReference(sheetName, updatedLeft + updatedRight) };
17906
+ }
17907
+ /**
17908
+ * Get a new token with a changed type of reference from the given cell token symbol.
17909
+ * Undefined behavior if given a token other than a cell or if the Xc contains a sheet reference
17910
+ *
17911
+ * A1 => $A$1 => A$1 => $A1 => A1
17912
+ */
17913
+ function getTokenNextReferenceType(xc) {
17914
+ switch (getReferenceType(xc)) {
17915
+ case "none":
17916
+ xc = setXcToFixedReferenceType(xc, "colrow");
17917
+ break;
17918
+ case "colrow":
17919
+ xc = setXcToFixedReferenceType(xc, "row");
17920
+ break;
17921
+ case "row":
17922
+ xc = setXcToFixedReferenceType(xc, "col");
17923
+ break;
17924
+ case "col":
17925
+ xc = setXcToFixedReferenceType(xc, "none");
17926
+ break;
17927
+ }
17928
+ return xc;
17929
+ }
17930
+ /**
17931
+ * Returns the given XC with the given reference type. The XC string should not contain a sheet name.
17932
+ */
17933
+ function setXcToFixedReferenceType(xc, referenceType) {
17934
+ if (xc.includes("!")) {
17935
+ throw new Error("The given XC should not contain a sheet name");
17936
+ }
17937
+ xc = xc.replace(/\$/g, "");
17938
+ let indexOfNumber;
17939
+ switch (referenceType) {
17940
+ case "col":
17941
+ return "$" + xc;
17942
+ case "row":
17943
+ indexOfNumber = xc.search(/[0-9]/);
17944
+ return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
17945
+ case "colrow":
17946
+ indexOfNumber = xc.search(/[0-9]/);
17947
+ if (indexOfNumber === -1 || indexOfNumber === 0) {
17948
+ // no row number (eg. A) or no column (eg. 1)
17949
+ return "$" + xc;
17950
+ }
17951
+ xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
17952
+ return "$" + xc;
17953
+ case "none":
17954
+ return xc;
17955
+ }
17956
+ }
17957
+ /**
17958
+ * Return the type of reference used in the given XC of a cell.
17959
+ * Undefined behavior if the XC have a sheet reference
17960
+ */
17961
+ function getReferenceType(xcCell) {
17962
+ if (isColAndRowFixed(xcCell)) {
17963
+ return "colrow";
17964
+ }
17965
+ else if (isColFixed(xcCell)) {
17966
+ return "col";
17967
+ }
17968
+ else if (isRowFixed(xcCell)) {
17969
+ return "row";
17970
+ }
17971
+ return "none";
17972
+ }
17973
+ function isColFixed(xc) {
17974
+ return xc.startsWith("$");
17975
+ }
17976
+ function isRowFixed(xc) {
17977
+ return xc.includes("$", 1);
17978
+ }
17979
+ function isColAndRowFixed(xc) {
17980
+ return xc.startsWith("$") && xc.length > 1 && xc.slice(1).includes("$");
17981
+ }
17982
+
17983
+ // -----------------------------------------------------------------------------
17984
+ // CELL
17985
+ // -----------------------------------------------------------------------------
17986
+ // NOTE: missing from Excel: "color", "filename", "parentheses", "prefix", "protect" and "width"
17987
+ const CELL_INFO_TYPES = ["address", "col", "contents", "format", "row", "type"];
17988
+ const CELL = {
17989
+ description: _t("Gets information about a cell."),
17990
+ args: [
17991
+ arg("info_type (string)", _t("The type of information requested. Can be one of %s", CELL_INFO_TYPES.join(", "))),
17992
+ arg("reference (meta)", _t("The reference to the cell.")),
17993
+ ],
17994
+ returns: ["ANY"],
17995
+ compute: function (info, reference) {
17996
+ const _info = toString(info).toLowerCase();
17997
+ assert(() => CELL_INFO_TYPES.includes(_info), _t("The info_type should be one of %s.", CELL_INFO_TYPES.join(", ")));
17998
+ const sheetId = this.__originSheetId;
17999
+ const topLeftReference = reference.includes(":") ? reference.split(":")[0] : reference;
18000
+ let { sheetName, xc } = splitReference(topLeftReference);
18001
+ // only put the sheet name if the referenced range is in another sheet than the cell the formula is on
18002
+ sheetName = sheetName === this.getters.getSheetName(sheetId) ? undefined : sheetName;
18003
+ const fixedRef = getFullReference(sheetName, setXcToFixedReferenceType(xc, "colrow"));
18004
+ const range = this.getters.getRangeFromSheetXC(sheetId, fixedRef);
18005
+ switch (_info) {
18006
+ case "address":
18007
+ return this.getters.getRangeString(range, sheetId);
18008
+ case "col":
18009
+ return range.zone.left + 1;
18010
+ case "contents": {
18011
+ const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
18012
+ return this.getters.getEvaluatedCell(position).value;
18013
+ }
18014
+ case "format": {
18015
+ const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
18016
+ return this.getters.getEvaluatedCell(position).format || "";
18017
+ }
18018
+ case "row":
18019
+ return range.zone.top + 1;
18020
+ case "type": {
18021
+ const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
18022
+ const type = this.getters.getEvaluatedCell(position).type;
18023
+ if (type === CellValueType.empty) {
18024
+ return "b"; // blank
18025
+ }
18026
+ else if (type === CellValueType.text) {
18027
+ return "l"; // label
18028
+ }
18029
+ else {
18030
+ return "v"; // value
18031
+ }
18032
+ }
18033
+ }
18034
+ return "";
18035
+ },
18036
+ isExported: true,
18037
+ };
17879
18038
  // -----------------------------------------------------------------------------
17880
18039
  // ISERR
17881
18040
  // -----------------------------------------------------------------------------
@@ -18031,6 +18190,7 @@
18031
18190
 
18032
18191
  var info = /*#__PURE__*/Object.freeze({
18033
18192
  __proto__: null,
18193
+ CELL: CELL,
18034
18194
  ISBLANK: ISBLANK,
18035
18195
  ISERR: ISERR,
18036
18196
  ISERROR: ISERROR,
@@ -18322,7 +18482,7 @@
18322
18482
  cellReference = rowPart + colPart;
18323
18483
  }
18324
18484
  if (sheet !== undefined) {
18325
- return `${getCanonicalSheetName(toString(sheet))}!${cellReference}`;
18485
+ return getFullReference(toString(sheet), cellReference);
18326
18486
  }
18327
18487
  return cellReference;
18328
18488
  },
@@ -18338,7 +18498,7 @@
18338
18498
  ],
18339
18499
  returns: ["NUMBER"],
18340
18500
  compute: function (cellReference) {
18341
- const _cellReference = cellReference || this.__originCellXC?.();
18501
+ const _cellReference = cellReference || this.__originCellXC();
18342
18502
  assert(() => !!_cellReference, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18343
18503
  const zone = toZone(_cellReference);
18344
18504
  return zone.left + 1;
@@ -18505,7 +18665,7 @@
18505
18665
  ],
18506
18666
  returns: ["NUMBER"],
18507
18667
  compute: function (cellReference) {
18508
- cellReference = cellReference || this.__originCellXC?.();
18668
+ cellReference = cellReference || this.__originCellXC();
18509
18669
  assert(() => !!cellReference, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18510
18670
  const zone = toZone(cellReference);
18511
18671
  return zone.top + 1;
@@ -23857,31 +24017,65 @@
23857
24017
  `;
23858
24018
  class FindAndReplacePanel extends owl.Component {
23859
24019
  static template = "o-spreadsheet-FindAndReplacePanel";
23860
- state = owl.useState(this.initialState());
24020
+ static components = { SelectionInput };
23861
24021
  debounceTimeoutId;
23862
- showFormulaState = false;
24022
+ initialShowFormulaState = false;
24023
+ dataRange = "";
23863
24024
  searchInput = owl.useRef("searchInput");
24025
+ replaceInput = owl.useRef("replaceInput");
23864
24026
  get hasSearchResult() {
23865
24027
  return this.env.model.getters.getCurrentSelectedMatchIndex() !== null;
23866
24028
  }
23867
24029
  get pendingSearch() {
23868
24030
  return this.debounceTimeoutId !== undefined;
23869
24031
  }
24032
+ get searchOptions() {
24033
+ return this.env.model.getters.getSearchOptions();
24034
+ }
24035
+ get toSearch() {
24036
+ return this.searchInput.el?.value || "";
24037
+ }
24038
+ get toReplace() {
24039
+ return this.replaceInput.el?.value || "";
24040
+ }
24041
+ get allSheetsMatchesCount() {
24042
+ return _t("%s in all sheets", this.env.model.getters.getAllSheetMatchesCount());
24043
+ }
24044
+ get currentSheetMatchesCount() {
24045
+ return _t("%(matches)s in sheet %(sheetName)s", {
24046
+ matches: this.env.model.getters.getActiveSheetMatchesCount(),
24047
+ sheetName: this.env.model.getters.getSheetName(this.env.model.getters.getActiveSheetId()),
24048
+ });
24049
+ }
24050
+ get specificRangeMatchesCount() {
24051
+ const range = this.searchOptions.specificRange;
24052
+ if (!range) {
24053
+ return "";
24054
+ }
24055
+ const { _sheetId, _zone } = range;
24056
+ return _t("%(matches)s in range %(range)s of sheet %(sheetName)s", {
24057
+ matches: this.env.model.getters.getSpecificRangeMatchesCount().toString(),
24058
+ range: zoneToXc(_zone),
24059
+ sheetName: this.env.model.getters.getSheetName(_sheetId),
24060
+ });
24061
+ }
23870
24062
  setup() {
23871
- this.showFormulaState = this.env.model.getters.shouldShowFormulas();
24063
+ this.initialShowFormulaState = this.env.model.getters.shouldShowFormulas();
23872
24064
  owl.onMounted(() => this.searchInput.el?.focus());
23873
24065
  owl.onWillUnmount(() => {
23874
24066
  clearTimeout(this.debounceTimeoutId);
23875
24067
  this.env.model.dispatch("CLEAR_SEARCH");
23876
- this.env.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.showFormulaState });
24068
+ this.env.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.initialShowFormulaState });
23877
24069
  });
23878
24070
  owl.useEffect(() => {
23879
- this.state.searchOptions.searchFormulas = this.env.model.getters.shouldShowFormulas();
23880
- this.searchFormulas();
24071
+ const showFormula = this.env.model.getters.shouldShowFormulas();
24072
+ this.updateSearch({ searchFormulas: showFormula });
23881
24073
  }, () => [this.env.model.getters.shouldShowFormulas()]);
23882
24074
  }
23883
- onInput(ev) {
23884
- this.state.toSearch = ev.target.value;
24075
+ onFocusSearch() {
24076
+ this.updateDataRange();
24077
+ }
24078
+ onInput() {
23885
24079
  this.debouncedUpdateSearch();
23886
24080
  }
23887
24081
  onKeydownSearch(ev) {
@@ -23898,11 +24092,36 @@
23898
24092
  this.replace();
23899
24093
  }
23900
24094
  }
23901
- searchFormulas() {
24095
+ searchFormulas(ev) {
24096
+ const showFormula = ev.target.checked;
23902
24097
  this.env.model.dispatch("SET_FORMULA_VISIBILITY", {
23903
- show: this.state.searchOptions.searchFormulas,
24098
+ show: showFormula,
23904
24099
  });
23905
- this.updateSearch();
24100
+ this.updateSearch({ searchFormulas: showFormula });
24101
+ }
24102
+ searchExactMatch(ev) {
24103
+ const exactMatch = ev.target.checked;
24104
+ this.updateSearch({ exactMatch });
24105
+ }
24106
+ searchMatchCase(ev) {
24107
+ const matchCase = ev.target.checked;
24108
+ this.updateSearch({ matchCase });
24109
+ }
24110
+ changeSearchScope(ev) {
24111
+ const searchScope = ev.target.value;
24112
+ this.updateSearch({ searchScope });
24113
+ }
24114
+ onSearchRangeChanged(ranges) {
24115
+ this.dataRange = ranges[0];
24116
+ }
24117
+ updateDataRange() {
24118
+ if (!this.dataRange) {
24119
+ return;
24120
+ }
24121
+ if (this.searchOptions.searchScope === "specificRange") {
24122
+ const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.dataRange).rangeData;
24123
+ this.updateSearch({ specificRange });
24124
+ }
23906
24125
  }
23907
24126
  onSelectPreviousCell() {
23908
24127
  this.env.model.dispatch("SELECT_SEARCH_PREVIOUS_MATCH");
@@ -23910,10 +24129,14 @@
23910
24129
  onSelectNextCell() {
23911
24130
  this.env.model.dispatch("SELECT_SEARCH_NEXT_MATCH");
23912
24131
  }
23913
- updateSearch() {
24132
+ updateSearch(updateSearchOptions) {
24133
+ const searchOptions = {
24134
+ ...this.env.model.getters.getSearchOptions(),
24135
+ ...updateSearchOptions,
24136
+ };
23914
24137
  this.env.model.dispatch("UPDATE_SEARCH", {
23915
- toSearch: this.state.toSearch,
23916
- searchOptions: this.state.searchOptions,
24138
+ toSearch: this.toSearch,
24139
+ searchOptions,
23917
24140
  });
23918
24141
  }
23919
24142
  debouncedUpdateSearch() {
@@ -23925,28 +24148,14 @@
23925
24148
  }
23926
24149
  replace() {
23927
24150
  this.env.model.dispatch("REPLACE_SEARCH", {
23928
- replaceWith: this.state.replaceWith,
24151
+ replaceWith: this.toReplace,
23929
24152
  });
23930
24153
  }
23931
24154
  replaceAll() {
23932
24155
  this.env.model.dispatch("REPLACE_ALL_SEARCH", {
23933
- replaceWith: this.state.replaceWith,
24156
+ replaceWith: this.toReplace,
23934
24157
  });
23935
24158
  }
23936
- // ---------------------------------------------------------------------------
23937
- // Private
23938
- // ---------------------------------------------------------------------------
23939
- initialState() {
23940
- return {
23941
- toSearch: "",
23942
- replaceWith: "",
23943
- searchOptions: {
23944
- matchCase: false,
23945
- exactMatch: false,
23946
- searchFormulas: false,
23947
- },
23948
- };
23949
- }
23950
24159
  }
23951
24160
  FindAndReplacePanel.props = {
23952
24161
  onCloseSidePanel: Function,
@@ -27092,7 +27301,7 @@
27092
27301
  get cellReference() {
27093
27302
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27094
27303
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27095
- return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
27304
+ return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
27096
27305
  }
27097
27306
  get cellReferenceStyle() {
27098
27307
  const { x: left, y: top } = this.rect;
@@ -28309,6 +28518,7 @@
28309
28518
  draggerShadowThickness: 0,
28310
28519
  delta: 0,
28311
28520
  base: 0,
28521
+ position: "before",
28312
28522
  });
28313
28523
  _computeHandleDisplay(ev) {
28314
28524
  const position = this._getEvOffset(ev);
@@ -28433,11 +28643,13 @@
28433
28643
  this.state.draggerLinePosition = dimensions.start;
28434
28644
  this.state.draggerShadowPosition = dimensions.start;
28435
28645
  this.state.base = elementIndex;
28646
+ this.state.position = "before";
28436
28647
  }
28437
28648
  else if (this._getSelectedZoneEnd() < elementIndex) {
28438
28649
  this.state.draggerLinePosition = dimensions.end;
28439
28650
  this.state.draggerShadowPosition = dimensions.end - this.state.draggerShadowThickness;
28440
- this.state.base = elementIndex + 1;
28651
+ this.state.base = elementIndex;
28652
+ this.state.position = "after";
28441
28653
  }
28442
28654
  else {
28443
28655
  this.state.draggerLinePosition = startDimensions.start;
@@ -28535,22 +28747,15 @@
28535
28747
  height: 10000px;
28536
28748
  background-color: ${SELECTION_BORDER_COLOR};
28537
28749
  }
28538
- .o-unhide {
28539
- width: ${UNHIDE_ICON_EDGE_LENGTH}px;
28540
- height: ${UNHIDE_ICON_EDGE_LENGTH}px;
28541
- position: absolute;
28542
- overflow: hidden;
28543
- border-radius: 2px;
28544
- top: calc(${HEADER_HEIGHT}px / 2 - ${UNHIDE_ICON_EDGE_LENGTH}px / 2);
28750
+ .o-unhide-buttons {
28751
+ width: fit-content;
28752
+ gap: 5px;
28753
+ transform: translate(-50%, 0);
28545
28754
  }
28546
28755
  .o-unhide:hover {
28547
28756
  z-index: ${ComponentsImportance.Grid + 1};
28548
28757
  background-color: lightgrey;
28549
28758
  }
28550
- .o-unhide > svg {
28551
- position: relative;
28552
- top: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28553
- }
28554
28759
  }
28555
28760
  `;
28556
28761
  AbstractResizer.props = {
@@ -28619,6 +28824,7 @@
28619
28824
  dimension: "COL",
28620
28825
  base: this.state.base,
28621
28826
  elements,
28827
+ position: this.state.position,
28622
28828
  });
28623
28829
  if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
28624
28830
  this.env.raiseError(MergeErrorMessage);
@@ -28660,8 +28866,8 @@
28660
28866
  dimension: "COL",
28661
28867
  });
28662
28868
  }
28663
- unhideStyleValue(hiddenIndex) {
28664
- return this._getDimensionsInViewport(hiddenIndex).start;
28869
+ getUnhideButtonStyle(hiddenIndex) {
28870
+ return cssPropertiesToCss({ left: this._getDimensionsInViewport(hiddenIndex).start + "px" });
28665
28871
  }
28666
28872
  }
28667
28873
  css /* scss */ `
@@ -28707,18 +28913,9 @@
28707
28913
  height: 1px;
28708
28914
  background-color: ${SELECTION_BORDER_COLOR};
28709
28915
  }
28710
- .o-unhide {
28711
- width: ${UNHIDE_ICON_EDGE_LENGTH}px;
28712
- height: ${UNHIDE_ICON_EDGE_LENGTH}px;
28713
- position: absolute;
28714
- overflow: hidden;
28715
- border-radius: 2px;
28716
- left: calc(${HEADER_WIDTH}px - ${UNHIDE_ICON_EDGE_LENGTH}px - 2px);
28717
- }
28718
- .o-unhide > svg {
28719
- position: relative;
28720
- left: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28721
- top: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28916
+ .o-unhide-buttons {
28917
+ height: fit-content;
28918
+ transform: translate(0, -50%);
28722
28919
  }
28723
28920
  .o-unhide:hover {
28724
28921
  z-index: ${ComponentsImportance.Grid + 1};
@@ -28792,6 +28989,7 @@
28792
28989
  dimension: "ROW",
28793
28990
  base: this.state.base,
28794
28991
  elements,
28992
+ position: this.state.position,
28795
28993
  });
28796
28994
  if (!result.isSuccessful && result.reasons.includes("WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */)) {
28797
28995
  this.env.raiseError(MergeErrorMessage);
@@ -28833,8 +29031,8 @@
28833
29031
  elements: hiddenElements,
28834
29032
  });
28835
29033
  }
28836
- unhideStyleValue(hiddenIndex) {
28837
- return this._getDimensionsInViewport(hiddenIndex).start;
29034
+ getUnhideButtonStyle(hiddenIndex) {
29035
+ return cssPropertiesToCss({ top: this._getDimensionsInViewport(hiddenIndex).start + "px" });
28838
29036
  }
28839
29037
  }
28840
29038
  css /* scss */ `
@@ -28899,8 +29097,8 @@
28899
29097
  return val * (deltaMode === 0 ? 1 : DEFAULT_CELL_HEIGHT);
28900
29098
  }
28901
29099
  const onMouseWheel = (ev) => {
28902
- const deltaX = normalize(ev.shiftKey ? ev.deltaY : ev.deltaX, ev.deltaMode);
28903
- const deltaY = normalize(ev.shiftKey ? ev.deltaX : ev.deltaY, ev.deltaMode);
29100
+ const deltaX = normalize(ev.shiftKey && !isMacOS() ? ev.deltaY : ev.deltaX, ev.deltaMode);
29101
+ const deltaY = normalize(ev.shiftKey && !isMacOS() ? ev.deltaX : ev.deltaY, ev.deltaMode);
28904
29102
  handler(deltaX, deltaY);
28905
29103
  };
28906
29104
  return onMouseWheel;
@@ -29341,7 +29539,6 @@
29341
29539
  owl.onMounted(() => this.focus());
29342
29540
  this.props.exposeFocus(() => this.focus());
29343
29541
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29344
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
29345
29542
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29346
29543
  this.moveCanvas(deltaX, deltaY);
29347
29544
  this.hoveredCell.col = undefined;
@@ -29778,6 +29975,7 @@
29778
29975
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29779
29976
  const target = this.env.model.getters.getSelectedZones();
29780
29977
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29978
+ const isCutOperation = this.env.model.getters.isCutOperation();
29781
29979
  if (clipboardString === content) {
29782
29980
  // the paste actually comes from o-spreadsheet itself
29783
29981
  interactivePaste(this.env, target);
@@ -29785,7 +29983,7 @@
29785
29983
  else {
29786
29984
  interactivePasteFromOS(this.env, target, content);
29787
29985
  }
29788
- if (this.env.model.getters.isCutOperation()) {
29986
+ if (isCutOperation) {
29789
29987
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29790
29988
  }
29791
29989
  }
@@ -31398,12 +31596,6 @@
31398
31596
  }
31399
31597
  function convertExcelRangeToSheetXC(range, dataSetsHaveTitle) {
31400
31598
  let { sheetName, xc } = splitReference(range);
31401
- if (sheetName) {
31402
- sheetName = getCanonicalSheetName(sheetName) + "!";
31403
- }
31404
- else {
31405
- sheetName = "";
31406
- }
31407
31599
  let zone = toUnboundedZone(xc);
31408
31600
  if (dataSetsHaveTitle && zone.bottom !== undefined && zone.right !== undefined) {
31409
31601
  const height = zone.bottom - zone.top + 1;
@@ -31416,7 +31608,7 @@
31416
31608
  }
31417
31609
  }
31418
31610
  const dataXC = zoneToXc(zone);
31419
- return sheetName + dataXC;
31611
+ return getFullReference(sheetName, dataXC);
31420
31612
  }
31421
31613
 
31422
31614
  /**
@@ -33343,7 +33535,7 @@
33343
33535
  SPACE: goTo(State.RightRef),
33344
33536
  NUMBER: goTo(State.Found),
33345
33537
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
33346
- SYMBOL: goTo(State.Found, (token) => isColHeader(token.value)),
33538
+ SYMBOL: goTo(State.Found, (token) => isColHeader(token.value) || isRowHeader(token.value)),
33347
33539
  },
33348
33540
  [State.RightColumnRef]: {
33349
33541
  SPACE: goTo(State.RightColumnRef),
@@ -33354,6 +33546,7 @@
33354
33546
  SPACE: goTo(State.RightRowRef),
33355
33547
  NUMBER: goTo(State.Found),
33356
33548
  REFERENCE: goTo(State.Found, (token) => isSingleCellReference(token.value)),
33549
+ SYMBOL: goTo(State.Found, (token) => isRowHeader(token.value)),
33357
33550
  },
33358
33551
  [State.Found]: {},
33359
33552
  };
@@ -34079,6 +34272,14 @@
34079
34272
  // ---------------------------------------------------------------------------
34080
34273
  // Command Handling
34081
34274
  // ---------------------------------------------------------------------------
34275
+ allowDispatch(cmd) {
34276
+ switch (cmd.type) {
34277
+ case "SET_BORDER":
34278
+ return this.checkBordersUnchanged(cmd);
34279
+ default:
34280
+ return "Success" /* CommandResult.Success */;
34281
+ }
34282
+ }
34082
34283
  handle(cmd) {
34083
34284
  switch (cmd.type) {
34084
34285
  case "ADD_MERGE":
@@ -34498,6 +34699,14 @@
34498
34699
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34499
34700
  }
34500
34701
  }
34702
+ checkBordersUnchanged(cmd) {
34703
+ const currentBorder = this.getCellBorder(cmd);
34704
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
34705
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
34706
+ return "NoChanges" /* CommandResult.NoChanges */;
34707
+ }
34708
+ return "Success" /* CommandResult.Success */;
34709
+ }
34501
34710
  // ---------------------------------------------------------------------------
34502
34711
  // Import/Export
34503
34712
  // ---------------------------------------------------------------------------
@@ -35324,7 +35533,6 @@
35324
35533
  static getters = [
35325
35534
  "zoneToXC",
35326
35535
  "getCells",
35327
- "getFormulaCellContent",
35328
35536
  "getTranslatedCellFormula",
35329
35537
  "getCellStyle",
35330
35538
  "getCellById",
@@ -35353,8 +35561,9 @@
35353
35561
  allowDispatch(cmd) {
35354
35562
  switch (cmd.type) {
35355
35563
  case "UPDATE_CELL":
35564
+ return this.checkCellOutOfSheet(cmd);
35356
35565
  case "CLEAR_CELL":
35357
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35566
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35358
35567
  default:
35359
35568
  return "Success" /* CommandResult.Success */;
35360
35569
  }
@@ -35523,24 +35732,18 @@
35523
35732
  */
35524
35733
  getCellById(cellId) {
35525
35734
  // this must be as fast as possible
35526
- for (const sheetId in this.cells) {
35527
- const sheet = this.cells[sheetId];
35528
- const cell = sheet[cellId];
35529
- if (cell) {
35530
- return cell;
35531
- }
35532
- }
35533
- return undefined;
35735
+ const position = this.getters.getCellPosition(cellId);
35736
+ const sheet = this.cells[position.sheetId];
35737
+ return sheet[cellId];
35534
35738
  }
35535
35739
  /*
35536
35740
  * Reconstructs the original formula string based on a normalized form and its dependencies
35537
35741
  */
35538
35742
  getFormulaCellContent(sheetId, compiledFormula, dependencies) {
35539
- const ranges = dependencies || compiledFormula.dependencies;
35540
35743
  let rangeIndex = 0;
35541
35744
  return concat(compiledFormula.tokens.map((token) => {
35542
35745
  if (token.type === "REFERENCE") {
35543
- const range = ranges[rangeIndex++];
35746
+ const range = dependencies[rangeIndex++];
35544
35747
  return this.getters.getRangeString(range, sheetId);
35545
35748
  }
35546
35749
  return token.value;
@@ -35761,13 +35964,23 @@
35761
35964
  },
35762
35965
  };
35763
35966
  }
35764
- checkCellOutOfSheet(sheetId, col, row) {
35967
+ checkCellOutOfSheet(cmd) {
35968
+ const { sheetId, col, row } = cmd;
35765
35969
  const sheet = this.getters.tryGetSheet(sheetId);
35766
35970
  if (!sheet)
35767
35971
  return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
35768
35972
  const sheetZone = this.getters.getSheetZone(sheetId);
35769
35973
  return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */;
35770
35974
  }
35975
+ checkUselessClearCell(cmd) {
35976
+ const cell = this.getters.getCell(cmd);
35977
+ if (!cell)
35978
+ return "NoChanges" /* CommandResult.NoChanges */;
35979
+ if (!cell.content && !cell.style && !cell.format) {
35980
+ return "NoChanges" /* CommandResult.NoChanges */;
35981
+ }
35982
+ return "Success" /* CommandResult.Success */;
35983
+ }
35771
35984
  }
35772
35985
  class FormulaCellWithDependencies {
35773
35986
  id;
@@ -36457,6 +36670,9 @@
36457
36670
  return this.rules[sheetId].find((rule) => rule.id === id);
36458
36671
  }
36459
36672
  getValidationRuleForCell({ sheetId, col, row }) {
36673
+ if (!this.rules[sheetId]) {
36674
+ return undefined;
36675
+ }
36460
36676
  for (const rule of this.rules[sheetId]) {
36461
36677
  for (const range of rule.ranges) {
36462
36678
  if (isInside(col, row, range.zone)) {
@@ -37310,7 +37526,16 @@
37310
37526
  return consecutiveIndexes;
37311
37527
  }
37312
37528
  getAllVisibleHeaders(sheetId, dimension) {
37313
- return range(0, this.hiddenHeaders[sheetId][dimension].length).filter((i) => !this.hiddenHeaders[sheetId][dimension][i]);
37529
+ const headers = range(0, this.getters.getNumberHeaders(sheetId, dimension));
37530
+ const foldedHeaders = [];
37531
+ this.getters.getHeaderGroups(sheetId, dimension).forEach((group) => {
37532
+ if (group.isFolded) {
37533
+ foldedHeaders.push(...range(group.start, group.end + 1));
37534
+ }
37535
+ });
37536
+ return headers.filter((i) => {
37537
+ return !this.hiddenHeaders[sheetId][dimension][i] && !foldedHeaders.includes(i);
37538
+ });
37314
37539
  }
37315
37540
  import(data) {
37316
37541
  for (let sheet of data.sheets) {
@@ -37629,7 +37854,7 @@
37629
37854
  const rangeString = this.getters.getRangeString(expandedRange, forSheetId);
37630
37855
  if (this.isSingleCellOrMerge(rangeImpl.sheetId, rangeImpl.zone)) {
37631
37856
  const { sheetName, xc } = splitReference(rangeString);
37632
- return `${sheetName !== undefined ? getCanonicalSheetName(sheetName) + "!" : ""}${xc.split(":")[0]}`;
37857
+ return getFullReference(sheetName, xc.split(":")[0]);
37633
37858
  }
37634
37859
  return rangeString;
37635
37860
  }
@@ -39054,10 +39279,13 @@
39054
39279
  // begin with the end.
39055
39280
  rows.sort((a, b) => b - a);
39056
39281
  for (let group of groupConsecutive(rows)) {
39282
+ // indexes are sorted in the descending order
39283
+ const from = group[group.length - 1];
39284
+ const to = group[0];
39057
39285
  // Move the cells.
39058
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
39059
- // Effectively delete the element and recompute the left-right/top-bottom.
39060
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
39286
+ this.moveCellOnRowsDeletion(sheet, from, to);
39287
+ // Effectively delete the rows
39288
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39061
39289
  }
39062
39290
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39063
39291
  if (count) {
@@ -39079,8 +39307,6 @@
39079
39307
  this.addEmptyRows(sheet, quantity);
39080
39308
  // Move the cells.
39081
39309
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39082
- // Recompute the left-right/top-bottom.
39083
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39084
39310
  if (index < sheet.panes.ySplit) {
39085
39311
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39086
39312
  }
@@ -39181,35 +39407,20 @@
39181
39407
  }
39182
39408
  }
39183
39409
  }
39184
- updateRowsStructureOnDeletion(index, sheet) {
39410
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39185
39411
  const rows = [];
39186
- const cellsQueue = sheet.rows.map((row) => row.cells);
39412
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39187
39413
  for (let i in sheet.rows) {
39188
- if (Number(i) === index) {
39414
+ const row = Number(i);
39415
+ if (row >= deleteFromRow && row <= deleteToRow) {
39189
39416
  continue;
39190
39417
  }
39191
39418
  rows.push({
39192
- cells: cellsQueue.shift(),
39419
+ cells: cellsQueue.pop(),
39193
39420
  });
39194
39421
  }
39195
39422
  this.history.update("sheets", sheet.id, "rows", rows);
39196
39423
  }
39197
- /**
39198
- * Update the rows of the sheet after an addition:
39199
- * - Rename the rows
39200
- *
39201
- * @param sheet Sheet on which the deletion occurs
39202
- * @param addedRow Index of the added row
39203
- * @param rowsToAdd Number of the rows to add
39204
- */
39205
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
39206
- const rows = [];
39207
- const cellsQueue = sheet.rows.map((row) => row.cells);
39208
- sheet.rows.forEach(() => rows.push({
39209
- cells: cellsQueue.shift(),
39210
- }));
39211
- this.history.update("sheets", sheet.id, "rows", rows);
39212
- }
39213
39424
  /**
39214
39425
  * Add empty rows at the end of the rows
39215
39426
  *
@@ -39736,6 +39947,7 @@
39736
39947
  getters;
39737
39948
  computeCell;
39738
39949
  evalContext;
39950
+ rangeCache = {};
39739
39951
  constructor(context, getters, computeCell) {
39740
39952
  this.getters = getters;
39741
39953
  this.computeCell = computeCell;
@@ -39758,7 +39970,8 @@
39758
39970
  refFn(range, isMeta, functionName, paramNumber) {
39759
39971
  if (isMeta) {
39760
39972
  // Use zoneToXc of zone instead of getRangeString to avoid sending unbounded ranges
39761
- return { value: zoneToXc(range.zone) };
39973
+ const sheetName = this.getters.getSheetName(range.sheetId);
39974
+ return { value: getFullReference(sheetName, zoneToXc(range.zone)) };
39762
39975
  }
39763
39976
  if (!isZoneValid(range.zone)) {
39764
39977
  throw new InvalidReferenceError();
@@ -39783,10 +39996,13 @@
39783
39996
  if (evaluatedCell === undefined) {
39784
39997
  return { value: null, format: this.getters.getCell(position)?.format };
39785
39998
  }
39999
+ if (evaluatedCell.type === CellValueType.error) {
40000
+ throw evaluatedCell.error;
40001
+ }
39786
40002
  return evaluatedCell;
39787
40003
  }
39788
40004
  getEvaluatedCellIfNotEmpty(position) {
39789
- const evaluatedCell = this.getEvaluatedCell(position);
40005
+ const evaluatedCell = this.computeCell(position);
39790
40006
  if (evaluatedCell.type === CellValueType.empty) {
39791
40007
  const cell = this.getters.getCell(position);
39792
40008
  if (!cell || (!cell.isFormula && cell.content === "")) {
@@ -39795,13 +40011,6 @@
39795
40011
  }
39796
40012
  return evaluatedCell;
39797
40013
  }
39798
- getEvaluatedCell(position) {
39799
- const evaluatedCell = this.computeCell(position);
39800
- if (evaluatedCell.type === CellValueType.error) {
39801
- throw evaluatedCell.error;
39802
- }
39803
- return evaluatedCell;
39804
- }
39805
40014
  /**
39806
40015
  * Return the values of the cell(s) used in reference, but always in the format of a range even
39807
40016
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -39821,17 +40030,33 @@
39821
40030
  if (!_zone) {
39822
40031
  return [[]];
39823
40032
  }
40033
+ const { top, left, bottom, right } = zone;
40034
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
40035
+ if (cacheKey in this.rangeCache) {
40036
+ const result = this.rangeCache[cacheKey];
40037
+ if (result instanceof EvaluationError) {
40038
+ throw result;
40039
+ }
40040
+ return result;
40041
+ }
39824
40042
  const height = _zone.bottom - _zone.top + 1;
39825
40043
  const width = _zone.right - _zone.left + 1;
39826
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
40044
+ const matrix = new Array(width);
39827
40045
  // Performance issue: nested loop is faster than a map here
39828
40046
  for (let col = _zone.left; col <= _zone.right; col++) {
40047
+ const colIndex = col - _zone.left;
40048
+ matrix[colIndex] = new Array(height);
39829
40049
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39830
- const colIndex = col - _zone.left;
40050
+ const evaluatedCell = this.getEvaluatedCellIfNotEmpty({ sheetId, col, row });
40051
+ if (evaluatedCell?.type === CellValueType.error) {
40052
+ this.rangeCache[cacheKey] = evaluatedCell.error;
40053
+ throw evaluatedCell.error;
40054
+ }
39831
40055
  const rowIndex = row - _zone.top;
39832
40056
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39833
40057
  }
39834
40058
  }
40059
+ this.rangeCache[cacheKey] = matrix;
39835
40060
  return matrix;
39836
40061
  }
39837
40062
  }
@@ -40037,6 +40262,7 @@
40037
40262
  this.formulaDependencies().addDependencies(positionId, dependencies);
40038
40263
  }
40039
40264
  updateCompilationParameters() {
40265
+ // rebuild the compilation parameters (with a clean cache)
40040
40266
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40041
40267
  }
40042
40268
  evaluateCells(positions) {
@@ -40080,6 +40306,16 @@
40080
40306
  this.evaluatedCells = new Map();
40081
40307
  this.evaluate(this.getAllCells());
40082
40308
  }
40309
+ evaluateFormula(sheetId, formulaString) {
40310
+ const compiledFormula = compile(formulaString);
40311
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
40312
+ this.updateCompilationParameters();
40313
+ const result = updateEvalContextAndExecute({ ...compiledFormula, dependencies: ranges }, this.compilationParams, sheetId);
40314
+ if (isMatrix(result)) {
40315
+ return matrixMap(result, (cell) => cell.value);
40316
+ }
40317
+ return result.value;
40318
+ }
40083
40319
  getAllCells() {
40084
40320
  const positionIds = new JetSet();
40085
40321
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40106,6 +40342,7 @@
40106
40342
  this.nextPositionsToUpdate = cells;
40107
40343
  let currentIteration = 0;
40108
40344
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40345
+ this.updateCompilationParameters();
40109
40346
  const positionIds = Array.from(this.nextPositionsToUpdate);
40110
40347
  this.nextPositionsToUpdate.clear();
40111
40348
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40129,7 +40366,8 @@
40129
40366
  if (!this.blockedArrayFormulas.has(positionId)) {
40130
40367
  this.invalidateSpreading(positionId);
40131
40368
  }
40132
- const cell = this.getCell(positionId);
40369
+ const cellPosition = this.decodePosition(positionId);
40370
+ const cell = this.getters.getCell(cellPosition);
40133
40371
  if (cell === undefined) {
40134
40372
  return createEvaluatedCell("", { locale: this.getters.getLocale() });
40135
40373
  }
@@ -40140,7 +40378,7 @@
40140
40378
  }
40141
40379
  this.cellsBeingComputed.add(cellId);
40142
40380
  return cell.isFormula
40143
- ? this.computeFormulaCell(cell)
40381
+ ? this.computeFormulaCell(cellPosition.sheetId, cell)
40144
40382
  : evaluateLiteral(cell.content, { format: cell.format, locale: this.getters.getLocale() });
40145
40383
  }
40146
40384
  catch (e) {
@@ -40166,14 +40404,9 @@
40166
40404
  e.message = e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled);
40167
40405
  return errorCell(e);
40168
40406
  }
40169
- computeFormulaCell(cellData) {
40407
+ computeFormulaCell(sheetId, cellData) {
40170
40408
  const cellId = cellData.id;
40171
- this.compilationParams[2].__originCellXC = () => {
40172
- // compute the value lazily for performance reasons
40173
- const position = this.compilationParams[2].getters.getCellPosition(cellId);
40174
- return toXC(position.col, position.row);
40175
- };
40176
- const formulaReturn = cellData.compiledFormula.execute(cellData.compiledFormula.dependencies, ...this.compilationParams);
40409
+ const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, sheetId, cellId);
40177
40410
  if (!isMatrix(formulaReturn)) {
40178
40411
  return createEvaluatedCell(formulaReturn.value, {
40179
40412
  format: cellData.format || formulaReturn.format,
@@ -40375,6 +40608,18 @@
40375
40608
  return sheetId;
40376
40609
  }
40377
40610
  }
40611
+ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
40612
+ compilationParams[2].__originCellXC = lazy(() => {
40613
+ if (!cellId) {
40614
+ return undefined;
40615
+ }
40616
+ // compute the value lazily for performance reasons
40617
+ const position = compilationParams[2].getters.getCellPosition(cellId);
40618
+ return toXC(position.col, position.row);
40619
+ });
40620
+ compilationParams[2].__originSheetId = sheetId;
40621
+ return compiledFormula.execute(compiledFormula.dependencies, ...compilationParams);
40622
+ }
40378
40623
 
40379
40624
  //#region
40380
40625
  // ---------------------------------------------------------------------------
@@ -40469,7 +40714,6 @@
40469
40714
  // of other cells depending on it, at the next iteration.
40470
40715
  //#endregion
40471
40716
  class EvaluationPlugin extends UIPlugin {
40472
- config;
40473
40717
  static getters = [
40474
40718
  "evaluateFormula",
40475
40719
  "getCorrespondingFormulaCell",
@@ -40480,15 +40724,13 @@
40480
40724
  "getEvaluatedCells",
40481
40725
  "getEvaluatedCellsInZone",
40482
40726
  "getSpreadPositionsOf",
40727
+ "getArrayFormulaSpreadingOn",
40483
40728
  ];
40484
40729
  shouldRebuildDependenciesGraph = true;
40485
40730
  evaluator;
40486
- compilationParams;
40487
40731
  positionsToUpdate = [];
40488
40732
  constructor(config) {
40489
40733
  super(config);
40490
- this.config = config;
40491
- this.compilationParams = this.getCompilationParameters();
40492
40734
  this.evaluator = new Evaluator(config.custom, this.getters);
40493
40735
  }
40494
40736
  // ---------------------------------------------------------------------------
@@ -40513,10 +40755,6 @@
40513
40755
  case "EVALUATE_CELLS":
40514
40756
  this.evaluator.evaluateAllCells();
40515
40757
  break;
40516
- case "UPDATE_LOCALE":
40517
- this.compilationParams = this.getCompilationParameters();
40518
- this.evaluator.updateCompilationParameters();
40519
- break;
40520
40758
  }
40521
40759
  }
40522
40760
  finalize() {
@@ -40534,16 +40772,7 @@
40534
40772
  // Getters
40535
40773
  // ---------------------------------------------------------------------------
40536
40774
  evaluateFormula(sheetId, formulaString) {
40537
- const compiledFormula = compile(formulaString);
40538
- const ranges = [];
40539
- for (let xc of compiledFormula.dependencies) {
40540
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
40541
- }
40542
- const array = compiledFormula.execute(ranges, ...this.compilationParams);
40543
- if (isMatrix(array)) {
40544
- return array.map((col) => col.map((row) => row.value));
40545
- }
40546
- return array.value;
40775
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40547
40776
  }
40548
40777
  /**
40549
40778
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40592,6 +40821,9 @@
40592
40821
  getSpreadPositionsOf(position) {
40593
40822
  return this.evaluator.getSpreadPositionsOf(position);
40594
40823
  }
40824
+ getArrayFormulaSpreadingOn(position) {
40825
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40826
+ }
40595
40827
  // ---------------------------------------------------------------------------
40596
40828
  // Export
40597
40829
  // ---------------------------------------------------------------------------
@@ -40635,7 +40867,7 @@
40635
40867
  else if (cell && cell.content) {
40636
40868
  return undefined;
40637
40869
  }
40638
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40870
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40639
40871
  if (spreadingFormulaPosition === undefined) {
40640
40872
  return undefined;
40641
40873
  }
@@ -40645,9 +40877,6 @@
40645
40877
  }
40646
40878
  return undefined;
40647
40879
  }
40648
- getCompilationParameters() {
40649
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40650
- }
40651
40880
  }
40652
40881
  function isBadExpression(tokens) {
40653
40882
  try {
@@ -40924,7 +41153,7 @@
40924
41153
  // ---------------------------------------------------------------------------
40925
41154
  handle(cmd) {
40926
41155
  if (invalidateCFEvaluationCommands.has(cmd.type) ||
40927
- (cmd.type === "UPDATE_CELL" && "content" in cmd)) {
41156
+ (cmd.type === "UPDATE_CELL" && ("content" in cmd || "format" in cmd))) {
40928
41157
  this.isStale = true;
40929
41158
  }
40930
41159
  }
@@ -41250,7 +41479,7 @@
41250
41479
  handle(cmd) {
41251
41480
  if (invalidateEvaluationCommands.has(cmd.type) ||
41252
41481
  cmd.type === "EVALUATE_CELLS" ||
41253
- (cmd.type === "UPDATE_CELL" && "content" in cmd)) {
41482
+ (cmd.type === "UPDATE_CELL" && ("content" in cmd || "format" in cmd))) {
41254
41483
  this.validationResults = {};
41255
41484
  return;
41256
41485
  }
@@ -41788,19 +42017,21 @@
41788
42017
  let col = zone.left;
41789
42018
  let row = zone.bottom;
41790
42019
  if (col > 0) {
41791
- let left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
41792
- while (left.type !== CellValueType.empty) {
42020
+ let leftPosition = { sheetId, col: col - 1, row };
42021
+ while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
42022
+ this.getters.getCell(leftPosition)?.content) {
41793
42023
  row += 1;
41794
- left = this.getters.getEvaluatedCell({ sheetId, col: col - 1, row });
42024
+ leftPosition = { sheetId, col: col - 1, row };
41795
42025
  }
41796
42026
  }
41797
42027
  if (row === zone.bottom) {
41798
42028
  col = zone.right;
41799
42029
  if (col <= this.getters.getNumberCols(sheetId)) {
41800
- let right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
41801
- while (right.type !== CellValueType.empty) {
42030
+ let rightPosition = { sheetId, col: col + 1, row };
42031
+ while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
42032
+ this.getters.getCell(rightPosition)?.content) {
41802
42033
  row += 1;
41803
- right = this.getters.getEvaluatedCell({ sheetId, col: col + 1, row });
42034
+ rightPosition = { sheetId, col: col + 1, row };
41804
42035
  }
41805
42036
  }
41806
42037
  }
@@ -42792,6 +43023,7 @@
42792
43023
  isReplayingInitialRevisions = false;
42793
43024
  processedRevisions = new Set();
42794
43025
  uuidGenerator = new UuidGenerator();
43026
+ lastLocalOperation;
42795
43027
  /**
42796
43028
  * Manages the collaboration between multiple users on the same spreadsheet.
42797
43029
  * It can forward local state changes to other users to ensure they all eventually
@@ -42824,6 +43056,11 @@
42824
43056
  return;
42825
43057
  const revision = new Revision(this.uuidGenerator.uuidv4(), this.clientId, commands, rootCommand, changes, Date.now());
42826
43058
  this.revisions.append(revision.id, revision);
43059
+ // REQUEST_REDO just repeats the last operation, the
43060
+ // last operation is still the same and should not change.
43061
+ if (rootCommand.type !== "REQUEST_REDO") {
43062
+ this.lastLocalOperation = revision;
43063
+ }
42827
43064
  this.trigger("new-local-state-update", { id: revision.id });
42828
43065
  this.sendUpdateMessage({
42829
43066
  type: "REMOTE_REVISION",
@@ -42920,19 +43157,10 @@
42920
43157
  return this.pendingMessages.length === 0;
42921
43158
  }
42922
43159
  /**
42923
- * Get the last local revision whose root command isn't in the given list of ignored commands
43160
+ * Get the last local revision
42924
43161
  * */
42925
- getLastLocalNonEmptyRevision(ignoredRootCommands) {
42926
- const revisions = this.revisions.getRevertedExecution();
42927
- for (const rev of revisions) {
42928
- if (rev.rootCommand === "SNAPSHOT")
42929
- return undefined;
42930
- if (!rev.rootCommand || rev.rootCommand === "REMOTE")
42931
- continue;
42932
- if (!ignoredRootCommands.includes(rev.rootCommand?.type) && rev.commands.length)
42933
- return rev;
42934
- }
42935
- return undefined;
43162
+ getLastLocalNonEmptyRevision() {
43163
+ return this.lastLocalOperation;
42936
43164
  }
42937
43165
  _move(position) {
42938
43166
  // this method is debounced and might be called after the client
@@ -42992,7 +43220,7 @@
42992
43220
  break;
42993
43221
  case "REMOTE_REVISION":
42994
43222
  const { clientId, commands, timestamp } = message;
42995
- const revision = new Revision(message.nextRevisionId, clientId, commands, "REMOTE", undefined, timestamp);
43223
+ const revision = new Revision(message.nextRevisionId, clientId, commands, undefined, undefined, timestamp);
42996
43224
  if (revision.clientId !== this.clientId) {
42997
43225
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
42998
43226
  const pendingCommands = this.pendingMessages
@@ -43005,10 +43233,11 @@
43005
43233
  }
43006
43234
  break;
43007
43235
  case "SNAPSHOT_CREATED": {
43008
- const revision = new Revision(message.nextRevisionId, "server", [], "SNAPSHOT", undefined, Date.now());
43236
+ const revision = new Revision(message.nextRevisionId, "server", [], undefined, undefined, Date.now());
43009
43237
  this.revisions.insert(revision.id, revision, message.serverRevisionId);
43010
43238
  this.dropPendingHistoryMessages();
43011
43239
  this.trigger("snapshot");
43240
+ this.lastLocalOperation = undefined;
43012
43241
  break;
43013
43242
  }
43014
43243
  }
@@ -43346,11 +43575,26 @@
43346
43575
  let cellsInRow = [];
43347
43576
  for (let col of columnsIndex) {
43348
43577
  const position = { col, row, sheetId };
43578
+ const spreader = getters.getArrayFormulaSpreadingOn(position);
43579
+ let cell = getters.getCell(position);
43580
+ const evaluatedCell = getters.getEvaluatedCell(position);
43581
+ if (spreader) {
43582
+ const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
43583
+ const content = isSpreaderCopied
43584
+ ? ""
43585
+ : formatValue(evaluatedCell.value, { locale: getters.getLocale() });
43586
+ cell = {
43587
+ id: cell?.id || "",
43588
+ style: cell?.style,
43589
+ format: evaluatedCell.format,
43590
+ content,
43591
+ isFormula: false,
43592
+ };
43593
+ }
43349
43594
  cellsInRow.push({
43350
- cell: getters.getCell(position),
43351
- style: getters.getCellComputedStyle(position),
43352
- evaluatedCell: getters.getEvaluatedCell(position),
43595
+ cell,
43353
43596
  border: getters.getCellBorder(position) || undefined,
43597
+ evaluatedCell,
43354
43598
  position,
43355
43599
  });
43356
43600
  }
@@ -43472,13 +43716,6 @@
43472
43716
  });
43473
43717
  }
43474
43718
  this.pasteCopiedTables(target);
43475
- this.cells.forEach((row) => {
43476
- row.forEach((c) => {
43477
- if (c.cell) {
43478
- c.cell = undefined;
43479
- }
43480
- });
43481
- });
43482
43719
  }
43483
43720
  /**
43484
43721
  * The clipped zone is copied as many times as it fits in the target.
@@ -43552,7 +43789,7 @@
43552
43789
  clearClippedZones() {
43553
43790
  for (const row of this.cells) {
43554
43791
  for (const cell of row) {
43555
- if (cell.cell) {
43792
+ if (cell?.cell) {
43556
43793
  this.dispatch("CLEAR_CELL", cell.position);
43557
43794
  }
43558
43795
  }
@@ -43578,6 +43815,9 @@
43578
43815
  const rowCells = this.cells[r];
43579
43816
  for (let c = 0; c < width; c++) {
43580
43817
  const origin = rowCells[c];
43818
+ if (!origin) {
43819
+ continue;
43820
+ }
43581
43821
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43582
43822
  // TODO: refactor this part. the "Paste merge" action is also executed with
43583
43823
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43601,53 +43841,42 @@
43601
43841
  pasteCell(origin, target, operation, clipboardOption) {
43602
43842
  const { sheetId, col, row } = target;
43603
43843
  const targetCell = this.getters.getEvaluatedCell(target);
43604
- if (clipboardOption?.pasteOption !== "onlyValue") {
43605
- const targetBorders = this.getters.getCellBorder(target);
43606
- const originBorders = origin.border;
43607
- const border = {
43608
- top: targetBorders?.top || originBorders?.top,
43609
- bottom: targetBorders?.bottom || originBorders?.bottom,
43610
- left: targetBorders?.left || originBorders?.left,
43611
- right: targetBorders?.right || originBorders?.right,
43612
- };
43613
- this.dispatch("SET_BORDER", { sheetId, col, row, border });
43844
+ if (clipboardOption?.pasteOption === "onlyValue") {
43845
+ const locale = this.getters.getLocale();
43846
+ const content = formatValue(origin.evaluatedCell.value, { locale });
43847
+ this.dispatch("UPDATE_CELL", { ...target, content });
43848
+ return;
43614
43849
  }
43615
- if (origin.cell) {
43616
- if (clipboardOption?.pasteOption === "onlyFormat") {
43617
- this.dispatch("UPDATE_CELL", {
43618
- ...target,
43619
- style: origin.cell.style,
43620
- format: origin.evaluatedCell.format,
43621
- });
43622
- return;
43623
- }
43624
- if (clipboardOption?.pasteOption === "onlyValue") {
43625
- const locale = this.getters.getLocale();
43626
- const content = formatValue(origin.evaluatedCell.value, { locale });
43627
- this.dispatch("UPDATE_CELL", { ...target, content });
43628
- return;
43629
- }
43630
- let content = origin.cell.content;
43631
- if (origin.cell.isFormula && operation === "COPY") {
43632
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula);
43633
- }
43850
+ const targetBorders = this.getters.getCellBorder(target);
43851
+ const originBorders = origin.border;
43852
+ const border = {
43853
+ top: targetBorders?.top || originBorders?.top,
43854
+ bottom: targetBorders?.bottom || originBorders?.bottom,
43855
+ left: targetBorders?.left || originBorders?.left,
43856
+ right: targetBorders?.right || originBorders?.right,
43857
+ };
43858
+ this.dispatch("SET_BORDER", { sheetId, col, row, border });
43859
+ if (clipboardOption?.pasteOption === "onlyFormat") {
43860
+ this.dispatch("UPDATE_CELL", {
43861
+ ...target,
43862
+ style: origin.cell?.style ?? null,
43863
+ format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
43864
+ });
43865
+ return;
43866
+ }
43867
+ const content = origin.cell && origin.cell.isFormula && operation === "COPY"
43868
+ ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula)
43869
+ : origin.cell?.content;
43870
+ if (content !== "" || origin.cell?.format || origin.cell?.style) {
43634
43871
  this.dispatch("UPDATE_CELL", {
43635
43872
  ...target,
43636
43873
  content,
43637
- style: origin.cell.style || null,
43638
- format: origin.cell.format,
43874
+ style: origin.cell?.style || null,
43875
+ format: origin.cell?.format,
43639
43876
  });
43640
43877
  }
43641
43878
  else if (targetCell) {
43642
- if (clipboardOption?.pasteOption === "onlyValue") {
43643
- this.dispatch("UPDATE_CELL", { ...target, content: "" });
43644
- }
43645
- else if (clipboardOption?.pasteOption === "onlyFormat") {
43646
- this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" });
43647
- }
43648
- else {
43649
- this.dispatch("CLEAR_CELL", target);
43650
- }
43879
+ this.dispatch("CLEAR_CELL", target);
43651
43880
  }
43652
43881
  }
43653
43882
  /**
@@ -43709,22 +43938,28 @@
43709
43938
  return (this.cells
43710
43939
  .map((cells) => {
43711
43940
  return cells
43712
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43941
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43713
43942
  ? c.cell?.content || ""
43714
- : c.evaluatedCell?.formattedValue || "")
43943
+ : c?.evaluatedCell?.formattedValue || "")
43715
43944
  .join("\t");
43716
43945
  })
43717
43946
  .join("\n") || "\t");
43718
43947
  }
43719
43948
  getHTMLContent() {
43720
43949
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43950
+ if (!this.cells[0][0]) {
43951
+ return "";
43952
+ }
43721
43953
  return this.getters.getCellText(this.cells[0][0].position);
43722
43954
  }
43723
43955
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43724
43956
  for (const row of this.cells) {
43725
43957
  htmlTable += "<tr>";
43726
43958
  for (const cell of row) {
43727
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43959
+ if (!cell) {
43960
+ continue;
43961
+ }
43962
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43728
43963
  const cellText = this.getters.getCellText(cell.position);
43729
43964
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43730
43965
  }
@@ -44052,24 +44287,49 @@
44052
44287
  */
44053
44288
  class FindAndReplacePlugin extends UIPlugin {
44054
44289
  static layers = [3 /* LAYERS.Search */];
44055
- static getters = ["getSearchMatches", "getCurrentSelectedMatchIndex"];
44056
- searchMatches = [];
44290
+ static getters = [
44291
+ "getSearchMatches",
44292
+ "getCurrentSelectedMatchIndex",
44293
+ "getSearchOptions",
44294
+ "getAllSheetMatchesCount",
44295
+ "getActiveSheetMatchesCount",
44296
+ "getSpecificRangeMatchesCount",
44297
+ ];
44298
+ allSheetsMatches = [];
44299
+ activeSheetMatches = [];
44300
+ specificRangeMatches = [];
44301
+ // fixme: why do we make selectedMatchIndex on top of a selected
44302
+ // property in the matches?
44057
44303
  selectedMatchIndex = null;
44058
44304
  currentSearchRegex = null;
44059
44305
  searchOptions = {
44060
44306
  matchCase: false,
44061
44307
  exactMatch: false,
44062
44308
  searchFormulas: false,
44309
+ searchScope: "allSheets",
44310
+ specificRange: undefined,
44063
44311
  };
44064
44312
  toSearch = "";
44065
44313
  isSearchDirty = false;
44314
+ get searchMatches() {
44315
+ switch (this.searchOptions.searchScope) {
44316
+ case "allSheets":
44317
+ return this.allSheetsMatches;
44318
+ case "activeSheet":
44319
+ return this.activeSheetMatches;
44320
+ case "specificRange":
44321
+ return this.specificRangeMatches;
44322
+ }
44323
+ }
44066
44324
  // ---------------------------------------------------------------------------
44067
44325
  // Command Handling
44068
44326
  // ---------------------------------------------------------------------------
44069
44327
  handle(cmd) {
44070
44328
  switch (cmd.type) {
44071
44329
  case "UPDATE_SEARCH":
44072
- this.updateSearch(cmd.toSearch, cmd.searchOptions);
44330
+ const rangeData = cmd.searchOptions.specificRange;
44331
+ const specificRange = rangeData && this.getters.getRangeFromRangeData(rangeData);
44332
+ this.updateSearch(cmd.toSearch, { ...cmd.searchOptions, specificRange });
44073
44333
  break;
44074
44334
  case "CLEAR_SEARCH":
44075
44335
  this.clearSearch();
@@ -44099,7 +44359,9 @@
44099
44359
  this.isSearchDirty = true;
44100
44360
  break;
44101
44361
  case "ACTIVATE_SHEET":
44102
- this.refreshSearch();
44362
+ if (this.searchOptions.searchScope === "activeSheet") {
44363
+ this.isSearchDirty = true;
44364
+ }
44103
44365
  break;
44104
44366
  }
44105
44367
  }
@@ -44118,6 +44380,18 @@
44118
44380
  getCurrentSelectedMatchIndex() {
44119
44381
  return this.selectedMatchIndex;
44120
44382
  }
44383
+ getSearchOptions() {
44384
+ return { ...this.searchOptions, specificRange: this.searchOptions.specificRange?.rangeData };
44385
+ }
44386
+ getAllSheetMatchesCount() {
44387
+ return this.allSheetsMatches.length;
44388
+ }
44389
+ getActiveSheetMatchesCount() {
44390
+ return this.activeSheetMatches.length;
44391
+ }
44392
+ getSpecificRangeMatchesCount() {
44393
+ return this.specificRangeMatches.length;
44394
+ }
44121
44395
  // ---------------------------------------------------------------------------
44122
44396
  // Search
44123
44397
  // ---------------------------------------------------------------------------
@@ -44138,8 +44412,8 @@
44138
44412
  * refresh the matches according to the current search options
44139
44413
  */
44140
44414
  refreshSearch() {
44141
- const matches = this.findMatches();
44142
- this.searchMatches = matches;
44415
+ this.selectedMatchIndex = null;
44416
+ this.findMatches();
44143
44417
  this.selectNextCell(Direction.current);
44144
44418
  }
44145
44419
  /**
@@ -44154,45 +44428,65 @@
44154
44428
  }
44155
44429
  this.currentSearchRegex = RegExp(searchValue, flags);
44156
44430
  }
44431
+ getSheetsInSearchOrder() {
44432
+ switch (this.searchOptions.searchScope) {
44433
+ case "allSheets":
44434
+ const sheetIds = this.getters.getSheetIds();
44435
+ const activeSheetIndex = sheetIds.findIndex((id) => id === this.getters.getActiveSheetId());
44436
+ return [
44437
+ sheetIds[activeSheetIndex],
44438
+ ...sheetIds.slice(activeSheetIndex + 1),
44439
+ ...sheetIds.slice(0, activeSheetIndex),
44440
+ ];
44441
+ case "activeSheet":
44442
+ return [this.getters.getActiveSheetId()];
44443
+ case "specificRange":
44444
+ const specificRange = this.searchOptions.specificRange;
44445
+ if (!specificRange) {
44446
+ return [];
44447
+ }
44448
+ return specificRange ? [specificRange.sheetId] : [];
44449
+ }
44450
+ }
44157
44451
  /**
44158
44452
  * Find matches using the current regex
44159
44453
  */
44160
44454
  findMatches() {
44161
- const sheetId = this.getters.getActiveSheetId();
44162
- const cells = this.getters.getCells(sheetId);
44163
44455
  const matches = [];
44164
- if (this.toSearch && this.currentSearchRegex) {
44165
- for (const cell of Object.values(cells)) {
44166
- const { col, row } = this.getters.getCellPosition(cell.id);
44167
- const cellPosition = { sheetId, col, row };
44456
+ if (this.toSearch) {
44457
+ for (const sheetId of this.getters.getSheetIds()) {
44458
+ matches.push(...this.findMatchesInSheet(sheetId));
44459
+ }
44460
+ }
44461
+ // set results
44462
+ this.allSheetsMatches = matches;
44463
+ this.activeSheetMatches = matches.filter((match) => match.sheetId === this.getters.getActiveSheetId());
44464
+ if (this.searchOptions.specificRange) {
44465
+ const { sheetId, zone } = this.searchOptions.specificRange;
44466
+ this.specificRangeMatches = matches.filter((match) => match.sheetId === sheetId && isInside(match.col, match.row, zone));
44467
+ }
44468
+ else {
44469
+ this.specificRangeMatches = [];
44470
+ }
44471
+ }
44472
+ findMatchesInSheet(sheetId) {
44473
+ const matches = [];
44474
+ const { left, right, top, bottom } = this.getters.getSheetZone(sheetId);
44475
+ for (let row = top; row <= bottom; row++) {
44476
+ for (let col = left; col <= right; col++) {
44168
44477
  const isColHidden = this.getters.isColHidden(sheetId, col);
44169
44478
  const isRowHidden = this.getters.isRowHidden(sheetId, row);
44170
44479
  if (isColHidden || isRowHidden) {
44171
44480
  continue;
44172
44481
  }
44173
- if (this.currentSearchRegex.test(this.getSearchableString(cellPosition))) {
44174
- const match = { col, row, selected: false };
44482
+ const cellPosition = { sheetId, col, row };
44483
+ if (this.currentSearchRegex?.test(this.getSearchableString(cellPosition))) {
44484
+ const match = { sheetId, col, row };
44175
44485
  matches.push(match);
44176
44486
  }
44177
- for (const spreadPosition of this.getters.getSpreadPositionsOf(cellPosition)) {
44178
- if (this.currentSearchRegex.test(this.getSearchableString(spreadPosition))) {
44179
- const match = {
44180
- col: spreadPosition.col,
44181
- row: spreadPosition.row,
44182
- selected: false,
44183
- };
44184
- matches.push(match);
44185
- }
44186
- }
44187
44487
  }
44188
44488
  }
44189
- return matches.sort(this.sortByRowThenColumn);
44190
- }
44191
- sortByRowThenColumn(a, b) {
44192
- if (a.row === b.row) {
44193
- return a.col - b.col;
44194
- }
44195
- return a.row > b.row ? 1 : -1;
44489
+ return matches;
44196
44490
  }
44197
44491
  /**
44198
44492
  * Changes the selected search cell. Given a direction it will
@@ -44210,30 +44504,47 @@
44210
44504
  }
44211
44505
  let nextIndex;
44212
44506
  if (this.selectedMatchIndex === null) {
44213
- nextIndex = 0;
44507
+ let nextMatchIndex = -1;
44508
+ // if search is not available in current sheet will select in next sheet
44509
+ for (const sheetId of this.getSheetsInSearchOrder()) {
44510
+ nextMatchIndex = matches.findIndex((match) => match.sheetId === sheetId);
44511
+ if (nextMatchIndex !== -1) {
44512
+ break;
44513
+ }
44514
+ }
44515
+ nextIndex = nextMatchIndex;
44214
44516
  }
44215
44517
  else {
44216
44518
  nextIndex = this.selectedMatchIndex + indexChange;
44217
44519
  }
44218
- //modulo of negative value to be able to cycle in both directions with previous and next
44219
- nextIndex = ((nextIndex % matches.length) + matches.length) % matches.length;
44520
+ // loop index value inside the array (index -1 => last index)
44521
+ nextIndex = (nextIndex + matches.length) % matches.length;
44220
44522
  this.selectedMatchIndex = nextIndex;
44523
+ const selectedMatch = matches[nextIndex];
44524
+ // Switch to the sheet where the match is located
44525
+ if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
44526
+ this.dispatch("ACTIVATE_SHEET", {
44527
+ sheetIdFrom: this.getters.getActiveSheetId(),
44528
+ sheetIdTo: selectedMatch.sheetId,
44529
+ });
44530
+ }
44221
44531
  // we want grid selection to capture the selection stream
44222
44532
  this.selection.getBackToDefault();
44223
- this.selection.selectCell(matches[nextIndex].col, matches[nextIndex].row);
44224
- for (let index = 0; index < this.searchMatches.length; index++) {
44225
- this.searchMatches[index].selected = index === this.selectedMatchIndex;
44226
- }
44533
+ this.selection.selectCell(selectedMatch.col, selectedMatch.row);
44227
44534
  }
44228
44535
  clearSearch() {
44229
44536
  this.toSearch = "";
44230
- this.searchMatches = [];
44231
44537
  this.selectedMatchIndex = null;
44232
44538
  this.currentSearchRegex = null;
44539
+ this.allSheetsMatches = [];
44540
+ this.activeSheetMatches = [];
44541
+ this.specificRangeMatches = [];
44233
44542
  this.searchOptions = {
44234
44543
  matchCase: false,
44235
44544
  exactMatch: false,
44236
44545
  searchFormulas: false,
44546
+ searchScope: "allSheets",
44547
+ specificRange: undefined,
44237
44548
  };
44238
44549
  }
44239
44550
  // ---------------------------------------------------------------------------
@@ -44243,9 +44554,7 @@
44243
44554
  if (!this.currentSearchRegex) {
44244
44555
  return;
44245
44556
  }
44246
- const sheetId = this.getters.getActiveSheetId();
44247
- const position = { sheetId, ...selectedMatch };
44248
- const cell = this.getters.getCell(position);
44557
+ const cell = this.getters.getCell(selectedMatch);
44249
44558
  if (!cell?.content) {
44250
44559
  return;
44251
44560
  }
@@ -44253,10 +44562,10 @@
44253
44562
  return;
44254
44563
  }
44255
44564
  const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
44256
- const toReplace = this.getSearchableString(position);
44565
+ const toReplace = this.getSearchableString(selectedMatch);
44257
44566
  const content = toReplace.replace(replaceRegex, replaceWith);
44258
44567
  const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
44259
- this.dispatch("UPDATE_CELL", { ...position, content: canonicalContent });
44568
+ this.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
44260
44569
  }
44261
44570
  /**
44262
44571
  * Replace the value of the currently selected match
@@ -44287,7 +44596,10 @@
44287
44596
  drawGrid(renderingContext) {
44288
44597
  const { ctx } = renderingContext;
44289
44598
  const sheetId = this.getters.getActiveSheetId();
44290
- for (const match of this.searchMatches) {
44599
+ for (const [index, match] of this.searchMatches.entries()) {
44600
+ if (match.sheetId !== sheetId) {
44601
+ continue; // Skip drawing matches from other sheets
44602
+ }
44291
44603
  const merge = this.getters.getMerge({ sheetId, col: match.col, row: match.row });
44292
44604
  const left = merge ? merge.left : match.col;
44293
44605
  const right = merge ? merge.right : match.col;
@@ -44297,12 +44609,21 @@
44297
44609
  if (width > 0 && height > 0) {
44298
44610
  ctx.fillStyle = BACKGROUND_COLOR;
44299
44611
  ctx.fillRect(x, y, width, height);
44300
- if (match.selected) {
44612
+ if (index === this.selectedMatchIndex) {
44301
44613
  ctx.strokeStyle = BORDER_COLOR;
44302
44614
  ctx.strokeRect(x, y, width, height);
44303
44615
  }
44304
44616
  }
44305
44617
  }
44618
+ if (this.searchOptions.searchScope === "specificRange") {
44619
+ const range = this.searchOptions.specificRange;
44620
+ if (!range || range.sheetId !== sheetId) {
44621
+ return;
44622
+ }
44623
+ const { x, y, width, height } = this.getters.getVisibleRect(range.zone);
44624
+ ctx.strokeStyle = BORDER_COLOR;
44625
+ ctx.strokeRect(x, y, width, height);
44626
+ }
44306
44627
  }
44307
44628
  }
44308
44629
 
@@ -45561,6 +45882,9 @@
45561
45882
  // Other
45562
45883
  // ---------------------------------------------------------------------------
45563
45884
  initInput(id, initialRanges, inputHasSingleRange = false) {
45885
+ if (this.inputs[id]) {
45886
+ this.unfocus();
45887
+ }
45564
45888
  this.inputs[id] = new SelectionInputPlugin(this.config, initialRanges, inputHasSingleRange);
45565
45889
  if (initialRanges.length === 0) {
45566
45890
  const input = this.inputs[id];
@@ -46269,7 +46593,7 @@
46269
46593
  * Ignore standard undo/redo revisions (that are empty)
46270
46594
  */
46271
46595
  getPossibleRevisionToRepeat() {
46272
- return this.session.getLastLocalNonEmptyRevision(["REQUEST_REDO"]);
46596
+ return this.session.getLastLocalNonEmptyRevision();
46273
46597
  }
46274
46598
  }
46275
46599
 
@@ -46768,11 +47092,14 @@
46768
47092
  }
46769
47093
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46770
47094
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
47095
+ if (this.state.operation === "CUT") {
47096
+ this.state = undefined;
47097
+ }
46771
47098
  this.lastPasteState = this.state;
46772
47099
  if (this.paintFormatStatus === "oneOff") {
46773
47100
  this.paintFormatStatus = "inactive";
46774
- this.status = "invisible";
46775
47101
  }
47102
+ this.status = "invisible";
46776
47103
  break;
46777
47104
  case "COPY_PASTE_CELLS_ABOVE":
46778
47105
  {
@@ -46999,92 +47326,6 @@
46999
47326
  }
47000
47327
  }
47001
47328
 
47002
- /**
47003
- * Change the reference types inside the given token, if the token represent a range or a cell
47004
- *
47005
- * Eg. :
47006
- * A1 => $A$1 => A$1 => $A1 => A1
47007
- * A1:$B$1 => $A$1:B$1 => A$1:$B1 => $A1:B1 => A1:$B$1
47008
- */
47009
- function loopThroughReferenceType(token) {
47010
- if (token.type !== "REFERENCE")
47011
- return token;
47012
- const { xc, sheetName } = splitReference(token.value);
47013
- const [left, right] = xc.split(":");
47014
- const sheetRef = sheetName ? `${getCanonicalSheetName(sheetName)}!` : "";
47015
- const updatedLeft = getTokenNextReferenceType(left);
47016
- const updatedRight = right ? `:${getTokenNextReferenceType(right)}` : "";
47017
- return { ...token, value: sheetRef + updatedLeft + updatedRight };
47018
- }
47019
- /**
47020
- * Get a new token with a changed type of reference from the given cell token symbol.
47021
- * Undefined behavior if given a token other than a cell or if the Xc contains a sheet reference
47022
- *
47023
- * A1 => $A$1 => A$1 => $A1 => A1
47024
- */
47025
- function getTokenNextReferenceType(xc) {
47026
- switch (getReferenceType(xc)) {
47027
- case "none":
47028
- xc = setXcToReferenceType(xc, "colrow");
47029
- break;
47030
- case "colrow":
47031
- xc = setXcToReferenceType(xc, "row");
47032
- break;
47033
- case "row":
47034
- xc = setXcToReferenceType(xc, "col");
47035
- break;
47036
- case "col":
47037
- xc = setXcToReferenceType(xc, "none");
47038
- break;
47039
- }
47040
- return xc;
47041
- }
47042
- /**
47043
- * Returns the given XC with the given reference type.
47044
- */
47045
- function setXcToReferenceType(xc, referenceType) {
47046
- xc = xc.replace(/\$/g, "");
47047
- let indexOfNumber;
47048
- switch (referenceType) {
47049
- case "col":
47050
- return "$" + xc;
47051
- case "row":
47052
- indexOfNumber = xc.search(/[0-9]/);
47053
- return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
47054
- case "colrow":
47055
- indexOfNumber = xc.search(/[0-9]/);
47056
- xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
47057
- return "$" + xc;
47058
- case "none":
47059
- return xc;
47060
- }
47061
- }
47062
- /**
47063
- * Return the type of reference used in the given XC of a cell.
47064
- * Undefined behavior if the XC have a sheet reference
47065
- */
47066
- function getReferenceType(xcCell) {
47067
- if (isColAndRowFixed(xcCell)) {
47068
- return "colrow";
47069
- }
47070
- else if (isColFixed(xcCell)) {
47071
- return "col";
47072
- }
47073
- else if (isRowFixed(xcCell)) {
47074
- return "row";
47075
- }
47076
- return "none";
47077
- }
47078
- function isColFixed(xc) {
47079
- return xc.startsWith("$");
47080
- }
47081
- function isRowFixed(xc) {
47082
- return xc.includes("$", 1);
47083
- }
47084
- function isColAndRowFixed(xc) {
47085
- return xc.startsWith("$") && xc.length > 1 && xc.slice(1).includes("$");
47086
- }
47087
-
47088
47329
  const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
47089
47330
  class EditionPlugin extends UIPlugin {
47090
47331
  static getters = [
@@ -47823,6 +48064,12 @@
47823
48064
  break;
47824
48065
  case "HIDE_COLUMNS_ROWS":
47825
48066
  case "UNHIDE_COLUMNS_ROWS":
48067
+ case "GROUP_HEADERS":
48068
+ case "UNGROUP_HEADERS":
48069
+ case "FOLD_HEADER_GROUP":
48070
+ case "UNFOLD_HEADER_GROUP":
48071
+ case "FOLD_ALL_HEADER_GROUPS":
48072
+ case "UNFOLD_ALL_HEADER_GROUPS":
47826
48073
  this.updateHiddenRows();
47827
48074
  break;
47828
48075
  case "UPDATE_FILTER":
@@ -48002,32 +48249,32 @@
48002
48249
  {
48003
48250
  name: _t("Sum"),
48004
48251
  types: [CellValueType.number],
48005
- compute: (values, locale) => SUM.compute.bind({ locale })([values]),
48252
+ compute: (values, locale) => sum([[values]], locale),
48006
48253
  },
48007
48254
  {
48008
48255
  name: _t("Avg"),
48009
48256
  types: [CellValueType.number],
48010
- compute: (values, locale) => AVERAGE.compute.bind({ locale })([values]),
48257
+ compute: (values, locale) => average([[values]], locale),
48011
48258
  },
48012
48259
  {
48013
48260
  name: _t("Min"),
48014
48261
  types: [CellValueType.number],
48015
- compute: (values, locale) => MIN.compute.bind({ locale })([values]),
48262
+ compute: (values, locale) => min([[values]], locale),
48016
48263
  },
48017
48264
  {
48018
48265
  name: _t("Max"),
48019
48266
  types: [CellValueType.number],
48020
- compute: (values, locale) => MAX.compute.bind({ locale })([values]),
48267
+ compute: (values, locale) => max([[values]], locale),
48021
48268
  },
48022
48269
  {
48023
48270
  name: _t("Count"),
48024
48271
  types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
48025
- compute: (values, locale) => COUNTA.compute.bind({ locale })([values]),
48272
+ compute: (values) => countAny([[values]]),
48026
48273
  },
48027
48274
  {
48028
48275
  name: _t("Count Numbers"),
48029
48276
  types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
48030
- compute: (values, locale) => COUNT.compute.bind({ locale })([values]),
48277
+ compute: (values, locale) => countNumbers([[values]], locale),
48031
48278
  },
48032
48279
  ];
48033
48280
  /**
@@ -48524,7 +48771,7 @@
48524
48771
  sheetId: cmd.sheetId,
48525
48772
  base: cmd.base,
48526
48773
  quantity: thickness,
48527
- position: "before",
48774
+ position: cmd.position,
48528
48775
  });
48529
48776
  const isCol = cmd.dimension === "COL";
48530
48777
  const start = cmd.elements[0];
@@ -48541,12 +48788,13 @@
48541
48788
  },
48542
48789
  ];
48543
48790
  const state = new ClipboardCellsState(target, "CUT", this.getters, this.dispatch, this.selection);
48791
+ const base = isBasedBefore ? cmd.base : cmd.base + 1;
48544
48792
  const pasteTarget = [
48545
48793
  {
48546
- left: isCol ? cmd.base : 0,
48547
- right: isCol ? cmd.base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
48548
- top: !isCol ? cmd.base : 0,
48549
- bottom: !isCol ? cmd.base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
48794
+ left: isCol ? base : 0,
48795
+ right: isCol ? base + thickness - 1 : this.getters.getNumberCols(cmd.sheetId) - 1,
48796
+ top: !isCol ? base : 0,
48797
+ bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
48550
48798
  },
48551
48799
  ];
48552
48800
  state.paste(pasteTarget, { selectTarget: true });
@@ -48581,6 +48829,11 @@
48581
48829
  doesElementsHaveCommonMerges(id, cmd.base - 1, cmd.base)) {
48582
48830
  return "WillRemoveExistingMerge" /* CommandResult.WillRemoveExistingMerge */;
48583
48831
  }
48832
+ const headers = [cmd.base, ...cmd.elements];
48833
+ const maxHeaderValue = isCol ? this.getters.getNumberCols(id) : this.getters.getNumberRows(id);
48834
+ if (headers.some((h) => h < 0 || h >= maxHeaderValue)) {
48835
+ return "InvalidHeaderIndex" /* CommandResult.InvalidHeaderIndex */;
48836
+ }
48584
48837
  return "Success" /* CommandResult.Success */;
48585
48838
  }
48586
48839
  //-------------------------------------------
@@ -48758,9 +49011,6 @@
48758
49011
  */
48759
49012
  adjustPosition(position) {
48760
49013
  const sheetId = this.sheetId;
48761
- if (!position) {
48762
- position = this.getters.getSheetPosition(sheetId);
48763
- }
48764
49014
  const mainCellPosition = this.getters.getMainCellPosition({ sheetId, ...position });
48765
49015
  const { col, row } = this.getters.getNextVisibleCellPosition(mainCellPosition);
48766
49016
  if (isInside(col, this.boundaries.top, this.boundaries)) {
@@ -48773,56 +49023,50 @@
48773
49023
  adjustPositionX(targetCol) {
48774
49024
  const sheetId = this.sheetId;
48775
49025
  const { end } = this.getters.getColDimensions(sheetId, targetCol);
48776
- const maxCol = this.getters.getNumberCols(sheetId);
48777
49026
  if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
49027
+ const maxCol = this.getters.getNumberCols(sheetId);
48778
49028
  let finalTarget = targetCol;
48779
- while (this.getters.isColHidden(sheetId, finalTarget) && targetCol < maxCol) {
49029
+ while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget < maxCol) {
48780
49030
  finalTarget++;
48781
49031
  }
48782
49032
  const finalTargetEnd = this.getters.getColDimensions(sheetId, finalTarget).end;
48783
49033
  const startIndex = this.searchHeaderIndex("COL", finalTargetEnd - this.viewportWidth - this.offsetCorrectionX, this.boundaries.left);
48784
- this.offsetX =
49034
+ this.offsetScrollbarX =
48785
49035
  this.getters.getColDimensions(sheetId, startIndex).end - this.offsetCorrectionX;
48786
- this.offsetScrollbarX = this.offsetX;
48787
- this.adjustViewportZoneX();
48788
49036
  }
48789
49037
  else if (this.left > targetCol) {
48790
49038
  let finalTarget = targetCol;
48791
- while (this.getters.isColHidden(sheetId, finalTarget) && targetCol > 0) {
49039
+ while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget > 0) {
48792
49040
  finalTarget--;
48793
49041
  }
48794
- this.offsetX =
49042
+ this.offsetScrollbarX =
48795
49043
  this.getters.getColDimensions(sheetId, finalTarget).start - this.offsetCorrectionX;
48796
- this.offsetScrollbarX = this.offsetX;
48797
- this.adjustViewportZoneX();
48798
49044
  }
49045
+ this.adjustViewportZoneX();
48799
49046
  }
48800
49047
  adjustPositionY(targetRow) {
48801
49048
  const sheetId = this.sheetId;
48802
49049
  const { end } = this.getters.getRowDimensions(sheetId, targetRow);
48803
- const maxRow = this.getters.getNumberRows(sheetId);
48804
49050
  if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
49051
+ const maxRow = this.getters.getNumberRows(sheetId);
48805
49052
  let finalTarget = targetRow;
48806
- while (this.getters.isRowHidden(sheetId, finalTarget) && targetRow < maxRow) {
49053
+ while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget < maxRow) {
48807
49054
  finalTarget++;
48808
49055
  }
48809
49056
  const finalTargetEnd = this.getters.getRowDimensions(sheetId, finalTarget).end;
48810
49057
  const startIndex = this.searchHeaderIndex("ROW", finalTargetEnd - this.viewportHeight - this.offsetCorrectionY, this.boundaries.top);
48811
- this.offsetY =
49058
+ this.offsetScrollbarY =
48812
49059
  this.getters.getRowDimensions(sheetId, startIndex).end - this.offsetCorrectionY;
48813
- this.offsetScrollbarY = this.offsetY;
48814
- this.adjustViewportZoneY();
48815
49060
  }
48816
49061
  else if (this.top > targetRow) {
48817
49062
  let finalTarget = targetRow;
48818
- while (this.getters.isRowHidden(sheetId, finalTarget) && targetRow > 0) {
49063
+ while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget > 0) {
48819
49064
  finalTarget--;
48820
49065
  }
48821
- this.offsetY =
49066
+ this.offsetScrollbarY =
48822
49067
  this.getters.getRowDimensions(sheetId, finalTarget).start - this.offsetCorrectionY;
48823
- this.offsetScrollbarY = this.offsetY;
48824
- this.adjustViewportZoneY();
48825
49068
  }
49069
+ this.adjustViewportZoneY();
48826
49070
  }
48827
49071
  setViewportOffset(offsetX, offsetY) {
48828
49072
  this.setViewportOffsetX(offsetX);
@@ -48838,11 +49082,10 @@
48838
49082
  * @returns Computes the absolute coordinate of a given zone inside the viewport
48839
49083
  */
48840
49084
  getRect(zone) {
48841
- const targetZone = intersection(zone, this.zone);
49085
+ const targetZone = intersection(zone, this);
48842
49086
  if (targetZone) {
48843
- const x = this.getters.getColRowOffset("COL", this.zone.left, targetZone.left) +
48844
- this.offsetCorrectionX;
48845
- const y = this.getters.getColRowOffset("ROW", this.zone.top, targetZone.top) + this.offsetCorrectionY;
49087
+ const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
49088
+ const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
48846
49089
  const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
48847
49090
  const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
48848
49091
  return {
@@ -48852,9 +49095,7 @@
48852
49095
  height,
48853
49096
  };
48854
49097
  }
48855
- else {
48856
- return undefined;
48857
- }
49098
+ return undefined;
48858
49099
  }
48859
49100
  isVisible(col, row) {
48860
49101
  const isInside = row <= this.bottom && row >= this.top && col >= this.left && col <= this.right;
@@ -48862,7 +49103,6 @@
48862
49103
  !this.getters.isColHidden(this.sheetId, col) &&
48863
49104
  !this.getters.isRowHidden(this.sheetId, row));
48864
49105
  }
48865
- // PRIVATE
48866
49106
  searchHeaderIndex(dimension, position, startIndex = 0) {
48867
49107
  const sheetId = this.sheetId;
48868
49108
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
@@ -48885,9 +49125,6 @@
48885
49125
  }
48886
49126
  return -1;
48887
49127
  }
48888
- get zone() {
48889
- return { left: this.left, right: this.right, top: this.top, bottom: this.bottom };
48890
- }
48891
49128
  setViewportOffsetX(offsetX) {
48892
49129
  if (!this.canScrollHorizontally) {
48893
49130
  return;
@@ -48912,11 +49149,6 @@
48912
49149
  this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
48913
49150
  }
48914
49151
  }
48915
- this.left = this.getColIndex(this.offsetScrollbarX);
48916
- this.right = this.getColIndex(this.offsetScrollbarX + this.viewportWidth);
48917
- if (this.right === -1) {
48918
- this.right = this.boundaries.right;
48919
- }
48920
49152
  this.adjustViewportZoneX();
48921
49153
  }
48922
49154
  /** Corrects the viewport's vertical offset based on the current structure
@@ -48929,11 +49161,6 @@
48929
49161
  this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
48930
49162
  }
48931
49163
  }
48932
- this.top = this.getRowIndex(this.offsetScrollbarY);
48933
- this.bottom = this.getRowIndex(this.offsetScrollbarY + this.viewportHeight);
48934
- if (this.bottom === -1) {
48935
- this.bottom = this.boundaries.bottom;
48936
- }
48937
49164
  this.adjustViewportZoneY();
48938
49165
  }
48939
49166
  /** Updates the pane zone and snapped offset based on its horizontal
@@ -49311,7 +49538,7 @@
49311
49538
  isVisibleInViewport({ sheetId, col, row }) {
49312
49539
  return this.getSubViewports(sheetId).some((pane) => pane.isVisible(col, row));
49313
49540
  }
49314
- // => return s the new offset
49541
+ // => returns the new offset
49315
49542
  getEdgeScrollCol(x, previousX, startingX) {
49316
49543
  let canEdgeScroll = false;
49317
49544
  let direction = 0;
@@ -49626,10 +49853,13 @@
49626
49853
  }
49627
49854
  break;
49628
49855
  case "UPDATE_CELL":
49629
- if ("content" in cmd || "format" in cmd || cmd.style?.fontSize !== undefined) {
49856
+ if ("content" in cmd || "format" in cmd) {
49630
49857
  this.headerPositions = {};
49631
49858
  this.isDirty = true;
49632
49859
  }
49860
+ else {
49861
+ this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
49862
+ }
49633
49863
  break;
49634
49864
  case "UPDATE_FILTER":
49635
49865
  case "REMOVE_FILTER_TABLE":
@@ -51786,6 +52016,8 @@
51786
52016
  *:before,
51787
52017
  *:after {
51788
52018
  box-sizing: content-box;
52019
+ /** rtl not supported ATM */
52020
+ direction: ltr;
51789
52021
  }
51790
52022
  .o-separator {
51791
52023
  border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
@@ -51942,6 +52174,7 @@
51942
52174
  };
51943
52175
  sidePanel;
51944
52176
  composer;
52177
+ spreadsheetRef = owl.useRef("spreadsheet");
51945
52178
  _focusGrid;
51946
52179
  keyDownMapping;
51947
52180
  isViewportTooSmall = false;
@@ -51976,6 +52209,18 @@
51976
52209
  clipboard: this.env.clipboard || instantiateClipboard(),
51977
52210
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
51978
52211
  });
52212
+ owl.useEffect(() => {
52213
+ /**
52214
+ * Only refocus the grid if the active element is not a child of the spreadsheet
52215
+ * (i.e. activeElement is outside of the spreadsheetRef component)
52216
+ * and spreadsheet is a child of that element. Anything else means that the focus
52217
+ * is on an element that needs to keep it.
52218
+ */
52219
+ if (!this.spreadsheetRef.el.contains(document.activeElement) &&
52220
+ document.activeElement?.contains(this.spreadsheetRef.el)) {
52221
+ this.focusGrid();
52222
+ }
52223
+ }, () => [this.env.model.getters.getActiveSheetId()]);
51979
52224
  owl.useExternalListener(window, "resize", () => this.render(true));
51980
52225
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
51981
52226
  this.bindModelEvents();
@@ -52057,7 +52302,7 @@
52057
52302
  }
52058
52303
  focusGrid() {
52059
52304
  if (!this._focusGrid) {
52060
- throw new Error("_focusGrid should be exposed by the grid component");
52305
+ return;
52061
52306
  }
52062
52307
  this._focusGrid();
52063
52308
  }
@@ -55450,19 +55695,22 @@
55450
55695
  * Check if the given command is allowed by all the plugins and the history.
55451
55696
  */
55452
55697
  checkDispatchAllowed(command) {
55453
- if (isCoreCommand(command)) {
55454
- return this.checkDispatchAllowedCoreCommand(command);
55698
+ const results = isCoreCommand(command)
55699
+ ? this.checkDispatchAllowedCoreCommand(command)
55700
+ : this.checkDispatchAllowedLocalCommand(command);
55701
+ if (results.some((r) => r !== "Success" /* CommandResult.Success */)) {
55702
+ return new DispatchResult(results.flat());
55455
55703
  }
55456
- return this.checkDispatchAllowedLocalCommand(command);
55704
+ return DispatchResult.Success;
55457
55705
  }
55458
55706
  checkDispatchAllowedCoreCommand(command) {
55459
55707
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55460
55708
  results.push(this.range.allowDispatch(command));
55461
- return new DispatchResult(results.flat());
55709
+ return results;
55462
55710
  }
55463
55711
  checkDispatchAllowedLocalCommand(command) {
55464
55712
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55465
- return new DispatchResult(results.flat());
55713
+ return results;
55466
55714
  }
55467
55715
  finalize() {
55468
55716
  this.status = 3 /* Status.Finalizing */;
@@ -55757,6 +56005,7 @@
55757
56005
  FigureComponent,
55758
56006
  Menu,
55759
56007
  SelectionInput,
56008
+ ValidationMessages,
55760
56009
  };
55761
56010
  const hooks = {
55762
56011
  useDragAndDropListItems,
@@ -55809,9 +56058,9 @@
55809
56058
  exports.tokenize = tokenize;
55810
56059
 
55811
56060
 
55812
- __info__.version = "17.1.0-alpha.3";
55813
- __info__.date = "2023-11-16T12:04:09.636Z";
55814
- __info__.hash = "2ef5b1a";
56061
+ __info__.version = "17.1.0-alpha.5";
56062
+ __info__.date = "2023-12-05T09:51:40.034Z";
56063
+ __info__.hash = "c2823eb";
55815
56064
 
55816
56065
 
55817
56066
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);