@odoo/o-spreadsheet 17.1.0-alpha.3 → 17.1.0-alpha.4

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.4
6
+ * @date 2023-11-24T13:12:24.882Z
7
+ * @hash 255821b
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) {
@@ -3427,11 +3425,18 @@ function isSingleCellReference(xc) {
3427
3425
  return singleCellReference.test(xc);
3428
3426
  }
3429
3427
  function splitReference(ref) {
3428
+ if (!ref.includes("!")) {
3429
+ return { xc: ref };
3430
+ }
3430
3431
  const parts = ref.split("!");
3431
3432
  const xc = parts.pop();
3432
3433
  const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
3433
3434
  return { sheetName, xc };
3434
3435
  }
3436
+ /** Return a reference SheetName!xc from the given arguments */
3437
+ function getFullReference(sheetName, xc) {
3438
+ return sheetName !== undefined ? `${getCanonicalSheetName(sheetName)}!${xc}` : xc;
3439
+ }
3435
3440
 
3436
3441
  /**
3437
3442
  * Convert from a cartesian reference to a Zone
@@ -4540,14 +4545,14 @@ function drawDecoratedText(context, text, position, underline = false, strikethr
4540
4545
  switch (context.textBaseline) {
4541
4546
  case "top":
4542
4547
  underlineY += boxHeight - 2 * strokeWidth;
4543
- strikeY += boxHeight - textHeight;
4548
+ strikeY += boxHeight / 2 - strokeWidth;
4544
4549
  break;
4545
4550
  case "middle":
4546
4551
  underlineY += boxHeight / 2 - strokeWidth;
4547
4552
  break;
4548
4553
  case "alphabetic":
4549
4554
  underlineY += 2 * strokeWidth;
4550
- strikeY -= textHeight / 2 - strokeWidth / 2;
4555
+ strikeY -= 3 * strokeWidth;
4551
4556
  break;
4552
4557
  case "bottom":
4553
4558
  underlineY = y;
@@ -4610,6 +4615,7 @@ function createAction(item) {
4610
4615
  const children = item.children;
4611
4616
  const description = item.description;
4612
4617
  const icon = item.icon;
4618
+ const secondaryIcon = item.secondaryIcon;
4613
4619
  return {
4614
4620
  id: item.id || uuidGenerator$2.uuidv4(),
4615
4621
  name: typeof name === "function" ? name : () => name,
@@ -4628,6 +4634,7 @@ function createAction(item) {
4628
4634
  isReadonlyAllowed: item.isReadonlyAllowed || false,
4629
4635
  separator: item.separator || false,
4630
4636
  icon: typeof icon === "function" ? icon : () => icon || "",
4637
+ secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
4631
4638
  description: typeof description === "function" ? description : () => description || "",
4632
4639
  textColor: item.textColor,
4633
4640
  sequence: item.sequence || 0,
@@ -6983,11 +6990,9 @@ css /* scss */ `
6983
6990
  padding: ${MENU_VERTICAL_PADDING}px 0px;
6984
6991
  width: ${MENU_WIDTH}px;
6985
6992
  box-sizing: border-box !important;
6993
+ user-select: none;
6986
6994
 
6987
6995
  .o-menu-item {
6988
- display: flex;
6989
- justify-content: space-between;
6990
- align-items: center;
6991
6996
  box-sizing: border-box;
6992
6997
  height: ${MENU_ITEM_HEIGHT}px;
6993
6998
  padding: ${MENU_ITEM_PADDING_VERTICAL}px ${MENU_ITEM_PADDING_HORIZONTAL}px;
@@ -6998,20 +7003,12 @@ css /* scss */ `
6998
7003
  min-width: 40%;
6999
7004
  }
7000
7005
 
7001
- &.o-menu-root {
7002
- display: flex;
7003
- justify-content: space-between;
7004
- }
7005
-
7006
7006
  .o-menu-item-icon {
7007
7007
  display: inline-block;
7008
7008
  margin: 0px 8px 0px 0px;
7009
7009
  width: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
7010
7010
  line-height: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
7011
7011
  }
7012
- .o-menu-item-root {
7013
- width: 10px;
7014
- }
7015
7012
 
7016
7013
  &:not(.disabled) {
7017
7014
  &:hover,
@@ -7354,7 +7351,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7354
7351
  return result;
7355
7352
  }
