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