@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
  'use strict';
@@ -229,7 +229,6 @@ const DEFAULT_CELL_HEIGHT = 23;
229
229
  const SCROLLBAR_WIDTH = 15;
230
230
  const AUTOFILL_EDGE_LENGTH = 8;
231
231
  const ICON_EDGE_LENGTH = 18;
232
- const UNHIDE_ICON_EDGE_LENGTH = 14;
233
232
  const MIN_CF_ICON_MARGIN = 4;
234
233
  const MIN_CELL_TEXT_MARGIN = 4;
235
234
  const CF_ICON_EDGE_LENGTH = 15;
@@ -693,8 +692,7 @@ function removeFalsyAttributes(obj) {
693
692
  *
694
693
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
695
694
  */
696
- const whiteSpaceCharacters = [
697
- " ",
695
+ const whiteSpaceSpecialCharacters = [
698
696
  "\t",
699
697
  "\f",
700
698
  "\v",
@@ -709,8 +707,7 @@ const whiteSpaceCharacters = [
709
707
  String.fromCharCode(parseInt("3000", 16)),
710
708
  String.fromCharCode(parseInt("feff", 16)),
711
709
  ];
712
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
713
- const newLineRegex = /\r\n|\r|\n/g;
710
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
714
711
  /**
715
712
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
716
713
  * different newlines types by \n.
@@ -718,9 +715,9 @@ const newLineRegex = /\r\n|\r|\n/g;
718
715
  function replaceSpecialSpaces(text) {
719
716
  if (!text)
720
717
  return "";
721
- text = text.replace(whiteSpaceRegexp, " ");
722
- text = text.replace(newLineRegex, NEWLINE);
723
- return text;
718
+ if (!whiteSpaceRegexp.test(text))
719
+ return text;
720
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
724
721
  }
725
722
  /**
726
723
  * Determine if the numbers are consecutive.
@@ -2062,6 +2059,7 @@ exports.CommandResult = void 0;
2062
2059
  CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
2063
2060
  CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
2064
2061
  CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
2062
+ CommandResult["NoChanges"] = "NoChanges";
2065
2063
  })(exports.CommandResult || (exports.CommandResult = {}));
2066
2064
 
2067
2065
  const DEFAULT_LOCALES = [
@@ -2191,12 +2189,12 @@ function toString(value) {
2191
2189
  }
2192
2190
  }
2193
2191
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2194
- function normalizeString(str) {
2192
+ const normalizeString = memoize(function normalizeString(str) {
2195
2193
  return str
2196
2194
  .toLowerCase()
2197
2195
  .normalize("NFD")
2198
2196
  .replace(/[\u0300-\u036f]/g, "");
2199
- }
2197
+ });
2200
2198
  const expectBooleanValueError = (value) => _t("The function [[FUNCTION_NAME]] expects a boolean value, but '%s' is a text, and cannot be coerced to a number.", value);
2201
2199
  function toBoolean(value) {
2202
2200
  switch (typeof value) {
@@ -3429,11 +3427,18 @@ function isSingleCellReference(xc) {
3429
3427
  return singleCellReference.test(xc);
3430
3428
  }
3431
3429
  function splitReference(ref) {
3430
+ if (!ref.includes("!")) {
3431
+ return { xc: ref };
3432
+ }
3432
3433
  const parts = ref.split("!");
3433
3434
  const xc = parts.pop();
3434
3435
  const sheetName = getUnquotedSheetName(parts.join("!")) || undefined;
3435
3436
  return { sheetName, xc };
3436
3437
  }
3438
+ /** Return a reference SheetName!xc from the given arguments */
3439
+ function getFullReference(sheetName, xc) {
3440
+ return sheetName !== undefined ? `${getCanonicalSheetName(sheetName)}!${xc}` : xc;
3441
+ }
3437
3442
 
3438
3443
  /**
3439
3444
  * Convert from a cartesian reference to a Zone
@@ -4542,14 +4547,14 @@ function drawDecoratedText(context, text, position, underline = false, strikethr
4542
4547
  switch (context.textBaseline) {
4543
4548
  case "top":
4544
4549
  underlineY += boxHeight - 2 * strokeWidth;
4545
- strikeY += boxHeight - textHeight;
4550
+ strikeY += boxHeight / 2 - strokeWidth;
4546
4551
  break;
4547
4552
  case "middle":
4548
4553
  underlineY += boxHeight / 2 - strokeWidth;
4549
4554
  break;
4550
4555
  case "alphabetic":
4551
4556
  underlineY += 2 * strokeWidth;
4552
- strikeY -= textHeight / 2 - strokeWidth / 2;
4557
+ strikeY -= 3 * strokeWidth;
4553
4558
  break;
4554
4559
  case "bottom":
4555
4560
  underlineY = y;
@@ -4612,6 +4617,7 @@ function createAction(item) {
4612
4617
  const children = item.children;
4613
4618
  const description = item.description;
4614
4619
  const icon = item.icon;
4620
+ const secondaryIcon = item.secondaryIcon;
4615
4621
  return {
4616
4622
  id: item.id || uuidGenerator$2.uuidv4(),
4617
4623
  name: typeof name === "function" ? name : () => name,
@@ -4630,6 +4636,7 @@ function createAction(item) {
4630
4636
  isReadonlyAllowed: item.isReadonlyAllowed || false,
4631
4637
  separator: item.separator || false,
4632
4638
  icon: typeof icon === "function" ? icon : () => icon || "",
4639
+ secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
4633
4640
  description: typeof description === "function" ? description : () => description || "",
4634
4641
  textColor: item.textColor,
4635
4642
  sequence: item.sequence || 0,
@@ -6985,11 +6992,9 @@ css /* scss */ `
6985
6992
  padding: ${MENU_VERTICAL_PADDING}px 0px;
6986
6993
  width: ${MENU_WIDTH}px;
6987
6994
  box-sizing: border-box !important;
6995
+ user-select: none;
6988
6996
 
6989
6997
  .o-menu-item {
6990
- display: flex;
6991
- justify-content: space-between;
6992
- align-items: center;
6993
6998
  box-sizing: border-box;
6994
6999
  height: ${MENU_ITEM_HEIGHT}px;
6995
7000
  padding: ${MENU_ITEM_PADDING_VERTICAL}px ${MENU_ITEM_PADDING_HORIZONTAL}px;
@@ -7000,20 +7005,12 @@ css /* scss */ `
7000
7005
  min-width: 40%;
7001
7006
  }
7002
7007
 
7003
- &.o-menu-root {
7004
- display: flex;
7005
- justify-content: space-between;
7006
- }
7007
-
7008
7008
  .o-menu-item-icon {
7009
7009
  display: inline-block;
7010
7010
  margin: 0px 8px 0px 0px;
7011
7011
  width: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
7012
7012
  line-height: ${MENU_ITEM_HEIGHT - 2 * MENU_ITEM_PADDING_VERTICAL}px;
7013
7013
  }
7014
- .o-menu-item-root {
7015
- width: 10px;
7016
- }
7017
7014
 
7018
7015
  &:not(.disabled) {
7019
7016
  &:hover,
@@ -7356,7 +7353,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7356
7353
  return result;
7357
7354
  }
7358
7355
  function tokenizeDebugger(chars) {
7359
- if (chars.current() === "?") {
7356
+ if (chars.current === "?") {
7360
7357
  chars.shift();
7361
7358
  return { type: "DEBUGGER", value: "?" };
7362
7359
  }
@@ -7367,7 +7364,7 @@ const misc$1 = {
7367
7364
  ")": "RIGHT_PAREN",
7368
7365
  };
7369
7366
  function tokenizeMisc(chars) {
7370
- if (chars.current() in misc$1) {
7367
+ if (chars.current in misc$1) {
7371
7368
  const value = chars.shift();
7372
7369
  const type = misc$1[value];
7373
7370
  return { type, value };
@@ -7375,7 +7372,7 @@ function tokenizeMisc(chars) {
7375
7372
  return null;
7376
7373
  }
7377
7374
  function tokenizeArgsSeparator(chars, locale) {
7378
- if (chars.current() === locale.formulaArgSeparator) {
7375
+ if (chars.current === locale.formulaArgSeparator) {
7379
7376
  const value = chars.shift();
7380
7377
  const type = "ARG_SEPARATOR";
7381
7378
  return { type, value };
@@ -7400,14 +7397,13 @@ function tokenizeNumber(chars, locale) {
7400
7397
  return null;
7401
7398
  }
7402
7399
  function tokenizeString(chars) {
7403
- if (chars.current() === '"') {
7400
+ if (chars.current === '"') {
7404
7401
  const startChar = chars.shift();
7405
7402
  let letters = startChar;
7406
- while (chars.current() &&
7407
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
7403
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
7408
7404
  letters += chars.shift();
7409
7405
  }
7410
- if (chars.current() === '"') {
7406
+ if (chars.current === '"') {
7411
7407
  letters += chars.shift();
7412
7408
  }
7413
7409
  return {
@@ -7434,14 +7430,14 @@ function tokenizeSymbol(chars) {
7434
7430
  let result = "";
7435
7431
  // there are two main cases to manage: either something which starts with
7436
7432
  // a ', like 'Sheet 2'A2, or a word-like element.
7437
- if (chars.current() === "'") {
7433
+ if (chars.current === "'") {
7438
7434
  let lastChar = chars.shift();
7439
7435
  result += lastChar;
7440
- while (chars.current()) {
7436
+ while (chars.current) {
7441
7437
  lastChar = chars.shift();
7442
7438
  result += lastChar;
7443
7439
  if (lastChar === "'") {
7444
- if (chars.current() && chars.current() === "'") {
7440
+ if (chars.current && chars.current === "'") {
7445
7441
  lastChar = chars.shift();
7446
7442
  result += lastChar;
7447
7443
  }
@@ -7457,7 +7453,7 @@ function tokenizeSymbol(chars) {
7457
7453
  };
7458
7454
  }
7459
7455
  }
7460
- while (chars.current() && separatorRegexp.test(chars.current())) {
7456
+ while (chars.current && separatorRegexp.test(chars.current)) {
7461
7457
  result += chars.shift();
7462
7458
  }
7463
7459
  if (result.length) {
@@ -7472,14 +7468,14 @@ function tokenizeSymbol(chars) {
7472
7468
  }
7473
7469
  function tokenizeSpace(chars) {
7474
7470
  let length = 0;
7475
- while (chars.current() === NEWLINE) {
7471
+ while (chars.current === NEWLINE) {
7476
7472
  length++;
7477
7473
  chars.shift();
7478
7474
  }
7479
7475
  if (length) {
7480
7476
  return { type: "SPACE", value: NEWLINE.repeat(length) };
7481
7477
  }
7482
- while (chars.current() === " ") {
7478
+ while (chars.current === " ") {
7483
7479
  length++;
7484
7480
  chars.shift();
7485
7481
  }
@@ -7498,17 +7494,20 @@ function tokenizeInvalidRange(chars) {
7498
7494
  class TokenizingChars {
7499
7495
  text;
7500
7496
  currentIndex = 0;
7497
+ current;
7501
7498
  constructor(text) {
7502
7499
  this.text = text;
7503
- }
7504
- current() {
7505
- return this.text[this.currentIndex];
7500
+ this.current = text[0];
7506
7501
  }
7507
7502
  shift() {
7508
- return this.text[this.currentIndex++];
7503
+ const current = this.current;
7504
+ const next = this.text[++this.currentIndex];
7505
+ this.current = next;
7506
+ return current;
7509
7507
  }
7510
7508
  advanceBy(length) {
7511
7509
  this.currentIndex += length;
7510
+ this.current = this.text[this.currentIndex];
7512
7511
  }
7513
7512
  isOver() {
7514
7513
  return this.currentIndex >= this.text.length;
@@ -11312,7 +11311,7 @@ const EXPAND = {
11312
11311
  ) {
11313
11312
  const _array = toMatrix(arg);
11314
11313
  const _nbRows = toInteger(rows?.value, this.locale);
11315
- const _nbColumns = columns !== undefined ? toInteger(columns.value, this.local) : _array.length;
11314
+ const _nbColumns = columns !== undefined ? toInteger(columns.value, this.locale) : _array.length;
11316
11315
  assert(() => _nbRows >= _array[0].length, _t("The rows arguments (%s) must be greater or equal than the number of rows of the array.", _nbRows.toString()));
11317
11316
  assert(() => _nbColumns >= _array.length, _t("The columns arguments (%s) must be greater or equal than the number of columns of the array.", _nbColumns.toString()));
11318
11317
  return generateMatrix(_nbColumns, _nbRows, (col, row) => col >= _array.length || row >= _array[col].length ? padWith : _array[col][row]);
@@ -11801,6 +11800,10 @@ var misc = /*#__PURE__*/Object.freeze({
11801
11800
  FORMAT_LARGE_NUMBER: FORMAT_LARGE_NUMBER
11802
11801
  });
11803
11802
 
11803
+ function sum(values, locale) {
11804
+ return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
11805
+ }
11806
+
11804
11807
  const DEFAULT_FACTOR = 1;
11805
11808
  const DEFAULT_MODE = 0;
11806
11809
  const DEFAULT_PLACES = 0;
@@ -12752,7 +12755,7 @@ const SUM = {
12752
12755
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
12753
12756
  },
12754
12757
  compute: function (...values) {
12755
- return reduceNumbers(values, (acc, a) => acc + a, 0, this.locale);
12758
+ return sum(values, this.locale);
12756
12759
  },
12757
12760
  isExported: true,
12758
12761
  };
@@ -12921,6 +12924,44 @@ function assertSameNumberOfElements(...args) {
12921
12924
  const dims = args[0].length;
12922
12925
  args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
12923
12926
  }
12927
+ function average(values, locale) {
12928
+ let count = 0;
12929
+ const sum = reduceNumbers(values, (acc, a) => {
12930
+ count += 1;
12931
+ return acc + a;
12932
+ }, 0, locale);
12933
+ assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
12934
+ return sum / count;
12935
+ }
12936
+ function countNumbers(values, locale) {
12937
+ let count = 0;
12938
+ for (let n of values) {
12939
+ if (isMatrix(n)) {
12940
+ for (let i of n) {
12941
+ for (let j of i) {
12942
+ if (typeof j === "number") {
12943
+ count += 1;
12944
+ }
12945
+ }
12946
+ }
12947
+ }
12948
+ else if (typeof n !== "string" || isNumber(n, locale) || parseDateTime(n, locale)) {
12949
+ count += 1;
12950
+ }
12951
+ }
12952
+ return count;
12953
+ }
12954
+ function countAny(values) {
12955
+ return reduceAny(values, (acc, a) => (a !== undefined && a !== null ? acc + 1 : acc), 0);
12956
+ }
12957
+ function max(values, locale) {
12958
+ const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
12959
+ return result === -Infinity ? 0 : result;
12960
+ }
12961
+ function min(values, locale) {
12962
+ const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
12963
+ return result === Infinity ? 0 : result;
12964
+ }
12924
12965
 
12925
12966
  function filterAndFlatData(dataY, dataX) {
12926
12967
  const _flatDataY = [];
@@ -13191,13 +13232,7 @@ const AVERAGE = {
13191
13232
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13192
13233
  },
13193
13234
  compute: function (...values) {
13194
- let count = 0;
13195
- const sum = reduceNumbers(values, (acc, a) => {
13196
- count += 1;
13197
- return acc + a;
13198
- }, 0, this.locale);
13199
- assert(() => count !== 0, _t("Evaluation of function [[FUNCTION_NAME]] caused a divide by zero error."));
13200
- return sum / count;
13235
+ return average(values, this.locale);
13201
13236
  },
13202
13237
  isExported: true,
13203
13238
  };
@@ -13355,24 +13390,7 @@ const COUNT = {
13355
13390
  ],
13356
13391
  returns: ["NUMBER"],
13357
13392
  compute: function (...values) {
13358
- let count = 0;
13359
- for (let n of values) {
13360
- if (isMatrix(n)) {
13361
- for (let i of n) {
13362
- for (let j of i) {
13363
- if (typeof j === "number") {
13364
- count += 1;
13365
- }
13366
- }
13367
- }
13368
- }
13369
- else if (typeof n !== "string" ||
13370
- isNumber(n, this.locale) ||
13371
- parseDateTime(n, this.locale)) {
13372
- count += 1;
13373
- }
13374
- }
13375
- return count;
13393
+ return countNumbers(values, this.locale);
13376
13394
  },
13377
13395
  isExported: true,
13378
13396
  };
@@ -13387,7 +13405,7 @@ const COUNTA = {
13387
13405
  ],
13388
13406
  returns: ["NUMBER"],
13389
13407
  compute: function (...values) {
13390
- return reduceAny(values, (acc, a) => (a !== undefined && a !== null ? acc + 1 : acc), 0);
13408
+ return countAny(values);
13391
13409
  },
13392
13410
  isExported: true,
13393
13411
  };
@@ -13617,8 +13635,7 @@ const MAX = {
13617
13635
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13618
13636
  },
13619
13637
  compute: function (...values) {
13620
- const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, this.locale);
13621
- return result === -Infinity ? 0 : result;
13638
+ return max(values, this.locale);
13622
13639
  },
13623
13640
  isExported: true,
13624
13641
  };
@@ -13705,8 +13722,7 @@ const MIN = {
13705
13722
  return isMatrix(value1) ? value1[0][0]?.format : value1?.format;
13706
13723
  },
13707
13724
  compute: function (...values) {
13708
- const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, this.locale);
13709
- return result === Infinity ? 0 : result;
13725
+ return min(values, this.locale);
13710
13726
  },
13711
13727
  isExported: true,
13712
13728
  };
@@ -17877,6 +17893,149 @@ var financial = /*#__PURE__*/Object.freeze({
17877
17893
  YIELDMAT: YIELDMAT
17878
17894
  });
17879
17895
 
17896
+ /**
17897
+ * Change the reference types inside the given token, if the token represent a range or a cell
17898
+ *
17899
+ * Eg. :
17900
+ * A1 => $A$1 => A$1 => $A1 => A1
17901
+ * A1:$B$1 => $A$1:B$1 => A$1:$B1 => $A1:B1 => A1:$B$1
17902
+ */
17903
+ function loopThroughReferenceType(token) {
17904
+ if (token.type !== "REFERENCE")
17905
+ return token;
17906
+ const { xc, sheetName } = splitReference(token.value);
17907
+ const [left, right] = xc.split(":");
17908
+ const updatedLeft = getTokenNextReferenceType(left);
17909
+ const updatedRight = right ? `:${getTokenNextReferenceType(right)}` : "";
17910
+ return { ...token, value: getFullReference(sheetName, updatedLeft + updatedRight) };
17911
+ }
17912
+ /**
17913
+ * Get a new token with a changed type of reference from the given cell token symbol.
17914
+ * Undefined behavior if given a token other than a cell or if the Xc contains a sheet reference
17915
+ *
17916
+ * A1 => $A$1 => A$1 => $A1 => A1
17917
+ */
17918
+ function getTokenNextReferenceType(xc) {
17919
+ switch (getReferenceType(xc)) {
17920
+ case "none":
17921
+ xc = setXcToFixedReferenceType(xc, "colrow");
17922
+ break;
17923
+ case "colrow":
17924
+ xc = setXcToFixedReferenceType(xc, "row");
17925
+ break;
17926
+ case "row":
17927
+ xc = setXcToFixedReferenceType(xc, "col");
17928
+ break;
17929
+ case "col":
17930
+ xc = setXcToFixedReferenceType(xc, "none");
17931
+ break;
17932
+ }
17933
+ return xc;
17934
+ }
17935
+ /**
17936
+ * Returns the given XC with the given reference type. The XC string should not contain a sheet name.
17937
+ */
17938
+ function setXcToFixedReferenceType(xc, referenceType) {
17939
+ if (xc.includes("!")) {
17940
+ throw new Error("The given XC should not contain a sheet name");
17941
+ }
17942
+ xc = xc.replace(/\$/g, "");
17943
+ let indexOfNumber;
17944
+ switch (referenceType) {
17945
+ case "col":
17946
+ return "$" + xc;
17947
+ case "row":
17948
+ indexOfNumber = xc.search(/[0-9]/);
17949
+ return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
17950
+ case "colrow":
17951
+ indexOfNumber = xc.search(/[0-9]/);
17952
+ xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
17953
+ return "$" + xc;
17954
+ case "none":
17955
+ return xc;
17956
+ }
17957
+ }
17958
+ /**
17959
+ * Return the type of reference used in the given XC of a cell.
17960
+ * Undefined behavior if the XC have a sheet reference
17961
+ */
17962
+ function getReferenceType(xcCell) {
17963
+ if (isColAndRowFixed(xcCell)) {
17964
+ return "colrow";
17965
+ }
17966
+ else if (isColFixed(xcCell)) {
17967
+ return "col";
17968
+ }
17969
+ else if (isRowFixed(xcCell)) {
17970
+ return "row";
17971
+ }
17972
+ return "none";
17973
+ }
17974
+ function isColFixed(xc) {
17975
+ return xc.startsWith("$");
17976
+ }
17977
+ function isRowFixed(xc) {
17978
+ return xc.includes("$", 1);
17979
+ }
17980
+ function isColAndRowFixed(xc) {
17981
+ return xc.startsWith("$") && xc.length > 1 && xc.slice(1).includes("$");
17982
+ }
17983
+
17984
+ // -----------------------------------------------------------------------------
17985
+ // CELL
17986
+ // -----------------------------------------------------------------------------
17987
+ // NOTE: missing from Excel: "color", "filename", "parentheses", "prefix", "protect" and "width"
17988
+ const CELL_INFO_TYPES = ["address", "col", "contents", "format", "row", "type"];
17989
+ const CELL = {
17990
+ description: _t("Gets information about a cell."),
17991
+ args: [
17992
+ arg("info_type (string)", _t("The type of information requested. Can be one of %s", CELL_INFO_TYPES.join(", "))),
17993
+ arg("reference (meta)", _t("The reference to the cell.")),
17994
+ ],
17995
+ returns: ["ANY"],
17996
+ compute: function (info, reference) {
17997
+ const _info = toString(info).toLowerCase();
17998
+ assert(() => CELL_INFO_TYPES.includes(_info), _t("The info_type should be one of %s.", CELL_INFO_TYPES.join(", ")));
17999
+ const sheetId = this.__originSheetId;
18000
+ const topLeftReference = reference.includes(":") ? reference.split(":")[0] : reference;
18001
+ let { sheetName, xc } = splitReference(topLeftReference);
18002
+ // only put the sheet name if the referenced range is in another sheet than the cell the formula is on
18003
+ sheetName = sheetName === this.getters.getSheetName(sheetId) ? undefined : sheetName;
18004
+ const fixedRef = getFullReference(sheetName, setXcToFixedReferenceType(xc, "colrow"));
18005
+ const range = this.getters.getRangeFromSheetXC(sheetId, fixedRef);
18006
+ switch (_info) {
18007
+ case "address":
18008
+ return this.getters.getRangeString(range, sheetId);
18009
+ case "col":
18010
+ return range.zone.left + 1;
18011
+ case "contents": {
18012
+ const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
18013
+ return this.getters.getEvaluatedCell(position).value;
18014
+ }
18015
+ case "format": {
18016
+ const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
18017
+ return this.getters.getEvaluatedCell(position).format || "";
18018
+ }
18019
+ case "row":
18020
+ return range.zone.top + 1;
18021
+ case "type": {
18022
+ const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
18023
+ const type = this.getters.getEvaluatedCell(position).type;
18024
+ if (type === CellValueType.empty) {
18025
+ return "b"; // blank
18026
+ }
18027
+ else if (type === CellValueType.text) {
18028
+ return "l"; // label
18029
+ }
18030
+ else {
18031
+ return "v"; // value
18032
+ }
18033
+ }
18034
+ }
18035
+ return "";
18036
+ },
18037
+ isExported: true,
18038
+ };
17880
18039
  // -----------------------------------------------------------------------------
17881
18040
  // ISERR
17882
18041
  // -----------------------------------------------------------------------------
@@ -18032,6 +18191,7 @@ const NA = {
18032
18191
 
18033
18192
  var info = /*#__PURE__*/Object.freeze({
18034
18193
  __proto__: null,
18194
+ CELL: CELL,
18035
18195
  ISBLANK: ISBLANK,
18036
18196
  ISERR: ISERR,
18037
18197
  ISERROR: ISERROR,
@@ -18323,7 +18483,7 @@ const ADDRESS = {
18323
18483
  cellReference = rowPart + colPart;
18324
18484
  }
18325
18485
  if (sheet !== undefined) {
18326
- return `${getCanonicalSheetName(toString(sheet))}!${cellReference}`;
18486
+ return getFullReference(toString(sheet), cellReference);
18327
18487
  }
18328
18488
  return cellReference;
18329
18489
  },
@@ -18339,7 +18499,7 @@ const COLUMN = {
18339
18499
  ],
18340
18500
  returns: ["NUMBER"],
18341
18501
  compute: function (cellReference) {
18342
- const _cellReference = cellReference || this.__originCellXC?.();
18502
+ const _cellReference = cellReference || this.__originCellXC();
18343
18503
  assert(() => !!_cellReference, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18344
18504
  const zone = toZone(_cellReference);
18345
18505
  return zone.left + 1;
@@ -18506,7 +18666,7 @@ const ROW = {
18506
18666
  ],
18507
18667
  returns: ["NUMBER"],
18508
18668
  compute: function (cellReference) {
18509
- cellReference = cellReference || this.__originCellXC?.();
18669
+ cellReference = cellReference || this.__originCellXC();
18510
18670
  assert(() => !!cellReference, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18511
18671
  const zone = toZone(cellReference);
18512
18672
  return zone.top + 1;
@@ -23858,31 +24018,65 @@ css /* scss */ `
23858
24018
  `;
23859
24019
  class FindAndReplacePanel extends owl.Component {
23860
24020
  static template = "o-spreadsheet-FindAndReplacePanel";
23861
- state = owl.useState(this.initialState());
24021
+ static components = { SelectionInput };
23862
24022
  debounceTimeoutId;
23863
- showFormulaState = false;
24023
+ initialShowFormulaState = false;
24024
+ dataRange = "";
23864
24025
  searchInput = owl.useRef("searchInput");
24026
+ replaceInput = owl.useRef("replaceInput");
23865
24027
  get hasSearchResult() {
23866
24028
  return this.env.model.getters.getCurrentSelectedMatchIndex() !== null;
23867
24029
  }
23868
24030
  get pendingSearch() {
23869
24031
  return this.debounceTimeoutId !== undefined;
23870
24032
  }
24033
+ get searchOptions() {
24034
+ return this.env.model.getters.getSearchOptions();
24035
+ }
24036
+ get toSearch() {
24037
+ return this.searchInput.el?.value || "";
24038
+ }
24039
+ get toReplace() {
24040
+ return this.replaceInput.el?.value || "";
24041
+ }
24042
+ get allSheetsMatchesCount() {
24043
+ return _t("%s in all sheets", this.env.model.getters.getAllSheetMatchesCount());
24044
+ }
24045
+ get currentSheetMatchesCount() {
24046
+ return _t("%(matches)s in sheet %(sheetName)s", {
24047
+ matches: this.env.model.getters.getActiveSheetMatchesCount(),
24048
+ sheetName: this.env.model.getters.getSheetName(this.env.model.getters.getActiveSheetId()),
24049
+ });
24050
+ }
24051
+ get specificRangeMatchesCount() {
24052
+ const range = this.searchOptions.specificRange;
24053
+ if (!range) {
24054
+ return "";
24055
+ }
24056
+ const { _sheetId, _zone } = range;
24057
+ return _t("%(matches)s in range %(range)s of sheet %(sheetName)s", {
24058
+ matches: this.env.model.getters.getSpecificRangeMatchesCount().toString(),
24059
+ range: zoneToXc(_zone),
24060
+ sheetName: this.env.model.getters.getSheetName(_sheetId),
24061
+ });
24062
+ }
23871
24063
  setup() {
23872
- this.showFormulaState = this.env.model.getters.shouldShowFormulas();
24064
+ this.initialShowFormulaState = this.env.model.getters.shouldShowFormulas();
23873
24065
  owl.onMounted(() => this.searchInput.el?.focus());
23874
24066
  owl.onWillUnmount(() => {
23875
24067
  clearTimeout(this.debounceTimeoutId);
23876
24068
  this.env.model.dispatch("CLEAR_SEARCH");
23877
- this.env.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.showFormulaState });
24069
+ this.env.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.initialShowFormulaState });
23878
24070
  });
23879
24071
  owl.useEffect(() => {
23880
- this.state.searchOptions.searchFormulas = this.env.model.getters.shouldShowFormulas();
23881
- this.searchFormulas();
24072
+ const showFormula = this.env.model.getters.shouldShowFormulas();
24073
+ this.updateSearch({ searchFormulas: showFormula });
23882
24074
  }, () => [this.env.model.getters.shouldShowFormulas()]);
23883
24075
  }
23884
- onInput(ev) {
23885
- this.state.toSearch = ev.target.value;
24076
+ onFocusSearch() {
24077
+ this.updateDataRange();
24078
+ }
24079
+ onInput() {
23886
24080
  this.debouncedUpdateSearch();
23887
24081
  }
23888
24082
  onKeydownSearch(ev) {
@@ -23899,11 +24093,36 @@ class FindAndReplacePanel extends owl.Component {
23899
24093
  this.replace();
23900
24094
  }
23901
24095
  }
23902
- searchFormulas() {
24096
+ searchFormulas(ev) {
24097
+ const showFormula = ev.target.checked;
23903
24098
  this.env.model.dispatch("SET_FORMULA_VISIBILITY", {
23904
- show: this.state.searchOptions.searchFormulas,
24099
+ show: showFormula,
23905
24100
  });
23906
- this.updateSearch();
24101
+ this.updateSearch({ searchFormulas: showFormula });
24102
+ }
24103
+ searchExactMatch(ev) {
24104
+ const exactMatch = ev.target.checked;
24105
+ this.updateSearch({ exactMatch });
24106
+ }
24107
+ searchMatchCase(ev) {
24108
+ const matchCase = ev.target.checked;
24109
+ this.updateSearch({ matchCase });
24110
+ }
24111
+ changeSearchScope(ev) {
24112
+ const searchScope = ev.target.value;
24113
+ this.updateSearch({ searchScope });
24114
+ }
24115
+ onSearchRangeChanged(ranges) {
24116
+ this.dataRange = ranges[0];
24117
+ }
24118
+ updateDataRange() {
24119
+ if (!this.dataRange) {
24120
+ return;
24121
+ }
24122
+ if (this.searchOptions.searchScope === "specificRange") {
24123
+ const specificRange = this.env.model.getters.getRangeFromSheetXC(this.env.model.getters.getActiveSheetId(), this.dataRange).rangeData;
24124
+ this.updateSearch({ specificRange });
24125
+ }
23907
24126
  }
23908
24127
  onSelectPreviousCell() {
23909
24128
  this.env.model.dispatch("SELECT_SEARCH_PREVIOUS_MATCH");
@@ -23911,10 +24130,14 @@ class FindAndReplacePanel extends owl.Component {
23911
24130
  onSelectNextCell() {
23912
24131
  this.env.model.dispatch("SELECT_SEARCH_NEXT_MATCH");
23913
24132
  }
23914
- updateSearch() {
24133
+ updateSearch(updateSearchOptions) {
24134
+ const searchOptions = {
24135
+ ...this.env.model.getters.getSearchOptions(),
24136
+ ...updateSearchOptions,
24137
+ };
23915
24138
  this.env.model.dispatch("UPDATE_SEARCH", {
23916
- toSearch: this.state.toSearch,
23917
- searchOptions: this.state.searchOptions,
24139
+ toSearch: this.toSearch,
24140
+ searchOptions,
23918
24141
  });
23919
24142
  }
23920
24143
  debouncedUpdateSearch() {
@@ -23926,28 +24149,14 @@ class FindAndReplacePanel extends owl.Component {
23926
24149
  }
23927
24150
  replace() {
23928
24151
  this.env.model.dispatch("REPLACE_SEARCH", {
23929
- replaceWith: this.state.replaceWith,
24152
+ replaceWith: this.toReplace,
23930
24153
  });
23931
24154
  }
23932
24155
  replaceAll() {
23933
24156
  this.env.model.dispatch("REPLACE_ALL_SEARCH", {
23934
- replaceWith: this.state.replaceWith,
24157
+ replaceWith: this.toReplace,
23935
24158
  });
23936
24159
  }
23937
- // ---------------------------------------------------------------------------
23938
- // Private
23939
- // ---------------------------------------------------------------------------
23940
- initialState() {
23941
- return {
23942
- toSearch: "",
23943
- replaceWith: "",
23944
- searchOptions: {
23945
- matchCase: false,
23946
- exactMatch: false,
23947
- searchFormulas: false,
23948
- },
23949
- };
23950
- }
23951
24160
  }
23952
24161
  FindAndReplacePanel.props = {
23953
24162
  onCloseSidePanel: Function,
@@ -27093,7 +27302,7 @@ class GridComposer extends owl.Component {
27093
27302
  get cellReference() {
27094
27303
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27095
27304
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27096
- return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
27305
+ return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
27097
27306
  }
27098
27307
  get cellReferenceStyle() {
27099
27308
  const { x: left, y: top } = this.rect;
@@ -28536,22 +28745,15 @@ css /* scss */ `
28536
28745
  height: 10000px;
28537
28746
  background-color: ${SELECTION_BORDER_COLOR};
28538
28747
  }
28539
- .o-unhide {
28540
- width: ${UNHIDE_ICON_EDGE_LENGTH}px;
28541
- height: ${UNHIDE_ICON_EDGE_LENGTH}px;
28542
- position: absolute;
28543
- overflow: hidden;
28544
- border-radius: 2px;
28545
- top: calc(${HEADER_HEIGHT}px / 2 - ${UNHIDE_ICON_EDGE_LENGTH}px / 2);
28748
+ .o-unhide-buttons {
28749
+ width: fit-content;
28750
+ gap: 5px;
28751
+ transform: translate(-50%, 0);
28546
28752
  }
28547
28753
  .o-unhide:hover {
28548
28754
  z-index: ${ComponentsImportance.Grid + 1};
28549
28755
  background-color: lightgrey;
28550
28756
  }
28551
- .o-unhide > svg {
28552
- position: relative;
28553
- top: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28554
- }
28555
28757
  }
28556
28758
  `;
28557
28759
  AbstractResizer.props = {
@@ -28661,8 +28863,8 @@ class ColResizer extends AbstractResizer {
28661
28863
  dimension: "COL",
28662
28864
  });
28663
28865
  }
28664
- unhideStyleValue(hiddenIndex) {
28665
- return this._getDimensionsInViewport(hiddenIndex).start;
28866
+ getUnhideButtonStyle(hiddenIndex) {
28867
+ return cssPropertiesToCss({ left: this._getDimensionsInViewport(hiddenIndex).start + "px" });
28666
28868
  }
28667
28869
  }
28668
28870
  css /* scss */ `
@@ -28708,18 +28910,9 @@ css /* scss */ `
28708
28910
  height: 1px;
28709
28911
  background-color: ${SELECTION_BORDER_COLOR};
28710
28912
  }
28711
- .o-unhide {
28712
- width: ${UNHIDE_ICON_EDGE_LENGTH}px;
28713
- height: ${UNHIDE_ICON_EDGE_LENGTH}px;
28714
- position: absolute;
28715
- overflow: hidden;
28716
- border-radius: 2px;
28717
- left: calc(${HEADER_WIDTH}px - ${UNHIDE_ICON_EDGE_LENGTH}px - 2px);
28718
- }
28719
- .o-unhide > svg {
28720
- position: relative;
28721
- left: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28722
- top: calc(${UNHIDE_ICON_EDGE_LENGTH}px / 2 - ${ICON_EDGE_LENGTH}px / 2);
28913
+ .o-unhide-buttons {
28914
+ height: fit-content;
28915
+ transform: translate(0, -50%);
28723
28916
  }
28724
28917
  .o-unhide:hover {
28725
28918
  z-index: ${ComponentsImportance.Grid + 1};
@@ -28834,8 +29027,8 @@ class RowResizer extends AbstractResizer {
28834
29027
  elements: hiddenElements,
28835
29028
  });
28836
29029
  }
28837
- unhideStyleValue(hiddenIndex) {
28838
- return this._getDimensionsInViewport(hiddenIndex).start;
29030
+ getUnhideButtonStyle(hiddenIndex) {
29031
+ return cssPropertiesToCss({ top: this._getDimensionsInViewport(hiddenIndex).start + "px" });
28839
29032
  }
28840
29033
  }
28841
29034
  css /* scss */ `
@@ -29342,7 +29535,6 @@ class Grid extends owl.Component {
29342
29535
  owl.onMounted(() => this.focus());
29343
29536
  this.props.exposeFocus(() => this.focus());
29344
29537
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29345
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
29346
29538
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29347
29539
  this.moveCanvas(deltaX, deltaY);
29348
29540
  this.hoveredCell.col = undefined;
@@ -29779,6 +29971,7 @@ class Grid extends owl.Component {
29779
29971
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29780
29972
  const target = this.env.model.getters.getSelectedZones();
29781
29973
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29974
+ const isCutOperation = this.env.model.getters.isCutOperation();
29782
29975
  if (clipboardString === content) {
29783
29976
  // the paste actually comes from o-spreadsheet itself
29784
29977
  interactivePaste(this.env, target);
@@ -29786,7 +29979,7 @@ class Grid extends owl.Component {
29786
29979
  else {
29787
29980
  interactivePasteFromOS(this.env, target, content);
29788
29981
  }
29789
- if (this.env.model.getters.isCutOperation()) {
29982
+ if (isCutOperation) {
29790
29983
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29791
29984
  }
29792
29985
  }
@@ -31399,12 +31592,6 @@ function convertChartData(chartData) {
31399
31592
  }
31400
31593
  function convertExcelRangeToSheetXC(range, dataSetsHaveTitle) {
31401
31594
  let { sheetName, xc } = splitReference(range);
31402
- if (sheetName) {
31403
- sheetName = getCanonicalSheetName(sheetName) + "!";
31404
- }
31405
- else {
31406
- sheetName = "";
31407
- }
31408
31595
  let zone = toUnboundedZone(xc);
31409
31596
  if (dataSetsHaveTitle && zone.bottom !== undefined && zone.right !== undefined) {
31410
31597
  const height = zone.bottom - zone.top + 1;
@@ -31417,7 +31604,7 @@ function convertExcelRangeToSheetXC(range, dataSetsHaveTitle) {
31417
31604
  }
31418
31605
  }
31419
31606
  const dataXC = zoneToXc(zone);
31420
- return sheetName + dataXC;
31607
+ return getFullReference(sheetName, dataXC);
31421
31608
  }
31422
31609
 
31423
31610
  /**
@@ -34080,6 +34267,14 @@ class BordersPlugin extends CorePlugin {
34080
34267
  // ---------------------------------------------------------------------------
34081
34268
  // Command Handling
34082
34269
  // ---------------------------------------------------------------------------
34270
+ allowDispatch(cmd) {
34271
+ switch (cmd.type) {
34272
+ case "SET_BORDER":
34273
+ return this.checkBordersUnchanged(cmd);
34274
+ default:
34275
+ return "Success" /* CommandResult.Success */;
34276
+ }
34277
+ }
34083
34278
  handle(cmd) {
34084
34279
  switch (cmd.type) {
34085
34280
  case "ADD_MERGE":
@@ -34499,6 +34694,14 @@ class BordersPlugin extends CorePlugin {
34499
34694
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34500
34695
  }
34501
34696
  }
34697
+ checkBordersUnchanged(cmd) {
34698
+ const currentBorder = this.getCellBorder(cmd);
34699
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
34700
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
34701
+ return "NoChanges" /* CommandResult.NoChanges */;
34702
+ }
34703
+ return "Success" /* CommandResult.Success */;
34704
+ }
34502
34705
  // ---------------------------------------------------------------------------
34503
34706
  // Import/Export
34504
34707
  // ---------------------------------------------------------------------------
@@ -35325,7 +35528,6 @@ class CellPlugin extends CorePlugin {
35325
35528
  static getters = [
35326
35529
  "zoneToXC",
35327
35530
  "getCells",
35328
- "getFormulaCellContent",
35329
35531
  "getTranslatedCellFormula",
35330
35532
  "getCellStyle",
35331
35533
  "getCellById",
@@ -35354,8 +35556,9 @@ class CellPlugin extends CorePlugin {
35354
35556
  allowDispatch(cmd) {
35355
35557
  switch (cmd.type) {
35356
35558
  case "UPDATE_CELL":
35559
+ return this.checkCellOutOfSheet(cmd);
35357
35560
  case "CLEAR_CELL":
35358
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35561
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35359
35562
  default:
35360
35563
  return "Success" /* CommandResult.Success */;
35361
35564
  }
@@ -35524,24 +35727,18 @@ class CellPlugin extends CorePlugin {
35524
35727
  */
35525
35728
  getCellById(cellId) {
35526
35729
  // this must be as fast as possible
35527
- for (const sheetId in this.cells) {
35528
- const sheet = this.cells[sheetId];
35529
- const cell = sheet[cellId];
35530
- if (cell) {
35531
- return cell;
35532
- }
35533
- }
35534
- return undefined;
35730
+ const position = this.getters.getCellPosition(cellId);
35731
+ const sheet = this.cells[position.sheetId];
35732
+ return sheet[cellId];
35535
35733
  }
35536
35734
  /*
35537
35735
  * Reconstructs the original formula string based on a normalized form and its dependencies
35538
35736
  */
35539
35737
  getFormulaCellContent(sheetId, compiledFormula, dependencies) {
35540
- const ranges = dependencies || compiledFormula.dependencies;
35541
35738
  let rangeIndex = 0;
35542
35739
  return concat(compiledFormula.tokens.map((token) => {
35543
35740
  if (token.type === "REFERENCE") {
35544
- const range = ranges[rangeIndex++];
35741
+ const range = dependencies[rangeIndex++];
35545
35742
  return this.getters.getRangeString(range, sheetId);
35546
35743
  }
35547
35744
  return token.value;
@@ -35762,13 +35959,23 @@ class CellPlugin extends CorePlugin {
35762
35959
  },
35763
35960
  };
35764
35961
  }
35765
- checkCellOutOfSheet(sheetId, col, row) {
35962
+ checkCellOutOfSheet(cmd) {
35963
+ const { sheetId, col, row } = cmd;
35766
35964
  const sheet = this.getters.tryGetSheet(sheetId);
35767
35965
  if (!sheet)
35768
35966
  return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
35769
35967
  const sheetZone = this.getters.getSheetZone(sheetId);
35770
35968
  return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */;
35771
35969
  }
35970
+ checkUselessClearCell(cmd) {
35971
+ const cell = this.getters.getCell(cmd);
35972
+ if (!cell)
35973
+ return "NoChanges" /* CommandResult.NoChanges */;
35974
+ if (!cell.content && !cell.style && !cell.format) {
35975
+ return "NoChanges" /* CommandResult.NoChanges */;
35976
+ }
35977
+ return "Success" /* CommandResult.Success */;
35978
+ }
35772
35979
  }
35773
35980
  class FormulaCellWithDependencies {
35774
35981
  id;
@@ -36458,6 +36665,9 @@ class DataValidationPlugin extends CorePlugin {
36458
36665
  return this.rules[sheetId].find((rule) => rule.id === id);
36459
36666
  }
36460
36667
  getValidationRuleForCell({ sheetId, col, row }) {
36668
+ if (!this.rules[sheetId]) {
36669
+ return undefined;
36670
+ }
36461
36671
  for (const rule of this.rules[sheetId]) {
36462
36672
  for (const range of rule.ranges) {
36463
36673
  if (isInside(col, row, range.zone)) {
@@ -37630,7 +37840,7 @@ class MergePlugin extends CorePlugin {
37630
37840
  const rangeString = this.getters.getRangeString(expandedRange, forSheetId);
37631
37841
  if (this.isSingleCellOrMerge(rangeImpl.sheetId, rangeImpl.zone)) {
37632
37842
  const { sheetName, xc } = splitReference(rangeString);
37633
- return `${sheetName !== undefined ? getCanonicalSheetName(sheetName) + "!" : ""}${xc.split(":")[0]}`;
37843
+ return getFullReference(sheetName, xc.split(":")[0]);
37634
37844
  }
37635
37845
  return rangeString;
37636
37846
  }
@@ -39055,10 +39265,13 @@ class SheetPlugin extends CorePlugin {
39055
39265
  // begin with the end.
39056
39266
  rows.sort((a, b) => b - a);
39057
39267
  for (let group of groupConsecutive(rows)) {
39268
+ // indexes are sorted in the descending order
39269
+ const from = group[group.length - 1];
39270
+ const to = group[0];
39058
39271
  // Move the cells.
39059
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
39060
- // Effectively delete the element and recompute the left-right/top-bottom.
39061
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
39272
+ this.moveCellOnRowsDeletion(sheet, from, to);
39273
+ // Effectively delete the rows
39274
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39062
39275
  }
39063
39276
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39064
39277
  if (count) {
@@ -39080,8 +39293,6 @@ class SheetPlugin extends CorePlugin {
39080
39293
  this.addEmptyRows(sheet, quantity);
39081
39294
  // Move the cells.
39082
39295
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39083
- // Recompute the left-right/top-bottom.
39084
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39085
39296
  if (index < sheet.panes.ySplit) {
39086
39297
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39087
39298
  }
@@ -39182,35 +39393,20 @@ class SheetPlugin extends CorePlugin {
39182
39393
  }
39183
39394
  }
39184
39395
  }
39185
- updateRowsStructureOnDeletion(index, sheet) {
39396
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39186
39397
  const rows = [];
39187
- const cellsQueue = sheet.rows.map((row) => row.cells);
39398
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39188
39399
  for (let i in sheet.rows) {
39189
- if (Number(i) === index) {
39400
+ const row = Number(i);
39401
+ if (row >= deleteFromRow && row <= deleteToRow) {
39190
39402
  continue;
39191
39403
  }
39192
39404
  rows.push({
39193
- cells: cellsQueue.shift(),
39405
+ cells: cellsQueue.pop(),
39194
39406
  });
39195
39407
  }
39196
39408
  this.history.update("sheets", sheet.id, "rows", rows);
39197
39409
  }
39198
- /**
39199
- * Update the rows of the sheet after an addition:
39200
- * - Rename the rows
39201
- *
39202
- * @param sheet Sheet on which the deletion occurs
39203
- * @param addedRow Index of the added row
39204
- * @param rowsToAdd Number of the rows to add
39205
- */
39206
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
39207
- const rows = [];
39208
- const cellsQueue = sheet.rows.map((row) => row.cells);
39209
- sheet.rows.forEach(() => rows.push({
39210
- cells: cellsQueue.shift(),
39211
- }));
39212
- this.history.update("sheets", sheet.id, "rows", rows);
39213
- }
39214
39410
  /**
39215
39411
  * Add empty rows at the end of the rows
39216
39412
  *
@@ -39737,6 +39933,7 @@ class CompilationParametersBuilder {
39737
39933
  getters;
39738
39934
  computeCell;
39739
39935
  evalContext;
39936
+ rangeCache = {};
39740
39937
  constructor(context, getters, computeCell) {
39741
39938
  this.getters = getters;
39742
39939
  this.computeCell = computeCell;
@@ -39759,7 +39956,8 @@ class CompilationParametersBuilder {
39759
39956
  refFn(range, isMeta, functionName, paramNumber) {
39760
39957
  if (isMeta) {
39761
39958
  // Use zoneToXc of zone instead of getRangeString to avoid sending unbounded ranges
39762
- return { value: zoneToXc(range.zone) };
39959
+ const sheetName = this.getters.getSheetName(range.sheetId);
39960
+ return { value: getFullReference(sheetName, zoneToXc(range.zone)) };
39763
39961
  }
39764
39962
  if (!isZoneValid(range.zone)) {
39765
39963
  throw new InvalidReferenceError();
@@ -39822,17 +40020,24 @@ class CompilationParametersBuilder {
39822
40020
  if (!_zone) {
39823
40021
  return [[]];
39824
40022
  }
40023
+ const { top, left, bottom, right } = zone;
40024
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
40025
+ if (cacheKey in this.rangeCache) {
40026
+ return this.rangeCache[cacheKey];
40027
+ }
39825
40028
  const height = _zone.bottom - _zone.top + 1;
39826
40029
  const width = _zone.right - _zone.left + 1;
39827
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
40030
+ const matrix = new Array(width);
39828
40031
  // Performance issue: nested loop is faster than a map here
39829
40032
  for (let col = _zone.left; col <= _zone.right; col++) {
40033
+ const colIndex = col - _zone.left;
40034
+ matrix[colIndex] = new Array(height);
39830
40035
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39831
- const colIndex = col - _zone.left;
39832
40036
  const rowIndex = row - _zone.top;
39833
40037
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39834
40038
  }
39835
40039
  }
40040
+ this.rangeCache[cacheKey] = matrix;
39836
40041
  return matrix;
39837
40042
  }
39838
40043
  }
@@ -40038,6 +40243,7 @@ class Evaluator {
40038
40243
  this.formulaDependencies().addDependencies(positionId, dependencies);
40039
40244
  }
40040
40245
  updateCompilationParameters() {
40246
+ // rebuild the compilation parameters (with a clean cache)
40041
40247
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40042
40248
  }
40043
40249
  evaluateCells(positions) {
@@ -40081,6 +40287,16 @@ class Evaluator {
40081
40287
  this.evaluatedCells = new Map();
40082
40288
  this.evaluate(this.getAllCells());
40083
40289
  }
40290
+ evaluateFormula(sheetId, formulaString) {
40291
+ const compiledFormula = compile(formulaString);
40292
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
40293
+ this.updateCompilationParameters();
40294
+ const result = updateEvalContextAndExecute({ ...compiledFormula, dependencies: ranges }, this.compilationParams, sheetId);
40295
+ if (isMatrix(result)) {
40296
+ return matrixMap(result, (cell) => cell.value);
40297
+ }
40298
+ return result.value;
40299
+ }
40084
40300
  getAllCells() {
40085
40301
  const positionIds = new JetSet();
40086
40302
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40107,6 +40323,7 @@ class Evaluator {
40107
40323
  this.nextPositionsToUpdate = cells;
40108
40324
  let currentIteration = 0;
40109
40325
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40326
+ this.updateCompilationParameters();
40110
40327
  const positionIds = Array.from(this.nextPositionsToUpdate);
40111
40328
  this.nextPositionsToUpdate.clear();
40112
40329
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40130,7 +40347,8 @@ class Evaluator {
40130
40347
  if (!this.blockedArrayFormulas.has(positionId)) {
40131
40348
  this.invalidateSpreading(positionId);
40132
40349
  }
40133
- const cell = this.getCell(positionId);
40350
+ const cellPosition = this.decodePosition(positionId);
40351
+ const cell = this.getters.getCell(cellPosition);
40134
40352
  if (cell === undefined) {
40135
40353
  return createEvaluatedCell("", { locale: this.getters.getLocale() });
40136
40354
  }
@@ -40141,7 +40359,7 @@ class Evaluator {
40141
40359
  }
40142
40360
  this.cellsBeingComputed.add(cellId);
40143
40361
  return cell.isFormula
40144
- ? this.computeFormulaCell(cell)
40362
+ ? this.computeFormulaCell(cellPosition.sheetId, cell)
40145
40363
  : evaluateLiteral(cell.content, { format: cell.format, locale: this.getters.getLocale() });
40146
40364
  }
40147
40365
  catch (e) {
@@ -40167,14 +40385,9 @@ class Evaluator {
40167
40385
  e.message = e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled);
40168
40386
  return errorCell(e);
40169
40387
  }
40170
- computeFormulaCell(cellData) {
40388
+ computeFormulaCell(sheetId, cellData) {
40171
40389
  const cellId = cellData.id;
40172
- this.compilationParams[2].__originCellXC = () => {
40173
- // compute the value lazily for performance reasons
40174
- const position = this.compilationParams[2].getters.getCellPosition(cellId);
40175
- return toXC(position.col, position.row);
40176
- };
40177
- const formulaReturn = cellData.compiledFormula.execute(cellData.compiledFormula.dependencies, ...this.compilationParams);
40390
+ const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, sheetId, cellId);
40178
40391
  if (!isMatrix(formulaReturn)) {
40179
40392
  return createEvaluatedCell(formulaReturn.value, {
40180
40393
  format: cellData.format || formulaReturn.format,
@@ -40376,6 +40589,18 @@ class PositionBitsEncoder {
40376
40589
  return sheetId;
40377
40590
  }
40378
40591
  }
40592
+ function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
40593
+ compilationParams[2].__originCellXC = lazy(() => {
40594
+ if (!cellId) {
40595
+ return undefined;
40596
+ }
40597
+ // compute the value lazily for performance reasons
40598
+ const position = compilationParams[2].getters.getCellPosition(cellId);
40599
+ return toXC(position.col, position.row);
40600
+ });
40601
+ compilationParams[2].__originSheetId = sheetId;
40602
+ return compiledFormula.execute(compiledFormula.dependencies, ...compilationParams);
40603
+ }
40379
40604
 
40380
40605
  //#region
40381
40606
  // ---------------------------------------------------------------------------
@@ -40470,7 +40695,6 @@ class PositionBitsEncoder {
40470
40695
  // of other cells depending on it, at the next iteration.
40471
40696
  //#endregion
40472
40697
  class EvaluationPlugin extends UIPlugin {
40473
- config;
40474
40698
  static getters = [
40475
40699
  "evaluateFormula",
40476
40700
  "getCorrespondingFormulaCell",
@@ -40481,15 +40705,13 @@ class EvaluationPlugin extends UIPlugin {
40481
40705
  "getEvaluatedCells",
40482
40706
  "getEvaluatedCellsInZone",
40483
40707
  "getSpreadPositionsOf",
40708
+ "getArrayFormulaSpreadingOn",
40484
40709
  ];
40485
40710
  shouldRebuildDependenciesGraph = true;
40486
40711
  evaluator;
40487
- compilationParams;
40488
40712
  positionsToUpdate = [];
40489
40713
  constructor(config) {
40490
40714
  super(config);
40491
- this.config = config;
40492
- this.compilationParams = this.getCompilationParameters();
40493
40715
  this.evaluator = new Evaluator(config.custom, this.getters);
40494
40716
  }
40495
40717
  // ---------------------------------------------------------------------------
@@ -40514,10 +40736,6 @@ class EvaluationPlugin extends UIPlugin {
40514
40736
  case "EVALUATE_CELLS":
40515
40737
  this.evaluator.evaluateAllCells();
40516
40738
  break;
40517
- case "UPDATE_LOCALE":
40518
- this.compilationParams = this.getCompilationParameters();
40519
- this.evaluator.updateCompilationParameters();
40520
- break;
40521
40739
  }
40522
40740
  }
40523
40741
  finalize() {
@@ -40535,16 +40753,7 @@ class EvaluationPlugin extends UIPlugin {
40535
40753
  // Getters
40536
40754
  // ---------------------------------------------------------------------------
40537
40755
  evaluateFormula(sheetId, formulaString) {
40538
- const compiledFormula = compile(formulaString);
40539
- const ranges = [];
40540
- for (let xc of compiledFormula.dependencies) {
40541
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
40542
- }
40543
- const array = compiledFormula.execute(ranges, ...this.compilationParams);
40544
- if (isMatrix(array)) {
40545
- return array.map((col) => col.map((row) => row.value));
40546
- }
40547
- return array.value;
40756
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40548
40757
  }
40549
40758
  /**
40550
40759
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40593,6 +40802,9 @@ class EvaluationPlugin extends UIPlugin {
40593
40802
  getSpreadPositionsOf(position) {
40594
40803
  return this.evaluator.getSpreadPositionsOf(position);
40595
40804
  }
40805
+ getArrayFormulaSpreadingOn(position) {
40806
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40807
+ }
40596
40808
  // ---------------------------------------------------------------------------
40597
40809
  // Export
40598
40810
  // ---------------------------------------------------------------------------
@@ -40636,7 +40848,7 @@ class EvaluationPlugin extends UIPlugin {
40636
40848
  else if (cell && cell.content) {
40637
40849
  return undefined;
40638
40850
  }
40639
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40851
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40640
40852
  if (spreadingFormulaPosition === undefined) {
40641
40853
  return undefined;
40642
40854
  }
@@ -40646,9 +40858,6 @@ class EvaluationPlugin extends UIPlugin {
40646
40858
  }
40647
40859
  return undefined;
40648
40860
  }
40649
- getCompilationParameters() {
40650
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40651
- }
40652
40861
  }
40653
40862
  function isBadExpression(tokens) {
40654
40863
  try {
@@ -43347,11 +43556,26 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43347
43556
  let cellsInRow = [];
43348
43557
  for (let col of columnsIndex) {
43349
43558
  const position = { col, row, sheetId };
43559
+ const spreader = getters.getArrayFormulaSpreadingOn(position);
43560
+ let cell = getters.getCell(position);
43561
+ const evaluatedCell = getters.getEvaluatedCell(position);
43562
+ if (spreader) {
43563
+ const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
43564
+ const content = isSpreaderCopied
43565
+ ? ""
43566
+ : formatValue(evaluatedCell.value, { locale: getters.getLocale() });
43567
+ cell = {
43568
+ id: cell?.id || "",
43569
+ style: cell?.style,
43570
+ format: evaluatedCell.format,
43571
+ content,
43572
+ isFormula: false,
43573
+ };
43574
+ }
43350
43575
  cellsInRow.push({
43351
- cell: getters.getCell(position),
43352
- style: getters.getCellComputedStyle(position),
43353
- evaluatedCell: getters.getEvaluatedCell(position),
43576
+ cell,
43354
43577
  border: getters.getCellBorder(position) || undefined,
43578
+ evaluatedCell,
43355
43579
  position,
43356
43580
  });
43357
43581
  }
@@ -43473,13 +43697,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43473
43697
  });
43474
43698
  }
43475
43699
  this.pasteCopiedTables(target);
43476
- this.cells.forEach((row) => {
43477
- row.forEach((c) => {
43478
- if (c.cell) {
43479
- c.cell = undefined;
43480
- }
43481
- });
43482
- });
43483
43700
  }
43484
43701
  /**
43485
43702
  * The clipped zone is copied as many times as it fits in the target.
@@ -43553,7 +43770,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43553
43770
  clearClippedZones() {
43554
43771
  for (const row of this.cells) {
43555
43772
  for (const cell of row) {
43556
- if (cell.cell) {
43773
+ if (cell?.cell) {
43557
43774
  this.dispatch("CLEAR_CELL", cell.position);
43558
43775
  }
43559
43776
  }
@@ -43579,6 +43796,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43579
43796
  const rowCells = this.cells[r];
43580
43797
  for (let c = 0; c < width; c++) {
43581
43798
  const origin = rowCells[c];
43799
+ if (!origin) {
43800
+ continue;
43801
+ }
43582
43802
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43583
43803
  // TODO: refactor this part. the "Paste merge" action is also executed with
43584
43804
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43602,53 +43822,42 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43602
43822
  pasteCell(origin, target, operation, clipboardOption) {
43603
43823
  const { sheetId, col, row } = target;
43604
43824
  const targetCell = this.getters.getEvaluatedCell(target);
43605
- if (clipboardOption?.pasteOption !== "onlyValue") {
43606
- const targetBorders = this.getters.getCellBorder(target);
43607
- const originBorders = origin.border;
43608
- const border = {
43609
- top: targetBorders?.top || originBorders?.top,
43610
- bottom: targetBorders?.bottom || originBorders?.bottom,
43611
- left: targetBorders?.left || originBorders?.left,
43612
- right: targetBorders?.right || originBorders?.right,
43613
- };
43614
- this.dispatch("SET_BORDER", { sheetId, col, row, border });
43825
+ if (clipboardOption?.pasteOption === "onlyValue") {
43826
+ const locale = this.getters.getLocale();
43827
+ const content = formatValue(origin.evaluatedCell.value, { locale });
43828
+ this.dispatch("UPDATE_CELL", { ...target, content });
43829
+ return;
43615
43830
  }
43616
- if (origin.cell) {
43617
- if (clipboardOption?.pasteOption === "onlyFormat") {
43618
- this.dispatch("UPDATE_CELL", {
43619
- ...target,
43620
- style: origin.cell.style,
43621
- format: origin.evaluatedCell.format,
43622
- });
43623
- return;
43624
- }
43625
- if (clipboardOption?.pasteOption === "onlyValue") {
43626
- const locale = this.getters.getLocale();
43627
- const content = formatValue(origin.evaluatedCell.value, { locale });
43628
- this.dispatch("UPDATE_CELL", { ...target, content });
43629
- return;
43630
- }
43631
- let content = origin.cell.content;
43632
- if (origin.cell.isFormula && operation === "COPY") {
43633
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula);
43634
- }
43831
+ const targetBorders = this.getters.getCellBorder(target);
43832
+ const originBorders = origin.border;
43833
+ const border = {
43834
+ top: targetBorders?.top || originBorders?.top,
43835
+ bottom: targetBorders?.bottom || originBorders?.bottom,
43836
+ left: targetBorders?.left || originBorders?.left,
43837
+ right: targetBorders?.right || originBorders?.right,
43838
+ };
43839
+ this.dispatch("SET_BORDER", { sheetId, col, row, border });
43840
+ if (clipboardOption?.pasteOption === "onlyFormat") {
43841
+ this.dispatch("UPDATE_CELL", {
43842
+ ...target,
43843
+ style: origin.cell?.style ?? null,
43844
+ format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
43845
+ });
43846
+ return;
43847
+ }
43848
+ const content = origin.cell && origin.cell.isFormula && operation === "COPY"
43849
+ ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula)
43850
+ : origin.cell?.content;
43851
+ if (content !== "" || origin.cell?.format || origin.cell?.style) {
43635
43852
  this.dispatch("UPDATE_CELL", {
43636
43853
  ...target,
43637
43854
  content,
43638
- style: origin.cell.style || null,
43639
- format: origin.cell.format,
43855
+ style: origin.cell?.style || null,
43856
+ format: origin.cell?.format,
43640
43857
  });
43641
43858
  }
43642
43859
  else if (targetCell) {
43643
- if (clipboardOption?.pasteOption === "onlyValue") {
43644
- this.dispatch("UPDATE_CELL", { ...target, content: "" });
43645
- }
43646
- else if (clipboardOption?.pasteOption === "onlyFormat") {
43647
- this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" });
43648
- }
43649
- else {
43650
- this.dispatch("CLEAR_CELL", target);
43651
- }
43860
+ this.dispatch("CLEAR_CELL", target);
43652
43861
  }
43653
43862
  }
43654
43863
  /**
@@ -43710,22 +43919,28 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43710
43919
  return (this.cells
43711
43920
  .map((cells) => {
43712
43921
  return cells
43713
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43922
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43714
43923
  ? c.cell?.content || ""
43715
- : c.evaluatedCell?.formattedValue || "")
43924
+ : c?.evaluatedCell?.formattedValue || "")
43716
43925
  .join("\t");
43717
43926
  })
43718
43927
  .join("\n") || "\t");
43719
43928
  }
43720
43929
  getHTMLContent() {
43721
43930
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43931
+ if (!this.cells[0][0]) {
43932
+ return "";
43933
+ }
43722
43934
  return this.getters.getCellText(this.cells[0][0].position);
43723
43935
  }
43724
43936
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43725
43937
  for (const row of this.cells) {
43726
43938
  htmlTable += "<tr>";
43727
43939
  for (const cell of row) {
43728
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43940
+ if (!cell) {
43941
+ continue;
43942
+ }
43943
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43729
43944
  const cellText = this.getters.getCellText(cell.position);
43730
43945
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43731
43946
  }
@@ -44053,24 +44268,49 @@ var Direction;
44053
44268
  */
44054
44269
  class FindAndReplacePlugin extends UIPlugin {
44055
44270
  static layers = [3 /* LAYERS.Search */];
44056
- static getters = ["getSearchMatches", "getCurrentSelectedMatchIndex"];
44057
- searchMatches = [];
44271
+ static getters = [
44272
+ "getSearchMatches",
44273
+ "getCurrentSelectedMatchIndex",
44274
+ "getSearchOptions",
44275
+ "getAllSheetMatchesCount",
44276
+ "getActiveSheetMatchesCount",
44277
+ "getSpecificRangeMatchesCount",
44278
+ ];
44279
+ allSheetsMatches = [];
44280
+ activeSheetMatches = [];
44281
+ specificRangeMatches = [];
44282
+ // fixme: why do we make selectedMatchIndex on top of a selected
44283
+ // property in the matches?
44058
44284
  selectedMatchIndex = null;
44059
44285
  currentSearchRegex = null;
44060
44286
  searchOptions = {
44061
44287
  matchCase: false,
44062
44288
  exactMatch: false,
44063
44289
  searchFormulas: false,
44290
+ searchScope: "allSheets",
44291
+ specificRange: undefined,
44064
44292
  };
44065
44293
  toSearch = "";
44066
44294
  isSearchDirty = false;
44295
+ get searchMatches() {
44296
+ switch (this.searchOptions.searchScope) {
44297
+ case "allSheets":
44298
+ return this.allSheetsMatches;
44299
+ case "activeSheet":
44300
+ return this.activeSheetMatches;
44301
+ case "specificRange":
44302
+ return this.specificRangeMatches;
44303
+ }
44304
+ }
44067
44305
  // ---------------------------------------------------------------------------
44068
44306
  // Command Handling
44069
44307
  // ---------------------------------------------------------------------------
44070
44308
  handle(cmd) {
44071
44309
  switch (cmd.type) {
44072
44310
  case "UPDATE_SEARCH":
44073
- this.updateSearch(cmd.toSearch, cmd.searchOptions);
44311
+ const rangeData = cmd.searchOptions.specificRange;
44312
+ const specificRange = rangeData && this.getters.getRangeFromRangeData(rangeData);
44313
+ this.updateSearch(cmd.toSearch, { ...cmd.searchOptions, specificRange });
44074
44314
  break;
44075
44315
  case "CLEAR_SEARCH":
44076
44316
  this.clearSearch();
@@ -44100,7 +44340,9 @@ class FindAndReplacePlugin extends UIPlugin {
44100
44340
  this.isSearchDirty = true;
44101
44341
  break;
44102
44342
  case "ACTIVATE_SHEET":
44103
- this.refreshSearch();
44343
+ if (this.searchOptions.searchScope === "activeSheet") {
44344
+ this.isSearchDirty = true;
44345
+ }
44104
44346
  break;
44105
44347
  }
44106
44348
  }
@@ -44119,6 +44361,18 @@ class FindAndReplacePlugin extends UIPlugin {
44119
44361
  getCurrentSelectedMatchIndex() {
44120
44362
  return this.selectedMatchIndex;
44121
44363
  }
44364
+ getSearchOptions() {
44365
+ return { ...this.searchOptions, specificRange: this.searchOptions.specificRange?.rangeData };
44366
+ }
44367
+ getAllSheetMatchesCount() {
44368
+ return this.allSheetsMatches.length;
44369
+ }
44370
+ getActiveSheetMatchesCount() {
44371
+ return this.activeSheetMatches.length;
44372
+ }
44373
+ getSpecificRangeMatchesCount() {
44374
+ return this.specificRangeMatches.length;
44375
+ }
44122
44376
  // ---------------------------------------------------------------------------
44123
44377
  // Search
44124
44378
  // ---------------------------------------------------------------------------
@@ -44139,8 +44393,8 @@ class FindAndReplacePlugin extends UIPlugin {
44139
44393
  * refresh the matches according to the current search options
44140
44394
  */
44141
44395
  refreshSearch() {
44142
- const matches = this.findMatches();
44143
- this.searchMatches = matches;
44396
+ this.selectedMatchIndex = null;
44397
+ this.findMatches();
44144
44398
  this.selectNextCell(Direction.current);
44145
44399
  }
44146
44400
  /**
@@ -44155,45 +44409,65 @@ class FindAndReplacePlugin extends UIPlugin {
44155
44409
  }
44156
44410
  this.currentSearchRegex = RegExp(searchValue, flags);
44157
44411
  }
44412
+ getSheetsInSearchOrder() {
44413
+ switch (this.searchOptions.searchScope) {
44414
+ case "allSheets":
44415
+ const sheetIds = this.getters.getSheetIds();
44416
+ const activeSheetIndex = sheetIds.findIndex((id) => id === this.getters.getActiveSheetId());
44417
+ return [
44418
+ sheetIds[activeSheetIndex],
44419
+ ...sheetIds.slice(activeSheetIndex + 1),
44420
+ ...sheetIds.slice(0, activeSheetIndex),
44421
+ ];
44422
+ case "activeSheet":
44423
+ return [this.getters.getActiveSheetId()];
44424
+ case "specificRange":
44425
+ const specificRange = this.searchOptions.specificRange;
44426
+ if (!specificRange) {
44427
+ return [];
44428
+ }
44429
+ return specificRange ? [specificRange.sheetId] : [];
44430
+ }
44431
+ }
44158
44432
  /**
44159
44433
  * Find matches using the current regex
44160
44434
  */
44161
44435
  findMatches() {
44162
- const sheetId = this.getters.getActiveSheetId();
44163
- const cells = this.getters.getCells(sheetId);
44164
44436
  const matches = [];
44165
- if (this.toSearch && this.currentSearchRegex) {
44166
- for (const cell of Object.values(cells)) {
44167
- const { col, row } = this.getters.getCellPosition(cell.id);
44168
- const cellPosition = { sheetId, col, row };
44437
+ if (this.toSearch) {
44438
+ for (const sheetId of this.getters.getSheetIds()) {
44439
+ matches.push(...this.findMatchesInSheet(sheetId));
44440
+ }
44441
+ }
44442
+ // set results
44443
+ this.allSheetsMatches = matches;
44444
+ this.activeSheetMatches = matches.filter((match) => match.sheetId === this.getters.getActiveSheetId());
44445
+ if (this.searchOptions.specificRange) {
44446
+ const { sheetId, zone } = this.searchOptions.specificRange;
44447
+ this.specificRangeMatches = matches.filter((match) => match.sheetId === sheetId && isInside(match.col, match.row, zone));
44448
+ }
44449
+ else {
44450
+ this.specificRangeMatches = [];
44451
+ }
44452
+ }
44453
+ findMatchesInSheet(sheetId) {
44454
+ const matches = [];
44455
+ const { left, right, top, bottom } = this.getters.getSheetZone(sheetId);
44456
+ for (let row = top; row <= bottom; row++) {
44457
+ for (let col = left; col <= right; col++) {
44169
44458
  const isColHidden = this.getters.isColHidden(sheetId, col);
44170
44459
  const isRowHidden = this.getters.isRowHidden(sheetId, row);
44171
44460
  if (isColHidden || isRowHidden) {
44172
44461
  continue;
44173
44462
  }
44174
- if (this.currentSearchRegex.test(this.getSearchableString(cellPosition))) {
44175
- const match = { col, row, selected: false };
44463
+ const cellPosition = { sheetId, col, row };
44464
+ if (this.currentSearchRegex?.test(this.getSearchableString(cellPosition))) {
44465
+ const match = { sheetId, col, row };
44176
44466
  matches.push(match);
44177
44467
  }
44178
- for (const spreadPosition of this.getters.getSpreadPositionsOf(cellPosition)) {
44179
- if (this.currentSearchRegex.test(this.getSearchableString(spreadPosition))) {
44180
- const match = {
44181
- col: spreadPosition.col,
44182
- row: spreadPosition.row,
44183
- selected: false,
44184
- };
44185
- matches.push(match);
44186
- }
44187
- }
44188
44468
  }
44189
44469
  }
44190
- return matches.sort(this.sortByRowThenColumn);
44191
- }
44192
- sortByRowThenColumn(a, b) {
44193
- if (a.row === b.row) {
44194
- return a.col - b.col;
44195
- }
44196
- return a.row > b.row ? 1 : -1;
44470
+ return matches;
44197
44471
  }
44198
44472
  /**
44199
44473
  * Changes the selected search cell. Given a direction it will
@@ -44211,30 +44485,47 @@ class FindAndReplacePlugin extends UIPlugin {
44211
44485
  }
44212
44486
  let nextIndex;
44213
44487
  if (this.selectedMatchIndex === null) {
44214
- nextIndex = 0;
44488
+ let nextMatchIndex = -1;
44489
+ // if search is not available in current sheet will select in next sheet
44490
+ for (const sheetId of this.getSheetsInSearchOrder()) {
44491
+ nextMatchIndex = matches.findIndex((match) => match.sheetId === sheetId);
44492
+ if (nextMatchIndex !== -1) {
44493
+ break;
44494
+ }
44495
+ }
44496
+ nextIndex = nextMatchIndex;
44215
44497
  }
44216
44498
  else {
44217
44499
  nextIndex = this.selectedMatchIndex + indexChange;
44218
44500
  }
44219
- //modulo of negative value to be able to cycle in both directions with previous and next
44220
- nextIndex = ((nextIndex % matches.length) + matches.length) % matches.length;
44501
+ // loop index value inside the array (index -1 => last index)
44502
+ nextIndex = (nextIndex + matches.length) % matches.length;
44221
44503
  this.selectedMatchIndex = nextIndex;
44504
+ const selectedMatch = matches[nextIndex];
44505
+ // Switch to the sheet where the match is located
44506
+ if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
44507
+ this.dispatch("ACTIVATE_SHEET", {
44508
+ sheetIdFrom: this.getters.getActiveSheetId(),
44509
+ sheetIdTo: selectedMatch.sheetId,
44510
+ });
44511
+ }
44222
44512
  // we want grid selection to capture the selection stream
44223
44513
  this.selection.getBackToDefault();
44224
- this.selection.selectCell(matches[nextIndex].col, matches[nextIndex].row);
44225
- for (let index = 0; index < this.searchMatches.length; index++) {
44226
- this.searchMatches[index].selected = index === this.selectedMatchIndex;
44227
- }
44514
+ this.selection.selectCell(selectedMatch.col, selectedMatch.row);
44228
44515
  }
44229
44516
  clearSearch() {
44230
44517
  this.toSearch = "";
44231
- this.searchMatches = [];
44232
44518
  this.selectedMatchIndex = null;
44233
44519
  this.currentSearchRegex = null;
44520
+ this.allSheetsMatches = [];
44521
+ this.activeSheetMatches = [];
44522
+ this.specificRangeMatches = [];
44234
44523
  this.searchOptions = {
44235
44524
  matchCase: false,
44236
44525
  exactMatch: false,
44237
44526
  searchFormulas: false,
44527
+ searchScope: "allSheets",
44528
+ specificRange: undefined,
44238
44529
  };
44239
44530
  }
44240
44531
  // ---------------------------------------------------------------------------
@@ -44244,9 +44535,7 @@ class FindAndReplacePlugin extends UIPlugin {
44244
44535
  if (!this.currentSearchRegex) {
44245
44536
  return;
44246
44537
  }
44247
- const sheetId = this.getters.getActiveSheetId();
44248
- const position = { sheetId, ...selectedMatch };
44249
- const cell = this.getters.getCell(position);
44538
+ const cell = this.getters.getCell(selectedMatch);
44250
44539
  if (!cell?.content) {
44251
44540
  return;
44252
44541
  }
@@ -44254,10 +44543,10 @@ class FindAndReplacePlugin extends UIPlugin {
44254
44543
  return;
44255
44544
  }
44256
44545
  const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
44257
- const toReplace = this.getSearchableString(position);
44546
+ const toReplace = this.getSearchableString(selectedMatch);
44258
44547
  const content = toReplace.replace(replaceRegex, replaceWith);
44259
44548
  const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
44260
- this.dispatch("UPDATE_CELL", { ...position, content: canonicalContent });
44549
+ this.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
44261
44550
  }
44262
44551
  /**
44263
44552
  * Replace the value of the currently selected match
@@ -44288,7 +44577,10 @@ class FindAndReplacePlugin extends UIPlugin {
44288
44577
  drawGrid(renderingContext) {
44289
44578
  const { ctx } = renderingContext;
44290
44579
  const sheetId = this.getters.getActiveSheetId();
44291
- for (const match of this.searchMatches) {
44580
+ for (const [index, match] of this.searchMatches.entries()) {
44581
+ if (match.sheetId !== sheetId) {
44582
+ continue; // Skip drawing matches from other sheets
44583
+ }
44292
44584
  const merge = this.getters.getMerge({ sheetId, col: match.col, row: match.row });
44293
44585
  const left = merge ? merge.left : match.col;
44294
44586
  const right = merge ? merge.right : match.col;
@@ -44298,12 +44590,21 @@ class FindAndReplacePlugin extends UIPlugin {
44298
44590
  if (width > 0 && height > 0) {
44299
44591
  ctx.fillStyle = BACKGROUND_COLOR;
44300
44592
  ctx.fillRect(x, y, width, height);
44301
- if (match.selected) {
44593
+ if (index === this.selectedMatchIndex) {
44302
44594
  ctx.strokeStyle = BORDER_COLOR;
44303
44595
  ctx.strokeRect(x, y, width, height);
44304
44596
  }
44305
44597
  }
44306
44598
  }
44599
+ if (this.searchOptions.searchScope === "specificRange") {
44600
+ const range = this.searchOptions.specificRange;
44601
+ if (!range || range.sheetId !== sheetId) {
44602
+ return;
44603
+ }
44604
+ const { x, y, width, height } = this.getters.getVisibleRect(range.zone);
44605
+ ctx.strokeStyle = BORDER_COLOR;
44606
+ ctx.strokeRect(x, y, width, height);
44607
+ }
44307
44608
  }
44308
44609
  }
44309
44610
 
@@ -46769,11 +47070,14 @@ class ClipboardPlugin extends UIPlugin {
46769
47070
  }
46770
47071
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46771
47072
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
47073
+ if (this.state.operation === "CUT") {
47074
+ this.state = undefined;
47075
+ }
46772
47076
  this.lastPasteState = this.state;
46773
47077
  if (this.paintFormatStatus === "oneOff") {
46774
47078
  this.paintFormatStatus = "inactive";
46775
- this.status = "invisible";
46776
47079
  }
47080
+ this.status = "invisible";
46777
47081
  break;
46778
47082
  case "COPY_PASTE_CELLS_ABOVE":
46779
47083
  {
@@ -47000,92 +47304,6 @@ class ClipboardPlugin extends UIPlugin {
47000
47304
  }
47001
47305
  }
47002
47306
 
47003
- /**
47004
- * Change the reference types inside the given token, if the token represent a range or a cell
47005
- *
47006
- * Eg. :
47007
- * A1 => $A$1 => A$1 => $A1 => A1
47008
- * A1:$B$1 => $A$1:B$1 => A$1:$B1 => $A1:B1 => A1:$B$1
47009
- */
47010
- function loopThroughReferenceType(token) {
47011
- if (token.type !== "REFERENCE")
47012
- return token;
47013
- const { xc, sheetName } = splitReference(token.value);
47014
- const [left, right] = xc.split(":");
47015
- const sheetRef = sheetName ? `${getCanonicalSheetName(sheetName)}!` : "";
47016
- const updatedLeft = getTokenNextReferenceType(left);
47017
- const updatedRight = right ? `:${getTokenNextReferenceType(right)}` : "";
47018
- return { ...token, value: sheetRef + updatedLeft + updatedRight };
47019
- }
47020
- /**
47021
- * Get a new token with a changed type of reference from the given cell token symbol.
47022
- * Undefined behavior if given a token other than a cell or if the Xc contains a sheet reference
47023
- *
47024
- * A1 => $A$1 => A$1 => $A1 => A1
47025
- */
47026
- function getTokenNextReferenceType(xc) {
47027
- switch (getReferenceType(xc)) {
47028
- case "none":
47029
- xc = setXcToReferenceType(xc, "colrow");
47030
- break;
47031
- case "colrow":
47032
- xc = setXcToReferenceType(xc, "row");
47033
- break;
47034
- case "row":
47035
- xc = setXcToReferenceType(xc, "col");
47036
- break;
47037
- case "col":
47038
- xc = setXcToReferenceType(xc, "none");
47039
- break;
47040
- }
47041
- return xc;
47042
- }
47043
- /**
47044
- * Returns the given XC with the given reference type.
47045
- */
47046
- function setXcToReferenceType(xc, referenceType) {
47047
- xc = xc.replace(/\$/g, "");
47048
- let indexOfNumber;
47049
- switch (referenceType) {
47050
- case "col":
47051
- return "$" + xc;
47052
- case "row":
47053
- indexOfNumber = xc.search(/[0-9]/);
47054
- return xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
47055
- case "colrow":
47056
- indexOfNumber = xc.search(/[0-9]/);
47057
- xc = xc.slice(0, indexOfNumber) + "$" + xc.slice(indexOfNumber);
47058
- return "$" + xc;
47059
- case "none":
47060
- return xc;
47061
- }
47062
- }
47063
- /**
47064
- * Return the type of reference used in the given XC of a cell.
47065
- * Undefined behavior if the XC have a sheet reference
47066
- */
47067
- function getReferenceType(xcCell) {
47068
- if (isColAndRowFixed(xcCell)) {
47069
- return "colrow";
47070
- }
47071
- else if (isColFixed(xcCell)) {
47072
- return "col";
47073
- }
47074
- else if (isRowFixed(xcCell)) {
47075
- return "row";
47076
- }
47077
- return "none";
47078
- }
47079
- function isColFixed(xc) {
47080
- return xc.startsWith("$");
47081
- }
47082
- function isRowFixed(xc) {
47083
- return xc.includes("$", 1);
47084
- }
47085
- function isColAndRowFixed(xc) {
47086
- return xc.startsWith("$") && xc.length > 1 && xc.slice(1).includes("$");
47087
- }
47088
-
47089
47307
  const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
47090
47308
  class EditionPlugin extends UIPlugin {
47091
47309
  static getters = [
@@ -48003,32 +48221,32 @@ const selectionStatisticFunctions = [
48003
48221
  {
48004
48222
  name: _t("Sum"),
48005
48223
  types: [CellValueType.number],
48006
- compute: (values, locale) => SUM.compute.bind({ locale })([values]),
48224
+ compute: (values, locale) => sum([[values]], locale),
48007
48225
  },
48008
48226
  {
48009
48227
  name: _t("Avg"),
48010
48228
  types: [CellValueType.number],
48011
- compute: (values, locale) => AVERAGE.compute.bind({ locale })([values]),
48229
+ compute: (values, locale) => average([[values]], locale),
48012
48230
  },
48013
48231
  {
48014
48232
  name: _t("Min"),
48015
48233
  types: [CellValueType.number],
48016
- compute: (values, locale) => MIN.compute.bind({ locale })([values]),
48234
+ compute: (values, locale) => min([[values]], locale),
48017
48235
  },
48018
48236
  {
48019
48237
  name: _t("Max"),
48020
48238
  types: [CellValueType.number],
48021
- compute: (values, locale) => MAX.compute.bind({ locale })([values]),
48239
+ compute: (values, locale) => max([[values]], locale),
48022
48240
  },
48023
48241
  {
48024
48242
  name: _t("Count"),
48025
48243
  types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
48026
- compute: (values, locale) => COUNTA.compute.bind({ locale })([values]),
48244
+ compute: (values) => countAny([[values]]),
48027
48245
  },
48028
48246
  {
48029
48247
  name: _t("Count Numbers"),
48030
48248
  types: [CellValueType.number, CellValueType.text, CellValueType.boolean, CellValueType.error],
48031
- compute: (values, locale) => COUNT.compute.bind({ locale })([values]),
48249
+ compute: (values, locale) => countNumbers([[values]], locale),
48032
48250
  },
48033
48251
  ];
48034
48252
  /**
@@ -51943,6 +52161,7 @@ class Spreadsheet extends owl.Component {
51943
52161
  };
51944
52162
  sidePanel;
51945
52163
  composer;
52164
+ spreadsheetRef = owl.useRef("spreadsheet");
51946
52165
  _focusGrid;
51947
52166
  keyDownMapping;
51948
52167
  isViewportTooSmall = false;
@@ -51977,6 +52196,18 @@ class Spreadsheet extends owl.Component {
51977
52196
  clipboard: this.env.clipboard || instantiateClipboard(),
51978
52197
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
51979
52198
  });
52199
+ owl.useEffect(() => {
52200
+ /**
52201
+ * Only refocus the grid if the active element is not a child of the spreadsheet
52202
+ * (i.e. activeElement is outside of the spreadsheetRef component)
52203
+ * and spreadsheet is a child of that element. Anything else means that the focus
52204
+ * is on an element that needs to keep it.
52205
+ */
52206
+ if (!this.spreadsheetRef.el.contains(document.activeElement) &&
52207
+ document.activeElement?.contains(this.spreadsheetRef.el)) {
52208
+ this.focusGrid();
52209
+ }
52210
+ }, () => [this.env.model.getters.getActiveSheetId()]);
51980
52211
  owl.useExternalListener(window, "resize", () => this.render(true));
51981
52212
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
51982
52213
  this.bindModelEvents();
@@ -52058,7 +52289,7 @@ class Spreadsheet extends owl.Component {
52058
52289
  }
52059
52290
  focusGrid() {
52060
52291
  if (!this._focusGrid) {
52061
- throw new Error("_focusGrid should be exposed by the grid component");
52292
+ return;
52062
52293
  }
52063
52294
  this._focusGrid();
52064
52295
  }
@@ -55451,19 +55682,22 @@ class Model extends EventBus {
55451
55682
  * Check if the given command is allowed by all the plugins and the history.
55452
55683
  */
55453
55684
  checkDispatchAllowed(command) {
55454
- if (isCoreCommand(command)) {
55455
- return this.checkDispatchAllowedCoreCommand(command);
55685
+ const results = isCoreCommand(command)
55686
+ ? this.checkDispatchAllowedCoreCommand(command)
55687
+ : this.checkDispatchAllowedLocalCommand(command);
55688
+ if (results.some((r) => r !== "Success" /* CommandResult.Success */)) {
55689
+ return new DispatchResult(results.flat());
55456
55690
  }
55457
- return this.checkDispatchAllowedLocalCommand(command);
55691
+ return DispatchResult.Success;
55458
55692
  }
55459
55693
  checkDispatchAllowedCoreCommand(command) {
55460
55694
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55461
55695
  results.push(this.range.allowDispatch(command));
55462
- return new DispatchResult(results.flat());
55696
+ return results;
55463
55697
  }
55464
55698
  checkDispatchAllowedLocalCommand(command) {
55465
55699
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55466
- return new DispatchResult(results.flat());
55700
+ return results;
55467
55701
  }
55468
55702
  finalize() {
55469
55703
  this.status = 3 /* Status.Finalizing */;
@@ -55758,6 +55992,7 @@ const components = {
55758
55992
  FigureComponent,
55759
55993
  Menu,
55760
55994
  SelectionInput,
55995
+ ValidationMessages,
55761
55996
  };
55762
55997
  const hooks = {
55763
55998
  useDragAndDropListItems,
@@ -55810,6 +56045,6 @@ exports.setTranslationMethod = setTranslationMethod;
55810
56045
  exports.tokenize = tokenize;
55811
56046
 
55812
56047
 
55813
- __info__.version = "17.1.0-alpha.3";
55814
- __info__.date = "2023-11-16T12:04:09.636Z";
55815
- __info__.hash = "2ef5b1a";
56048
+ __info__.version = "17.1.0-alpha.4";
56049
+ __info__.date = "2023-11-24T13:12:24.882Z";
56050
+ __info__.hash = "255821b";