7356
7353
  function tokenizeDebugger(chars) {
7357
- if (chars.current() === "?") {
7354
+ if (chars.current === "?") {
7358
7355
  chars.shift();
7359
7356
  return { type: "DEBUGGER", value: "?" };
7360
7357
  }
@@ -7365,7 +7362,7 @@ const misc$1 = {
7365
7362
  ")": "RIGHT_PAREN",
7366
7363
  };
7367
7364
  function tokenizeMisc(chars) {
7368
- if (chars.current() in misc$1) {
7365
+ if (chars.current in misc$1) {
7369
7366
  const value = chars.shift();
7370
7367
  const type = misc$1[value];
7371
7368
  return { type, value };
@@ -7373,7 +7370,7 @@ function tokenizeMisc(chars) {
7373
7370
  return null;
7374
7371
  }
7375
7372
  function tokenizeArgsSeparator(chars, locale) {
7376
- if (chars.current() === locale.formulaArgSeparator) {
7373
+ if (chars.current === locale.formulaArgSeparator) {
7377
7374
  const value = chars.shift();
7378
7375
  const type = "ARG_SEPARATOR";
7379
7376
  return { type, value };
@@ -7398,14 +7395,13 @@ function tokenizeNumber(chars, locale) {
7398
7395
  return null;
7399
7396
  }
7400
7397
  function tokenizeString(chars) {
7401
- if (chars.current() === '"') {
7398
+ if (chars.current === '"') {
7402
7399
  const startChar = chars.shift();
7403
7400
  let letters = startChar;
7404
- while (chars.current() &&
7405
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
7401
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
7406
7402
  letters += chars.shift();
7407
7403
  }
7408
- if (chars.current() === '"') {
7404
+ if (chars.current === '"') {
7409
7405
  letters += chars.shift();
7410
7406
  }
7411
7407
  return {
@@ -7432,14 +7428,14 @@ function tokenizeSymbol(chars) {
7432
7428
  let result = "";
7433
7429
  // there are two main cases to manage: either something which starts with
7434
7430
  // a ', like 'Sheet 2'A2, or a word-like element.
7435
- if (chars.current() === "'") {
7431
+ if (chars.current === "'") {
7436
7432
  let lastChar = chars.shift();
7437
7433
  result += lastChar;
7438
- while (chars.current()) {
7434
+ while (chars.current) {
7439
7435
  lastChar = chars.shift();
7440
7436
  result += lastChar;
7441
7437
  if (lastChar === "'") {
7442
- if (chars.current() && chars.current() === "'") {
7438
+ if (chars.current && chars.current === "'") {
7443
7439
  lastChar = chars.shift();
7444
7440
  result += lastChar;
7445
7441
  }
@@ -7455,7 +7451,7 @@ function tokenizeSymbol(chars) {
7455
7451
  };
7456
7452
  }
7457
7453
  }
7458
- while (chars.current() && separatorRegexp.test(chars.current())) {
7454
+ while (chars.current && separatorRegexp.test(chars.current)) {
7459
7455
  result += chars.shift();
7460
7456
  }
7461
7457
  if (result.length) {
@@ -7470,14 +7466,14 @@ function tokenizeSymbol(chars) {
7470
7466
  }
7471
7467
  function tokenizeSpace(chars) {
7472
7468
  let length = 0;
7473
- while (chars.current() === NEWLINE) {
7469
+ while (chars.current === NEWLINE) {
7474
7470
  length++;
7475
7471
  chars.shift();
7476
7472
  }
7477
7473
  if (length) {
7478
7474
  return { type: "SPACE", value: NEWLINE.repeat(length) };
7479
7475
  }
7480
- while (chars.current() === " ") {
7476
+ while (chars.current === " ") {
7481
7477
  length++;
7482
7478
  chars.shift();
7483
7479
  }
@@ -7496,17 +7492,20 @@ function tokenizeInvalidRange(chars) {
7496
7492
  class TokenizingChars {
7497
7493
  text;
7498
7494
  currentIndex = 0;
7495
+ current;
7499
7496
  constructor(text) {
7500
7497
  this.text = text;
7501
- }
7502
- current() {
7503
- return this.text[this.currentIndex];
7498
+ this.current = text[0];
7504
7499
  }
7505
7500
  shift() {
7506
- return this.text[this.currentIndex++];
7501
+ const current = this.current;
7502
+ const next = this.text[++this.currentIndex];
7503
+ this.current = next;
7504
+ return current;
7507
7505
  }
7508
7506
  advanceBy(length) {
7509
7507
  this.currentIndex += length;
7508
+ this.current = this.text[this.currentIndex];
7510
7509
  }
7511
7510
  isOver() {
7512
7511
  return this.currentIndex >= this.text.length;
@@ -11310,7 +11309,7 @@ const EXPAND = {
11310
11309
  ) {
11311
11310
  const _array = toMatrix(arg);
11312
11311
  const _nbRows = toInteger(rows?.value, this.locale);
11313
- const _nbColumns = columns !== undefined ? toInteger(columns.value, this.local) : _array.length;
11312
+ const _nbColumns = columns !== undefined ? toInteger(columns.value, this.locale) : _array.length;
11314
11313
  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
11314
  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
11315
  return generateMatrix(_nbColumns, _nbRows, (col, row) => col >= _array.length || row >= _array[col].length ? padWith : _array[col][row]);
@@ -11799,6 +11798,10 @@ var misc = /*#__PURE__*/Object.freeze({
11799
11798
  FORMAT_LARGE_NUMBER: FORMAT_LARGE_NUMBER
11800
11799
  });
11801
11800
 
11801
+ function sum(values, locale) {
11802
+ return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
11803
+ }
11804
+
11802
11805
  const DEFAULT_FACTOR = 1;
11803
11806
  const DEFAULT_MODE = 0;
11804
11807
  const DEFAULT_PLACES = 0;
@@ -12750,7 +12753,7 @@ const SUM = {
12750
12753
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
12751
12754
  },
12752
12755
  compute: function (...values) {
12753
- return reduceNumbers(values, (acc, a) => acc + a, 0, this.locale);
12756
+ return sum(values, this.locale);
12754
12757
  },
12755
12758
  isExported: true,
12756
12759
  };
@@ -12919,6 +12922,44 @@ function assertSameNumberOfElements(...args) {
12919
12922
  const dims = args[0].length;
12920
12923
  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
12924
  }
12925
+ function average(values, locale) {
12926
+ let count = 0;
12927
+ const sum = reduceNumbers(values, (acc, a) => {
12928
+ count += 1;
12929
+ return acc + a;
12930
+ }, 0, locale);
12931
+ assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
12932
+ return sum / count;
12933
+ }
12934
+ function countNumbers(values, locale) {
12935
+ let count = 0;
12936
+ for (let n of values) {
12937
+ if (isMatrix(n)) {
12938
+ for (let i of n) {
12939
+ for (let j of i) {
12940
+ if (typeof j === "number") {
12941
+ count += 1;
12942
+ }
12943
+ }
12944
+ }
12945
+ }
12946
+ else if (typeof n !== "string" || isNumber(n, locale) || parseDateTime(n, locale)) {
12947
+ count += 1;
12948
+ }
12949
+ }
12950
+ return count;
12951
+ }
12952
+ function countAny(values) {
12953
+ return reduceAny(values, (acc, a) => (a !== undefined && a !== null ? acc + 1 : acc), 0);
12954
+ }
12955
+ function max(values, locale) {
12956
+ const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
12957
+ return result === -Infinity ? 0 : result;
12958
+ }
12959
+ function min(values, locale) {
12960
+ const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
12961
+ return result === Infinity ? 0 : result;
12962
+ }
12922
12963
 
12923
12964
  function filterAndFlatData(dataY, dataX) {
12924
12965
  const _flatDataY = [];
@@ -13189,13 +13230,7 @@ const AVERAGE = {
13189
13230
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13190
13231
  },
13191
13232
  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;
13233
+ return average(values, this.locale);
13199
13234
  },
13200
13235
  isExported: true,
13201
13236
  };
@@ -13353,24 +13388,7 @@ const COUNT = {
13353
13388
  ],
13354
13389
  returns: ["NUMBER"],
13355
13390
  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;
13391
+ return countNumbers(values, this.locale);
13374
13392
  },
13375
13393
  isExported: true,
13376
13394
  };
@@ -13385,7 +13403,7 @@ const COUNTA = {
13385
13403
  ],
13386
13404
  returns: ["NUMBER"],
13387
13405
  compute: function (...values) {
13388
- return reduceAny(values, (acc, a) => (a !== undefined && a !== null ? acc + 1 : acc), 0);
13406
+ return countAny(values);
13389
13407
  },
13390
13408
  isExported: true,
13391
13409
  };
@@ -13615,8 +13633,7 @@ const MAX = {
13615
13633
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13616
13634
  },
13617
13635
  compute: function (...values) {
13618
- const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, this.locale);
13619
- return result === -Infinity ? 0 : result;
13636
+ return max(values, this.locale);
13620
13637
  },
13621
13638
  isExported: true,
13622
13639
  };
@@ -13703,8 +13720,7 @@ const MIN = {
13703
13720
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13704
13721
  },
13705
13722
  compute: function (...values) {
13706
- const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, this.locale);
13707
- return result === Infinity ? 0 : result;
13723
+ return min(values, this.locale);
13708
13724
  },
13709
13725
  isExported: true,
13710
13726
  };
@@ -17875,6 +17891,149 @@ var financial = /*#__PURE__*/Object.freeze({
17875
17891
  YIELDMAT: YIELDMAT
17876
17892
  });
17877
17893
 
17894
+ /**
17895
+ * Change the reference types inside the given token, if the token represent a range or a cell
17896
+ *
17897
+ * Eg. :
17898
+ * A1 => $A$1 => A$1 => $A1 => A1
17899
+ * A1:$B$1 => $A$1:B$1 => A$1:$B1 => $A1:B1 => A1:$B$1
17900
+ */
17901
+ function loopThroughReferenceType(token) {
17902
+ if (token.type !== "REFERENCE")
17903
+ return token;
17904
+ const { xc, sheetName } = splitReference(token.value);
17905
+ const [left, right] = xc.split(":");
17906
+ const updatedLeft = getTokenNextReferenceType(left);
17907
+ const updatedRight = right ? `:${getTokenNextReferenceType(right)}` : "";
17908
+ return { ...token, value: getFullReference(sheetName, updatedLeft + updatedRight) };
17909
+ }
17910
+ /**
17911
+ * Get a new token with a changed type of reference from the given cell token symbol.
17912
+ * Undefined behavior if given a token other than a cell or if the Xc contains a sheet reference
17913
+ *
17914
+ * A1 => $A$1 => A$1 => $A1 => A1
17915
+ */
17916
+ function getTokenNextReferenceType(xc) {
17917
+ switch (getReferenceType(xc)) {
17918
+ case "none":
17919
+ xc = setXcToFixedReferenceType(xc, "colrow");
17920
+ break;
17921
+ case "colrow":
17922
+ xc = setXcToFixedReferenceType(xc, "row");
17923
+ break;
17924
+ case "row":
17925
+ xc = setXcToFixedReferenceType(xc, "col");
17926
+ break;
17927
+ case "col":
17928
+ xc = setXcToFixedReferenceType(xc, "none");
17929
+ break;
17930
+ }
17931
+ return xc;
17932
+ }
17933
+ /**
17934
+ * Returns the given XC with the given reference type. The XC string should not contain a sheet name.
17935
+ */
17936
+ function setXcToFixedReferenceType(xc, referenceType) {
17937
+ if (xc.includes("!")) {
17938
+ throw new Error("The given XC should not contain a sheet name");
17939
+ }
17940
+ xc = xc.replace(/\$/g, "");
17941
+ let indexOfNumber;
17942
+ switch (referenceType) {
17943
+ case "col":
17944
+ return "$" + xc;
17945
+ case "row":
17946
+ indexOfNumber = xc.search(/[0-9]/);
17947
+ return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
17948
+ case "colrow":
17949
+ indexOfNumber = xc.search(/[0-9]/);
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;
@@ -28534,22 +28743,15 @@ css /* scss */ `
28534
28743
  height: 10000px;
28535
28744
  background-color: ${SELECTION_BORDER_COLOR};
28536
28745
  }
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);
28746
+ .o-unhide-buttons {
28747
+ width: fit-content;
28748
+ gap: 5px;
28749
+ transform: translate(-50%, 0);
28544
28750
  }
28545
28751
  .o-unhide:hover {
28546
28752
  z-index: ${ComponentsImportance.Grid + 1};
28547
28753
  background-color: lightgrey;
28548
28754
  }
28549
- .o-unhide > svg {
28550
- position: relative;
28551
- top: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28552
- }
28553
28755
  }
28554
28756
  `;
28555
28757
  AbstractResizer.props = {
@@ -28659,8 +28861,8 @@ class ColResizer extends AbstractResizer {
28659
28861
  dimension: "COL",
28660
28862
  });
28661
28863
  }
28662
- unhideStyleValue(hiddenIndex) {
28663
- return this._getDimensionsInViewport(hiddenIndex).start;
28864
+ getUnhideButtonStyle(hiddenIndex) {
28865
+ return cssPropertiesToCss({ left: this._getDimensionsInViewport(hiddenIndex).start + "px" });
28664
28866
  }
28665
28867
  }
28666
28868
  css /* scss */ `
@@ -28706,18 +28908,9 @@ css /* scss */ `
28706
28908
  height: 1px;
28707
28909
  background-color: ${SELECTION_BORDER_COLOR};
28708
28910
  }
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);
28911
+ .o-unhide-buttons {
28912
+ height: fit-content;
28913
+ transform: translate(0, -50%);
28721
28914
  }
28722
28915
  .o-unhide:hover {
28723
28916
  z-index: ${ComponentsImportance.Grid + 1};
@@ -28832,8 +29025,8 @@ class RowResizer extends AbstractResizer {
28832
29025
  elements: hiddenElements,
28833
29026
  });
28834
29027
  }
28835
- unhideStyleValue(hiddenIndex) {
28836
- return this._getDimensionsInViewport(hiddenIndex).start;
29028
+ getUnhideButtonStyle(hiddenIndex) {
29029
+ return cssPropertiesToCss({ top: this._getDimensionsInViewport(hiddenIndex).start + "px" });
28837
29030
  }
28838
29031
  }
28839
29032
  css /* scss */ `
@@ -29340,7 +29533,6 @@ class Grid extends Component {
29340
29533
  onMounted(() => this.focus());
29341
29534
  this.props.exposeFocus(() => this.focus());
29342
29535
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29343
- useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
29344
29536
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29345
29537
  this.moveCanvas(deltaX, deltaY);
29346
29538
  this.hoveredCell.col = undefined;
@@ -29777,6 +29969,7 @@ class Grid extends Component {
29777
29969
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29778
29970
  const target = this.env.model.getters.getSelectedZones();
29779
29971
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29972
+ const isCutOperation = this.env.model.getters.isCutOperation();
29780
29973
  if (clipboardString === content) {
29781
29974
  // the paste actually comes from o-spreadsheet itself
29782
29975
  interactivePaste(this.env, target);
@@ -29784,7 +29977,7 @@ class Grid extends Component {
29784
29977
  else {
29785
29978
  interactivePasteFromOS(this.env, target, content);
29786
29979
  }
29787
- if (this.env.model.getters.isCutOperation()) {
29980
+ if (isCutOperation) {
29788
29981
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29789
29982
  }
29790
29983
  }
@@ -31397,12 +31590,6 @@ function convertChartData(chartData) {
31397
31590
  }
31398
31591
  function convertExcelRangeToSheetXC(range, dataSetsHaveTitle) {
31399
31592
  let { sheetName, xc } = splitReference(range);
31400
- if (sheetName) {
31401
- sheetName = getCanonicalSheetName(sheetName) + "!";
31402
- }
31403
- else {
31404
- sheetName = "";
31405
- }
31406
31593
  let zone = toUnboundedZone(xc);
31407
31594
  if (dataSetsHaveTitle && zone.bottom !== undefined && zone.right !== undefined) {
31408
31595
  const height = zone.bottom - zone.top + 1;
@@ -31415,7 +31602,7 @@ function convertExcelRangeToSheetXC(range, dataSetsHaveTitle) {
31415
31602
  }
31416
31603
  }
31417
31604
  const dataXC = zoneToXc(zone);
31418
- return sheetName + dataXC;
31605
+ return getFullReference(sheetName, dataXC);
31419
31606
  }
31420
31607
 
31421
31608
  /**
@@ -34078,6 +34265,14 @@ class BordersPlugin extends CorePlugin {
34078
34265
  // ---------------------------------------------------------------------------
34079
34266
  // Command Handling
34080
34267
  // ---------------------------------------------------------------------------
34268
+ allowDispatch(cmd) {
34269
+ switch (cmd.type) {
34270
+ case "SET_BORDER":
34271
+ return this.checkBordersUnchanged(cmd);
34272
+ default:
34273
+ return "Success" /* CommandResult.Success */;
34274
+ }
34275
+ }
34081
34276
  handle(cmd) {
34082
34277
  switch (cmd.type) {
34083
34278
  case "ADD_MERGE":
@@ -34497,6 +34692,14 @@ class BordersPlugin extends CorePlugin {
34497
34692
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34498
34693
  }
34499
34694
  }
34695
+ checkBordersUnchanged(cmd) {
34696
+ const currentBorder = this.getCellBorder(cmd);
34697
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
34698
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
34699
+ return "NoChanges" /* CommandResult.NoChanges */;
34700
+ }
34701
+ return "Success" /* CommandResult.Success */;
34702
+ }
34500
34703
  // ---------------------------------------------------------------------------
34501
34704
  // Import/Export
34502
34705
  // ---------------------------------------------------------------------------
@@ -35323,7 +35526,6 @@ class CellPlugin extends CorePlugin {
35323
35526
  static getters = [
35324
35527
  "zoneToXC",
35325
35528
  "getCells",
35326
- "getFormulaCellContent",
35327
35529
  "getTranslatedCellFormula",
35328
35530
  "getCellStyle",
35329
35531
  "getCellById",
@@ -35352,8 +35554,9 @@ class CellPlugin extends CorePlugin {
35352
35554
  allowDispatch(cmd) {
35353
35555
  switch (cmd.type) {
35354
35556
  case "UPDATE_CELL":
35557
+ return this.checkCellOutOfSheet(cmd);
35355
35558
  case "CLEAR_CELL":
35356
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35559
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35357
35560
  default:
35358
35561
  return "Success" /* CommandResult.Success */;
35359
35562
  }
@@ -35522,24 +35725,18 @@ class CellPlugin extends CorePlugin {
35522
35725
  */
35523
35726
  getCellById(cellId) {
35524
35727
  // 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;
35728
+ const position = this.getters.getCellPosition(cellId);
35729
+ const sheet = this.cells[position.sheetId];
35730
+ return sheet[cellId];
35533
35731
  }
35534
35732
  /*
35535
35733
  * Reconstructs the original formula string based on a normalized form and its dependencies
35536
35734
  */
35537
35735
  getFormulaCellContent(sheetId, compiledFormula, dependencies) {
35538
- const ranges = dependencies || compiledFormula.dependencies;
35539
35736
  let rangeIndex = 0;
35540
35737
  return concat(compiledFormula.tokens.map((token) => {
35541
35738
  if (token.type === "REFERENCE") {
35542
- const range = ranges[rangeIndex++];
35739
+ const range = dependencies[rangeIndex++];
35543
35740
  return this.getters.getRangeString(range, sheetId);
35544
35741
  }
35545
35742
  return token.value;
@@ -35760,13 +35957,23 @@ class CellPlugin extends CorePlugin {
35760
35957
  },
35761
35958
  };
35762
35959
  }
35763
- checkCellOutOfSheet(sheetId, col, row) {
35960
+ checkCellOutOfSheet(cmd) {
35961
+ const { sheetId, col, row } = cmd;
35764
35962
  const sheet = this.getters.tryGetSheet(sheetId);
35765
35963
  if (!sheet)
35766
35964
  return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
35767
35965
  const sheetZone = this.getters.getSheetZone(sheetId);
35768
35966
  return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */;
35769
35967
  }
35968
+ checkUselessClearCell(cmd) {
35969
+ const cell = this.getters.getCell(cmd);
35970
+ if (!cell)
35971
+ return "NoChanges" /* CommandResult.NoChanges */;
35972
+ if (!cell.content && !cell.style && !cell.format) {
35973
+ return "NoChanges" /* CommandResult.NoChanges */;
35974
+ }
35975
+ return "Success" /* CommandResult.Success */;
35976
+ }
35770
35977
  }
35771
35978
  class FormulaCellWithDependencies {
35772
35979
  id;
@@ -36456,6 +36663,9 @@ class DataValidationPlugin extends CorePlugin {
36456
36663
  return this.rules[sheetId].find((rule) => rule.id === id);
36457
36664
  }
36458
36665
  getValidationRuleForCell({ sheetId, col, row }) {
36666
+ if (!this.rules[sheetId]) {
36667
+ return undefined;
36668
+ }
36459
36669
  for (const rule of this.rules[sheetId]) {
36460
36670
  for (const range of rule.ranges) {
36461
36671
  if (isInside(col, row, range.zone)) {
@@ -37628,7 +37838,7 @@ class MergePlugin extends CorePlugin {
37628
37838
  const rangeString = this.getters.getRangeString(expandedRange, forSheetId);
37629
37839
  if (this.isSingleCellOrMerge(rangeImpl.sheetId, rangeImpl.zone)) {
37630
37840
  const { sheetName, xc } = splitReference(rangeString);
37631
- return `${sheetName !== undefined ? getCanonicalSheetName(sheetName) + "!" : ""}${xc.split(":")[0]}`;
37841
+ return getFullReference(sheetName, xc.split(":")[0]);
37632
37842
  }
37633
37843
  return rangeString;
37634
37844
  }
@@ -39053,10 +39263,13 @@ class SheetPlugin extends CorePlugin {
39053
39263
  // begin with the end.
39054
39264
  rows.sort((a, b) => b - a);
39055
39265
  for (let group of groupConsecutive(rows)) {
39266
+ // indexes are sorted in the descending order
39267
+ const from = group[group.length - 1];
39268
+ const to = group[0];
39056
39269
  // 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));
39270
+ this.moveCellOnRowsDeletion(sheet, from, to);
39271
+ // Effectively delete the rows
39272
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39060
39273
  }
39061
39274
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39062
39275
  if (count) {
@@ -39078,8 +39291,6 @@ class SheetPlugin extends CorePlugin {
39078
39291
  this.addEmptyRows(sheet, quantity);
39079
39292
  // Move the cells.
39080
39293
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39081
- // Recompute the left-right/top-bottom.
39082
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39083
39294
  if (index < sheet.panes.ySplit) {
39084
39295
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39085
39296
  }
@@ -39180,35 +39391,20 @@ class SheetPlugin extends CorePlugin {
39180
39391
  }
39181
39392
  }
39182
39393
  }
39183
- updateRowsStructureOnDeletion(index, sheet) {
39394
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39184
39395
  const rows = [];
39185
- const cellsQueue = sheet.rows.map((row) => row.cells);
39396
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39186
39397
  for (let i in sheet.rows) {
39187
- if (Number(i) === index) {
39398
+ const row = Number(i);
39399
+ if (row >= deleteFromRow && row <= deleteToRow) {
39188
39400
  continue;
39189
39401
  }
39190
39402
  rows.push({
39191
- cells: cellsQueue.shift(),
39403
+ cells: cellsQueue.pop(),
39192
39404
  });
39193
39405
  }
39194
39406
  this.history.update("sheets", sheet.id, "rows", rows);
39195
39407
  }
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
39408
  /**
39213
39409
  * Add empty rows at the end of the rows
39214
39410
  *
@@ -39735,6 +39931,7 @@ class CompilationParametersBuilder {
39735
39931
  getters;
39736
39932
  computeCell;
39737
39933
  evalContext;
39934
+ rangeCache = {};
39738
39935
  constructor(context, getters, computeCell) {
39739
39936
  this.getters = getters;
39740
39937
  this.computeCell = computeCell;
@@ -39757,7 +39954,8 @@ class CompilationParametersBuilder {
39757
39954
  refFn(range, isMeta, functionName, paramNumber) {
39758
39955
  if (isMeta) {
39759
39956
  // Use zoneToXc of zone instead of getRangeString to avoid sending unbounded ranges
39760
- return { value: zoneToXc(range.zone) };
39957
+ const sheetName = this.getters.getSheetName(range.sheetId);
39958
+ return { value: getFullReference(sheetName, zoneToXc(range.zone)) };
39761
39959
  }
39762
39960
  if (!isZoneValid(range.zone)) {
39763
39961
  throw new InvalidReferenceError();
@@ -39820,17 +40018,24 @@ class CompilationParametersBuilder {
39820
40018
  if (!_zone) {
39821
40019
  return [[]];
39822
40020
  }
40021
+ const { top, left, bottom, right } = zone;
40022
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
40023
+ if (cacheKey in this.rangeCache) {
40024
+ return this.rangeCache[cacheKey];
40025
+ }
39823
40026
  const height = _zone.bottom - _zone.top + 1;
39824
40027
  const width = _zone.right - _zone.left + 1;
39825
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
40028
+ const matrix = new Array(width);
39826
40029
  // Performance issue: nested loop is faster than a map here
39827
40030
  for (let col = _zone.left; col <= _zone.right; col++) {
40031
+ const colIndex = col - _zone.left;
40032
+ matrix[colIndex] = new Array(height);
39828
40033
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39829
- const colIndex = col - _zone.left;
39830
40034
  const rowIndex = row - _zone.top;
39831
40035
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39832
40036
  }
39833
40037
  }
40038
+ this.rangeCache[cacheKey] = matrix;
39834
40039
  return matrix;
39835
40040
  }
39836
40041
  }
@@ -40036,6 +40241,7 @@ class Evaluator {
40036
40241
  this.formulaDependencies().addDependencies(positionId, dependencies);
40037
40242
  }
40038
40243
  updateCompilationParameters() {
40244
+ // rebuild the compilation parameters (with a clean cache)
40039
40245
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40040
40246
  }
40041
40247
  evaluateCells(positions) {
@@ -40079,6 +40285,16 @@ class Evaluator {
40079
40285
  this.evaluatedCells = new Map();
40080
40286
  this.evaluate(this.getAllCells());
40081
40287
  }
40288
+ evaluateFormula(sheetId, formulaString) {
40289
+ const compiledFormula = compile(formulaString);
40290
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
40291
+ this.updateCompilationParameters();
40292
+ const result = updateEvalContextAndExecute({ ...compiledFormula, dependencies: ranges }, this.compilationParams, sheetId);
40293
+ if (isMatrix(result)) {
40294
+ return matrixMap(result, (cell) => cell.value);
40295
+ }
40296
+ return result.value;
40297
+ }
40082
40298
  getAllCells() {
40083
40299
  const positionIds = new JetSet();
40084
40300
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40105,6 +40321,7 @@ class Evaluator {
40105
40321
  this.nextPositionsToUpdate = cells;
40106
40322
  let currentIteration = 0;
40107
40323
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40324
+ this.updateCompilationParameters();
40108
40325
  const positionIds = Array.from(this.nextPositionsToUpdate);
40109
40326
  this.nextPositionsToUpdate.clear();
40110
40327
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40128,7 +40345,8 @@ class Evaluator {
40128
40345
  if (!this.blockedArrayFormulas.has(positionId)) {
40129
40346
  this.invalidateSpreading(positionId);
40130
40347
  }
40131
- const cell = this.getCell(positionId);
40348
+ const cellPosition = this.decodePosition(positionId);
40349
+ const cell = this.getters.getCell(cellPosition);
40132
40350
  if (cell === undefined) {
40133
40351
  return createEvaluatedCell("", { locale: this.getters.getLocale() });
40134
40352
  }
@@ -40139,7 +40357,7 @@ class Evaluator {
40139
40357
  }
40140
40358
  this.cellsBeingComputed.add(cellId);
40141
40359
  return cell.isFormula
40142
- ? this.computeFormulaCell(cell)
40360
+ ? this.computeFormulaCell(cellPosition.sheetId, cell)
40143
40361
  : evaluateLiteral(cell.content, { format: cell.format, locale: this.getters.getLocale() });
40144
40362
  }
40145
40363
  catch (e) {
@@ -40165,14 +40383,9 @@ class Evaluator {
40165
40383
  e.message = e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled);
40166
40384
  return errorCell(e);
40167
40385
  }
40168
- computeFormulaCell(cellData) {
40386
+ computeFormulaCell(sheetId, cellData) {
40169
40387
  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);
40388
+ const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, sheetId, cellId);
40176
40389
  if (!isMatrix(formulaReturn)) {
40177
40390
  return createEvaluatedCell(formulaReturn.value, {
40178
40391
  format: cellData.format || formulaReturn.format,
@@ -40374,6 +40587,18 @@ class PositionBitsEncoder {
40374
40587
  return sheetId;
40375
40588
  }
40376
40589
  }
40590
+ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
40591
+ compilationParams[2].__originCellXC = lazy(() => {
40592
+ if (!cellId) {
40593
+ return undefined;
40594
+ }
40595
+ // compute the value lazily for performance reasons
40596
+ const position = compilationParams[2].getters.getCellPosition(cellId);
40597
+ return toXC(position.col, position.row);
40598
+ });
40599
+ compilationParams[2].__originSheetId = sheetId;
40600
+ return compiledFormula.execute(compiledFormula.dependencies, ...compilationParams);
40601
+ }
40377
40602
 
40378
40603
  //#region
40379
40604
  // ---------------------------------------------------------------------------
@@ -40468,7 +40693,6 @@ class PositionBitsEncoder {
40468
40693
  // of other cells depending on it, at the next iteration.
40469
40694
  //#endregion
40470
40695
  class EvaluationPlugin extends UIPlugin {
40471
- config;
40472
40696
  static getters = [
40473
40697
  "evaluateFormula",
40474
40698
  "getCorrespondingFormulaCell",
@@ -40479,15 +40703,13 @@ class EvaluationPlugin extends UIPlugin {
40479
40703
  "getEvaluatedCells",
40480
40704
  "getEvaluatedCellsInZone",
40481
40705
  "getSpreadPositionsOf",
40706
+ "getArrayFormulaSpreadingOn",
40482
40707
  ];
40483
40708
  shouldRebuildDependenciesGraph = true;
40484
40709
  evaluator;
40485
- compilationParams;
40486
40710
  positionsToUpdate = [];
40487
40711
  constructor(config) {
40488
40712
  super(config);
40489
- this.config = config;
40490
- this.compilationParams = this.getCompilationParameters();
40491
40713
  this.evaluator = new Evaluator(config.custom, this.getters);
40492
40714
  }
40493
40715
  // ---------------------------------------------------------------------------
@@ -40512,10 +40734,6 @@ class EvaluationPlugin extends UIPlugin {
40512
40734
  case "EVALUATE_CELLS":
40513
40735
  this.evaluator.evaluateAllCells();
40514
40736
  break;
40515
- case "UPDATE_LOCALE":
40516
- this.compilationParams = this.getCompilationParameters();
40517
- this.evaluator.updateCompilationParameters();
40518
- break;
40519
40737
  }
40520
40738
  }
40521
40739
  finalize() {
@@ -40533,16 +40751,7 @@ class EvaluationPlugin extends UIPlugin {
40533
40751
  // Getters
40534
40752
  // ---------------------------------------------------------------------------
40535
40753
  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;
40754
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40546
40755
  }
40547
40756
  /**
40548
40757
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40591,6 +40800,9 @@ class EvaluationPlugin extends UIPlugin {
40591
40800
  getSpreadPositionsOf(position) {
40592
40801
  return this.evaluator.getSpreadPositionsOf(position);
40593
40802
  }
40803
+ getArrayFormulaSpreadingOn(position) {
40804
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40805
+ }
40594
40806
  // ---------------------------------------------------------------------------
40595
40807
  // Export
40596
40808
  // ---------------------------------------------------------------------------
@@ -40634,7 +40846,7 @@ class EvaluationPlugin extends UIPlugin {
40634
40846
  else if (cell && cell.content) {
40635
40847
  return undefined;
40636
40848
  }
40637
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40849
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40638
40850
  if (spreadingFormulaPosition === undefined) {
40639
40851
  return undefined;
40640
40852
  }
@@ -40644,9 +40856,6 @@ class EvaluationPlugin extends UIPlugin {
40644
40856
  }
40645
40857
  return undefined;
40646
40858
  }
40647
- getCompilationParameters() {
40648
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40649
- }
40650
40859
  }
40651
40860
  function isBadExpression(tokens) {
40652
40861
  try {
@@ -43345,11 +43554,26 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43345
43554
  let cellsInRow = [];
43346
43555
  for (let col of columnsIndex) {
43347
43556
  const position = { col, row, sheetId };
43557
+ const spreader = getters.getArrayFormulaSpreadingOn(position);
43558
+ let cell = getters.getCell(position);
43559
+ const evaluatedCell = getters.getEvaluatedCell(position);
43560
+ if (spreader) {
43561
+ const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
43562
+ const content = isSpreaderCopied
43563
+ ? ""
43564
+ : formatValue(evaluatedCell.value, { locale: getters.getLocale() });
43565
+ cell = {
43566
+ id: cell?.id || "",
43567
+ style: cell?.style,
43568
+ format: evaluatedCell.format,
43569
+ content,
43570
+ isFormula: false,
43571
+ };
43572
+ }
43348
43573
  cellsInRow.push({
43349
- cell: getters.getCell(position),
43350
- style: getters.getCellComputedStyle(position),
43351
- evaluatedCell: getters.getEvaluatedCell(position),
43574
+ cell,
43352
43575
  border: getters.getCellBorder(position) || undefined,
43576
+ evaluatedCell,
43353
43577
  position,
43354
43578
  });
43355
43579
  }
@@ -43471,13 +43695,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43471
43695
  });
43472
43696
  }
43473
43697
  this.pasteCopiedTables(target);
43474
- this.cells.forEach((row) => {
43475
- row.forEach((c) => {
43476
- if (c.cell) {
43477
- c.cell = undefined;
43478
- }
43479
- });
43480
- });
43481
43698
  }
43482
43699
  /**
43483
43700
  * The clipped zone is copied as many times as it fits in the target.
@@ -43551,7 +43768,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43551
43768
  clearClippedZones() {
43552
43769
  for (const row of this.cells) {
43553
43770
  for (const cell of row) {
43554
- if (cell.cell) {
43771
+ if (cell?.cell) {
43555
43772
  this.dispatch("CLEAR_CELL", cell.position);
43556
43773
  }
43557
43774
  }
@@ -43577,6 +43794,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43577
43794
  const rowCells = this.cells[r];
43578
43795
  for (let c = 0; c < width; c++) {
43579
43796
  const origin = rowCells[c];
43797
+ if (!origin) {
43798
+ continue;
43799
+ }
43580
43800
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43581
43801
  // TODO: refactor this part. the "Paste merge" action is also executed with
43582
43802
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43600,53 +43820,42 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43600
43820
  pasteCell(origin, target, operation, clipboardOption) {
43601
43821
  const { sheetId, col, row } = target;
43602
43822
  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 });
43823
+ if (clipboardOption?.pasteOption === "onlyValue") {
43824
+ const locale = this.getters.getLocale();
43825
+ const content = formatValue(origin.evaluatedCell.value, { locale });
43826
+ this.dispatch("UPDATE_CELL", { ...target, content });
43827
+ return;
43613
43828
  }
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
- }
43829
+ const targetBorders = this.getters.getCellBorder(target);
43830
+ const originBorders = origin.border;
43831
+ const border = {
43832
+ top: targetBorders?.top || originBorders?.top,
43833
+ bottom: targetBorders?.bottom || originBorders?.bottom,
43834
+ left: targetBorders?.left || originBorders?.left,
43835
+ right: targetBorders?.right || originBorders?.right,
43836
+ };
43837
+ this.dispatch("SET_BORDER", { sheetId, col, row, border });
43838
+ if (clipboardOption?.pasteOption === "onlyFormat") {
43839
+ this.dispatch("UPDATE_CELL", {
43840
+ ...target,
43841
+ style: origin.cell?.style ?? null,
43842
+ format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
43843
+ });
43844
+ return;
43845
+ }
43846
+ const content = origin.cell && origin.cell.isFormula && operation === "COPY"
43847
+ ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula)
43848
+ : origin.cell?.content;
43849
+ if (content !== "" || origin.cell?.format || origin.cell?.style) {
43633
43850
  this.dispatch("UPDATE_CELL", {
43634
43851
  ...target,
43635
43852
  content,
43636
- style: origin.cell.style || null,
43637
- format: origin.cell.format,
43853
+ style: origin.cell?.style || null,
43854
+ format: origin.cell?.format,
43638
43855
  });
43639
43856
  }
43640
43857
  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
- }
43858
+ this.dispatch("CLEAR_CELL", target);
43650
43859
  }
43651
43860
  }
43652
43861
  /**
@@ -43708,22 +43917,28 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43708
43917
  return (this.cells
43709
43918
  .map((cells) => {
43710
43919
  return cells
43711
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43920
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43712
43921
  ? c.cell?.content || ""
43713
- : c.evaluatedCell?.formattedValue || "")
43922
+ : c?.evaluatedCell?.formattedValue || "")
43714
43923
  .join("\t");
43715
43924
  })
43716
43925
  .join("\n") || "\t");
43717
43926
  }
43718
43927
  getHTMLContent() {
43719
43928
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43929
+ if (!this.cells[0][0]) {
43930
+ return "";
43931
+ }
43720
43932
  return this.getters.getCellText(this.cells[0][0].position);
43721
43933
  }
43722
43934
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43723
43935
  for (const row of this.cells) {
43724
43936
  htmlTable += "<tr>";
43725
43937
  for (const cell of row) {
43726
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43938
+ if (!cell) {
43939
+ continue;
43940
+ }
43941
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43727
43942
  const cellText = this.getters.getCellText(cell.position);
43728
43943
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43729
43944
  }
@@ -44051,24 +44266,49 @@ var Direction;
44051
44266
  */
44052
44267
  class FindAndReplacePlugin extends UIPlugin {
44053
44268
  static layers = [3 /* LAYERS.Search */];
44054
- static getters = ["getSearchMatches", "getCurrentSelectedMatchIndex"];
44055
- searchMatches = [];
44269
+ static getters = [
44270
+ "getSearchMatches",
44271
+ "getCurrentSelectedMatchIndex",
44272
+ "getSearchOptions",
44273
+ "getAllSheetMatchesCount",
44274
+ "getActiveSheetMatchesCount",
44275
+ "getSpecificRangeMatchesCount",
44276
+ ];
44277
+ allSheetsMatches = [];
44278
+ activeSheetMatches = [];
44279
+ specificRangeMatches = [];
44280
+ // fixme: why do we make selectedMatchIndex on top of a selected
44281
+ // property in the matches?
44056
44282
  selectedMatchIndex = null;
44057
44283
  currentSearchRegex = null;
44058
44284
  searchOptions = {
44059
44285
  matchCase: false,
44060
44286
  exactMatch: false,
44061
44287
  searchFormulas: false,
44288
+ searchScope: "allSheets",
44289
+ specificRange: undefined,
44062
44290
  };
44063
44291
  toSearch = "";
44064
44292
  isSearchDirty = false;
44293
+ get searchMatches() {
44294
+ switch (this.searchOptions.searchScope) {
44295
+ case "allSheets":
44296
+ return this.allSheetsMatches;
44297
+ case "activeSheet":
44298
+ return this.activeSheetMatches;
44299
+ case "specificRange":
44300
+ return this.specificRangeMatches;
44301
+ }
44302
+ }
44065
44303
  // ---------------------------------------------------------------------------
44066
44304
  // Command Handling
44067
44305
  // ---------------------------------------------------------------------------
44068
44306
  handle(cmd) {
44069
44307
  switch (cmd.type) {
44070
44308
  case "UPDATE_SEARCH":
44071
- this.updateSearch(cmd.toSearch, cmd.searchOptions);
44309
+ const rangeData = cmd.searchOptions.specificRange;
44310
+ const specificRange = rangeData && this.getters.getRangeFromRangeData(rangeData);
44311
+ this.updateSearch(cmd.toSearch, { ...cmd.searchOptions, specificRange });
44072
44312
  break;
44073
44313
  case "CLEAR_SEARCH":
44074
44314
  this.clearSearch();
@@ -44098,7 +44338,9 @@ class FindAndReplacePlugin extends UIPlugin {
44098
44338
  this.isSearchDirty = true;
44099
44339
  break;
44100
44340
  case "ACTIVATE_SHEET":
44101
- this.refreshSearch();
44341
+ if (this.searchOptions.searchScope === "activeSheet") {
44342
+ this.isSearchDirty = true;
44343
+ }
44102
44344
  break;
44103
44345
  }
44104
44346
  }
@@ -44117,6 +44359,18 @@ class FindAndReplacePlugin extends UIPlugin {
44117
44359
  getCurrentSelectedMatchIndex() {
44118
44360
  return this.selectedMatchIndex;
44119
44361
  }
44362
+ getSearchOptions() {
44363
+ return { ...this.searchOptions, specificRange: this.searchOptions.specificRange?.rangeData };
44364
+ }
44365
+ getAllSheetMatchesCount() {
44366
+ return this.allSheetsMatches.length;
44367
+ }
44368
+ getActiveSheetMatchesCount() {
44369
+ return this.activeSheetMatches.length;
44370
+ }
44371
+ getSpecificRangeMatchesCount() {
44372
+ return this.specificRangeMatches.length;
44373
+ }
44120
44374
  // ---------------------------------------------------------------------------
44121
44375
  // Search
44122
44376
  // ---------------------------------------------------------------------------
@@ -44137,8 +44391,8 @@ class FindAndReplacePlugin extends UIPlugin {
44137
44391
  * refresh the matches according to the current search options
44138
44392
  */
44139
44393
  refreshSearch() {
44140
- const matches = this.findMatches();
44141
- this.searchMatches = matches;
44394
+ this.selectedMatchIndex = null;
44395
+ this.findMatches();
44142
44396
  this.selectNextCell(Direction.current);
44143
44397
  }
44144
44398
  /**
@@ -44153,45 +44407,65 @@ class FindAndReplacePlugin extends UIPlugin {
44153
44407
  }
44154
44408
  this.currentSearchRegex = RegExp(searchValue, flags);
44155
44409
  }
44410
+ getSheetsInSearchOrder() {
44411
+ switch (this.searchOptions.searchScope) {
44412
+ case "allSheets":
44413
+ const sheetIds = this.getters.getSheetIds();
44414
+ const activeSheetIndex = sheetIds.findIndex((id) => id === this.getters.getActiveSheetId());
44415
+ return [
44416
+ sheetIds[activeSheetIndex],
44417
+ ...sheetIds.slice(activeSheetIndex + 1),
44418
+ ...sheetIds.slice(0, activeSheetIndex),
44419
+ ];
44420
+ case "activeSheet":
44421
+ return [this.getters.getActiveSheetId()];
44422
+ case "specificRange":
44423
+ const specificRange = this.searchOptions.specificRange;
44424
+ if (!specificRange) {
44425
+ return [];
44426
+ }
44427
+ return specificRange ? [specificRange.sheetId] : [];
44428
+ }
44429
+ }
44156
44430
  /**
44157
44431
  * Find matches using the current regex
44158
44432
  */
44159
44433
  findMatches() {
44160
- const sheetId = this.getters.getActiveSheetId();
44161
- const cells = this.getters.getCells(sheetId);
44162
44434
  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 };
44435
+ if (this.toSearch) {
44436
+ for (const sheetId of this.getters.getSheetIds()) {
44437
+ matches.push(...this.findMatchesInSheet(sheetId));
44438
+ }
44439
+ }
44440
+ // set results
44441
+ this.allSheetsMatches = matches;
44442
+ this.activeSheetMatches = matches.filter((match) => match.sheetId === this.getters.getActiveSheetId());
44443
+ if (this.searchOptions.specificRange) {
44444
+ const { sheetId, zone } = this.searchOptions.specificRange;
44445
+ this.specificRangeMatches = matches.filter((match) => match.sheetId === sheetId && isInside(match.col, match.row, zone));
44446
+ }
44447
+ else {
44448
+ this.specificRangeMatches = [];
44449
+ }
44450
+ }
44451
+ findMatchesInSheet(sheetId) {
44452
+ const matches = [];
44453
+ const { left, right, top, bottom } = this.getters.getSheetZone(sheetId);
44454
+ for (let row = top; row <= bottom; row++) {
44455
+ for (let col = left; col <= right; col++) {
44167
44456
  const isColHidden = this.getters.isColHidden(sheetId, col);
44168
44457
  const isRowHidden = this.getters.isRowHidden(sheetId, row);
44169
44458
  if (isColHidden || isRowHidden) {
44170
44459
  continue;
44171
44460
  }
44172
- if (this.currentSearchRegex.test(this.getSearchableString(cellPosition))) {
44173
- const match = { col, row, selected: false };
44461
+ const cellPosition = { sheetId, col, row };
44462
+ if (this.currentSearchRegex?.test(this.getSearchableString(cellPosition))) {
44463
+ const match = { sheetId, col, row };
44174
44464
  matches.push(match);
44175
44465
  }
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
44466
  }
44187
44467
  }
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;
44468
+ return matches;
44195
44469
  }
44196
44470
  /**
44197
44471
  * Changes the selected search cell. Given a direction it will
@@ -44209,30 +44483,47 @@ class FindAndReplacePlugin extends UIPlugin {
44209
44483
  }
44210
44484
  let nextIndex;
44211
44485
  if (this.selectedMatchIndex === null) {
44212
- nextIndex = 0;
44486
+ let nextMatchIndex = -1;
44487
+ // if search is not available in current sheet will select in next sheet
44488
+ for (const sheetId of this.getSheetsInSearchOrder()) {
44489
+ nextMatchIndex = matches.findIndex((match) => match.sheetId === sheetId);
44490
+ if (nextMatchIndex !== -1) {
44491
+ break;
44492
+ }
44493
+ }
44494
+ nextIndex = nextMatchIndex;
44213
44495
  }
44214
44496
  else {
44215
44497
  nextIndex = this.selectedMatchIndex + indexChange;
44216
44498
  }
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;
44499
+ // loop index value inside the array (index -1 => last index)
44500
+ nextIndex = (nextIndex + matches.length) % matches.length;
44219
44501
  this.selectedMatchIndex = nextIndex;
44502
+ const selectedMatch = matches[nextIndex];
44503
+ // Switch to the sheet where the match is located
44504
+ if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
44505
+ this.dispatch("ACTIVATE_SHEET", {
44506
+ sheetIdFrom: this.getters.getActiveSheetId(),
44507
+ sheetIdTo: selectedMatch.sheetId,
44508
+ });
44509
+ }
44220
44510
  // we want grid selection to capture the selection stream
44221
44511
  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
- }
44512
+ this.selection.selectCell(selectedMatch.col, selectedMatch.row);
44226
44513
  }
44227
44514
  clearSearch() {
44228
44515
  this.toSearch = "";
44229
- this.searchMatches = [];
44230
44516
  this.selectedMatchIndex = null;
44231
44517
  this.currentSearchRegex = null;
44518
+ this.allSheetsMatches = [];
44519
+ this.activeSheetMatches = [];
44520
+ this.specificRangeMatches = [];
44232
44521
  this.searchOptions = {
44233
44522
  matchCase: false,
44234
44523
  exactMatch: false,
44235
44524
  searchFormulas: false,
44525
+ searchScope: "allSheets",
44526
+ specificRange: undefined,
44236
44527
  };
44237
44528
  }
44238
44529
  // ---------------------------------------------------------------------------
@@ -44242,9 +44533,7 @@ class FindAndReplacePlugin extends UIPlugin {
44242
44533
  if (!this.currentSearchRegex) {
44243
44534
  return;
44244
44535
  }
44245
- const sheetId = this.getters.getActiveSheetId();
44246
- const position = { sheetId, ...selectedMatch };
44247
- const cell = this.getters.getCell(position);
44536
+ const cell = this.getters.getCell(selectedMatch);
44248
44537
  if (!cell?.content) {
44249
44538
  return;
44250
44539
  }
@@ -44252,10 +44541,10 @@ class FindAndReplacePlugin extends UIPlugin {
44252
44541
  return;
44253
44542
  }
44254
44543
  const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
44255
- const toReplace = this.getSearchableString(position);
44544
+ const toReplace = this.getSearchableString(selectedMatch);
44256
44545
  const content = toReplace.replace(replaceRegex, replaceWith);
44257
44546
  const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
44258
- this.dispatch("UPDATE_CELL", { ...position, content: canonicalContent });
44547
+ this.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
44259
44548
  }
44260
44549
  /**
44261
44550
  * Replace the value of the currently selected match
@@ -44286,7 +44575,10 @@ class FindAndReplacePlugin extends UIPlugin {
44286
44575
  drawGrid(renderingContext) {
44287
44576
  const { ctx } = renderingContext;
44288
44577
  const sheetId = this.getters.getActiveSheetId();
44289
- for (const match of this.searchMatches) {
44578
+ for (const [index, match] of this.searchMatches.entries()) {
44579
+ if (match.sheetId !== sheetId) {
44580
+ continue; // Skip drawing matches from other sheets
44581
+ }
44290
44582
  const merge = this.getters.getMerge({ sheetId, col: match.col, row: match.row });
44291
44583
  const left = merge ? merge.left : match.col;
44292
44584
  const right = merge ? merge.right : match.col;
@@ -44296,12 +44588,21 @@ class FindAndReplacePlugin extends UIPlugin {
44296
44588
  if (width > 0 && height > 0) {
44297
44589
  ctx.fillStyle = BACKGROUND_COLOR;
44298
44590
  ctx.fillRect(x, y, width, height);
44299
- if (match.selected) {
44591
+ if (index === this.selectedMatchIndex) {
44300
44592
  ctx.strokeStyle = BORDER_COLOR;
44301
44593
  ctx.strokeRect(x, y, width, height);
44302
44594
  }
44303
44595
  }
44304
44596
  }
44597
+ if (this.searchOptions.searchScope === "specificRange") {
44598
+ const range = this.searchOptions.specificRange;
44599
+ if (!range || range.sheetId !== sheetId) {
44600
+ return;
44601
+ }
44602
+ const { x, y, width, height } = this.getters.getVisibleRect(range.zone);
44603
+ ctx.strokeStyle = BORDER_COLOR;
44604
+ ctx.strokeRect(x, y, width, height);
44605
+ }
44305
44606
  }
44306
44607
  }
44307
44608
 
@@ -46767,11 +47068,14 @@ class ClipboardPlugin extends UIPlugin {
46767
47068
  }
46768
47069
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46769
47070
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
47071
+ if (this.state.operation === "CUT") {
47072
+ this.state = undefined;
47073
+ }
46770
47074
  this.lastPasteState = this.state;
46771
47075
  if (this.paintFormatStatus === "oneOff") {
46772
47076
  this.paintFormatStatus = "inactive";
46773
- this.status = "invisible";
46774
47077
  }
47078
+ this.status = "invisible";
46775
47079
  break;
46776
47080
  case "COPY_PASTE_CELLS_ABOVE":
46777
47081
  {
@@ -46998,92 +47302,6 @@ class ClipboardPlugin extends UIPlugin {
46998
47302
  }
46999
47303
  }
47000
47304
 
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
47305
  const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
47088
47306
  class EditionPlugin extends UIPlugin {
47089
47307
  static getters = [
@@ -48001,32 +48219,32 @@ const selectionStatisticFunctions = [
48001
48219
  {
48002
48220
  name: _t("Sum"),
48003
48221
  types: [CellValueType.number],
48004
- compute: (values, locale) => SUM.compute.bind({ locale })([values]),
48222
+ compute: (values, locale) => sum([[values]], locale),
48005
48223
  },
48006
48224
  {
48007
48225
  name: _t("Avg"),
48008
48226
  types: [CellValueType.number],
48009
- compute: (values, locale) => AVERAGE.compute.bind({ locale })([values]),
48227
+ compute: (values, locale) => average([[values]], locale),
48010
48228
  },
48011
48229
  {
48012
48230
  name: _t("Min"),
48013
48231
  types: [CellValueType.number],
48014
- compute: (values, locale) => MIN.compute.bind({ locale })([values]),
48232
+ compute: (values, locale) => min([[values]], locale),
48015
48233
  },
48016
48234
  {
48017
48235
  name: _t("Max"),
48018
48236
  types: [CellValueType.number],
48019
- compute: (values, locale) => MAX.compute.bind({ locale })([values]),
48237
+ compute: (values, locale) => max([[values]], locale),
48020
48238
  },
48021
48239
  {
48022
48240
  name: _t("Count"),
48023
48241
  types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
48024
- compute: (values, locale) => COUNTA.compute.bind({ locale })([values]),
48242
+ compute: (values) => countAny([[values]]),
48025
48243
  },
48026
48244
  {
48027
48245
  name: _t("Count Numbers"),
48028
48246
  types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
48029
- compute: (values, locale) => COUNT.compute.bind({ locale })([values]),
48247
+ compute: (values, locale) => countNumbers([[values]], locale),
48030
48248
  },
48031
48249
  ];
48032
48250
  /**
@@ -51941,6 +52159,7 @@ class Spreadsheet extends Component {
51941
52159
  };
51942
52160
  sidePanel;
51943
52161
  composer;
52162
+ spreadsheetRef = useRef("spreadsheet");
51944
52163
  _focusGrid;
51945
52164
  keyDownMapping;
51946
52165
  isViewportTooSmall = false;
@@ -51975,6 +52194,18 @@ class Spreadsheet extends Component {
51975
52194
  clipboard: this.env.clipboard || instantiateClipboard(),
51976
52195
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
51977
52196
  });
52197
+ useEffect(() => {
52198
+ /**
52199
+ * Only refocus the grid if the active element is not a child of the spreadsheet
52200
+ * (i.e. activeElement is outside of the spreadsheetRef component)
52201
+ * and spreadsheet is a child of that element. Anything else means that the focus
52202
+ * is on an element that needs to keep it.
52203
+ */
52204
+ if (!this.spreadsheetRef.el.contains(document.activeElement) &&
52205
+ document.activeElement?.contains(this.spreadsheetRef.el)) {
52206
+ this.focusGrid();
52207
+ }
52208
+ }, () => [this.env.model.getters.getActiveSheetId()]);
51978
52209
  useExternalListener(window, "resize", () => this.render(true));
51979
52210
  useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
51980
52211
  this.bindModelEvents();
@@ -52056,7 +52287,7 @@ class Spreadsheet extends Component {
52056
52287
  }
52057
52288
  focusGrid() {
52058
52289
  if (!this._focusGrid) {
52059
- throw new Error("_focusGrid should be exposed by the grid component");
52290
+ return;
52060
52291
  }
52061
52292
  this._focusGrid();
52062
52293
  }
@@ -55449,19 +55680,22 @@ class Model extends EventBus {
55449
55680
  * Check if the given command is allowed by all the plugins and the history.
55450
55681
  */
55451
55682
  checkDispatchAllowed(command) {
55452
- if (isCoreCommand(command)) {
55453
- return this.checkDispatchAllowedCoreCommand(command);
55683
+ const results = isCoreCommand(command)
55684
+ ? this.checkDispatchAllowedCoreCommand(command)
55685
+ : this.checkDispatchAllowedLocalCommand(command);
55686
+ if (results.some((r) => r !== "Success" /* CommandResult.Success */)) {
55687
+ return new DispatchResult(results.flat());
55454
55688
  }
55455
- return this.checkDispatchAllowedLocalCommand(command);
55689
+ return DispatchResult.Success;
55456
55690
  }
55457
55691
  checkDispatchAllowedCoreCommand(command) {
55458
55692
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55459
55693
  results.push(this.range.allowDispatch(command));
55460
- return new DispatchResult(results.flat());
55694
+ return results;
55461
55695
  }
55462
55696
  checkDispatchAllowedLocalCommand(command) {
55463
55697
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55464
- return new DispatchResult(results.flat());
55698
+ return results;
55465
55699
  }
55466
55700
  finalize() {
55467
55701
  this.status = 3 /* Status.Finalizing */;
@@ -55756,6 +55990,7 @@ const components = {
55756
55990
  FigureComponent,
55757
55991
  Menu,
55758
55992
  SelectionInput,
55993
+ ValidationMessages,
55759
55994
  };
55760
55995
  const hooks = {
55761
55996
  useDragAndDropListItems,
@@ -55773,6 +56008,6 @@ const constants = {
55773
56008
  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
56009
 
55775
56010
 
55776
- __info__.version = "17.1.0-alpha.3";
55777
- __info__.date = "2023-11-16T12:04:09.636Z";
55778
- __info__.hash = "2ef5b1a";
56011
+ __info__.version = "17.1.0-alpha.4";
56012
+ __info__.date = "2023-11-24T13:12:24.882Z";
56013
+ __info__.hash = "255821b";