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