@odoo/o-spreadsheet 17.0.2 → 17.0.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.0.2
6
- * @date 2023-11-03T12:22:44.945Z
7
- * @hash 3184728
5
+ * @version 17.0.4
6
+ * @date 2023-11-24T13:13:42.575Z
7
+ * @hash 7346d0a
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';
@@ -691,8 +691,7 @@ function removeFalsyAttributes(obj) {
691
691
  *
692
692
  * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
693
693
  */
694
- const whiteSpaceCharacters = [
695
- " ",
694
+ const whiteSpaceSpecialCharacters = [
696
695
  "\t",
697
696
  "\f",
698
697
  "\v",
@@ -707,8 +706,7 @@ const whiteSpaceCharacters = [
707
706
  String.fromCharCode(parseInt("3000", 16)),
708
707
  String.fromCharCode(parseInt("feff", 16)),
709
708
  ];
710
- const whiteSpaceRegexp = new RegExp(whiteSpaceCharacters.join("|"), "g");
711
- const newLineRegex = /\r\n|\r|\n/g;
709
+ const whiteSpaceRegexp = new RegExp(whiteSpaceSpecialCharacters.join("|") + "|(\r\n|\r|\n)", "g");
712
710
  /**
713
711
  * Replace all the special spaces in a string (non-breaking, tabs, ...) by normal spaces, and all the
714
712
  * different newlines types by \n.
@@ -716,9 +714,9 @@ const newLineRegex = /\r\n|\r|\n/g;
716
714
  function replaceSpecialSpaces(text) {
717
715
  if (!text)
718
716
  return "";
719
- text = text.replace(whiteSpaceRegexp, " ");
720
- text = text.replace(newLineRegex, NEWLINE);
721
- return text;
717
+ if (!whiteSpaceRegexp.test(text))
718
+ return text;
719
+ return text.replace(whiteSpaceRegexp, (match, newLine) => (newLine ? NEWLINE : " "));
722
720
  }
723
721
  /**
724
722
  * Determine if the numbers are consecutive.
@@ -2060,6 +2058,7 @@ var CommandResult;
2060
2058
  CommandResult["InvalidNumberOfCriterionValues"] = "InvalidNumberOfCriterionValues";
2061
2059
  CommandResult["BlockingValidationRule"] = "BlockingValidationRule";
2062
2060
  CommandResult["InvalidCopyPasteSelection"] = "InvalidCopyPasteSelection";
2061
+ CommandResult["NoChanges"] = "NoChanges";
2063
2062
  })(CommandResult || (CommandResult = {}));
2064
2063
 
2065
2064
  const DEFAULT_LOCALES = [
@@ -2189,12 +2188,12 @@ function toString(value) {
2189
2188
  }
2190
2189
  }
2191
2190
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
2192
- function normalizeString(str) {
2191
+ const normalizeString = memoize(function normalizeString(str) {
2193
2192
  return str
2194
2193
  .toLowerCase()
2195
2194
  .normalize("NFD")
2196
2195
  .replace(/[\u0300-\u036f]/g, "");
2197
- }
2196
+ });
2198
2197
  const expectBooleanValueError = (value) => _t("The function [[FUNCTION_NAME]] expects a boolean value, but '%s' is a text, and cannot be coerced to a number.", value);
2199
2198
  function toBoolean(value) {
2200
2199
  switch (typeof value) {
@@ -4540,14 +4539,14 @@ function drawDecoratedText(context, text, position, underline = false, strikethr
4540
4539
  switch (context.textBaseline) {
4541
4540
  case "top":
4542
4541
  underlineY += boxHeight - 2 * strokeWidth;
4543
- strikeY += boxHeight - textHeight;
4542
+ strikeY += boxHeight / 2 - strokeWidth;
4544
4543
  break;
4545
4544
  case "middle":
4546
4545
  underlineY += boxHeight / 2 - strokeWidth;
4547
4546
  break;
4548
4547
  case "alphabetic":
4549
4548
  underlineY += 2 * strokeWidth;
4550
- strikeY -= textHeight / 2 - strokeWidth / 2;
4549
+ strikeY -= 3 * strokeWidth;
4551
4550
  break;
4552
4551
  case "bottom":
4553
4552
  underlineY = y;
@@ -5966,7 +5965,7 @@ autofillModifiersRegistry
5966
5965
  return { cellData: {} };
5967
5966
  }
5968
5967
  const sheetId = data.sheetId;
5969
- const content = getters.getTranslatedCellFormula(sheetId, x, y, cell.compiledFormula, cell.dependencies);
5968
+ const content = getters.getTranslatedCellFormula(sheetId, x, y, cell.compiledFormula);
5970
5969
  return {
5971
5970
  cellData: {
5972
5971
  border: data.border,
@@ -7329,7 +7328,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7329
7328
  return result;
7330
7329
  }
7331
7330
  function tokenizeDebugger(chars) {
7332
- if (chars.current() === "?") {
7331
+ if (chars.current === "?") {
7333
7332
  chars.shift();
7334
7333
  return { type: "DEBUGGER", value: "?" };
7335
7334
  }
@@ -7340,7 +7339,7 @@ const misc$1 = {
7340
7339
  ")": "RIGHT_PAREN",
7341
7340
  };
7342
7341
  function tokenizeMisc(chars) {
7343
- if (chars.current() in misc$1) {
7342
+ if (chars.current in misc$1) {
7344
7343
  const value = chars.shift();
7345
7344
  const type = misc$1[value];
7346
7345
  return { type, value };
@@ -7348,7 +7347,7 @@ function tokenizeMisc(chars) {
7348
7347
  return null;
7349
7348
  }
7350
7349
  function tokenizeArgsSeparator(chars, locale) {
7351
- if (chars.current() === locale.formulaArgSeparator) {
7350
+ if (chars.current === locale.formulaArgSeparator) {
7352
7351
  const value = chars.shift();
7353
7352
  const type = "ARG_SEPARATOR";
7354
7353
  return { type, value };
@@ -7373,14 +7372,13 @@ function tokenizeNumber(chars, locale) {
7373
7372
  return null;
7374
7373
  }
7375
7374
  function tokenizeString(chars) {
7376
- if (chars.current() === '"') {
7375
+ if (chars.current === '"') {
7377
7376
  const startChar = chars.shift();
7378
7377
  let letters = startChar;
7379
- while (chars.current() &&
7380
- (chars.current() !== startChar || letters[letters.length - 1] === "\\")) {
7378
+ while (chars.current && (chars.current !== startChar || letters[letters.length - 1] === "\\")) {
7381
7379
  letters += chars.shift();
7382
7380
  }
7383
- if (chars.current() === '"') {
7381
+ if (chars.current === '"') {
7384
7382
  letters += chars.shift();
7385
7383
  }
7386
7384
  return {
@@ -7407,14 +7405,14 @@ function tokenizeSymbol(chars) {
7407
7405
  let result = "";
7408
7406
  // there are two main cases to manage: either something which starts with
7409
7407
  // a ', like 'Sheet 2'A2, or a word-like element.
7410
- if (chars.current() === "'") {
7408
+ if (chars.current === "'") {
7411
7409
  let lastChar = chars.shift();
7412
7410
  result += lastChar;
7413
- while (chars.current()) {
7411
+ while (chars.current) {
7414
7412
  lastChar = chars.shift();
7415
7413
  result += lastChar;
7416
7414
  if (lastChar === "'") {
7417
- if (chars.current() && chars.current() === "'") {
7415
+ if (chars.current && chars.current === "'") {
7418
7416
  lastChar = chars.shift();
7419
7417
  result += lastChar;
7420
7418
  }
@@ -7430,7 +7428,7 @@ function tokenizeSymbol(chars) {
7430
7428
  };
7431
7429
  }
7432
7430
  }
7433
- while (chars.current() && separatorRegexp.test(chars.current())) {
7431
+ while (chars.current && separatorRegexp.test(chars.current)) {
7434
7432
  result += chars.shift();
7435
7433
  }
7436
7434
  if (result.length) {
@@ -7445,14 +7443,14 @@ function tokenizeSymbol(chars) {
7445
7443
  }
7446
7444
  function tokenizeSpace(chars) {
7447
7445
  let length = 0;
7448
- while (chars.current() === NEWLINE) {
7446
+ while (chars.current === NEWLINE) {
7449
7447
  length++;
7450
7448
  chars.shift();
7451
7449
  }
7452
7450
  if (length) {
7453
7451
  return { type: "SPACE", value: NEWLINE.repeat(length) };
7454
7452
  }
7455
- while (chars.current() === " ") {
7453
+ while (chars.current === " ") {
7456
7454
  length++;
7457
7455
  chars.shift();
7458
7456
  }
@@ -7471,17 +7469,20 @@ function tokenizeInvalidRange(chars) {
7471
7469
  class TokenizingChars {
7472
7470
  text;
7473
7471
  currentIndex = 0;
7472
+ current;
7474
7473
  constructor(text) {
7475
7474
  this.text = text;
7476
- }
7477
- current() {
7478
- return this.text[this.currentIndex];
7475
+ this.current = text[0];
7479
7476
  }
7480
7477
  shift() {
7481
- return this.text[this.currentIndex++];
7478
+ const current = this.current;
7479
+ const next = this.text[++this.currentIndex];
7480
+ this.current = next;
7481
+ return current;
7482
7482
  }
7483
7483
  advanceBy(length) {
7484
7484
  this.currentIndex += length;
7485
+ this.current = this.text[this.currentIndex];
7485
7486
  }
7486
7487
  isOver() {
7487
7488
  return this.currentIndex >= this.text.length;
@@ -10693,6 +10694,17 @@ const SELECTION_CONTAINS_FILTER = (env) => {
10693
10694
  const IS_ONLY_ONE_RANGE = (env) => {
10694
10695
  return env.model.getters.getSelectedZones().length === 1;
10695
10696
  };
10697
+ const CAN_INSERT_HEADER = (env, dimension) => {
10698
+ if (!IS_ONLY_ONE_RANGE(env)) {
10699
+ return false;
10700
+ }
10701
+ const activeHeaders = dimension === "COL" ? env.model.getters.getActiveCols() : env.model.getters.getActiveRows();
10702
+ const ortogonalActiveHeaders = dimension === "COL" ? env.model.getters.getActiveRows() : env.model.getters.getActiveCols();
10703
+ const sheetId = env.model.getters.getActiveSheetId();
10704
+ const zone = env.model.getters.getSelectedZone();
10705
+ const allSheetSelected = isEqual(zone, env.model.getters.getSheetZone(sheetId));
10706
+ return isConsecutive(activeHeaders) && (ortogonalActiveHeaders.size === 0 || allSheetSelected);
10707
+ };
10696
10708
 
10697
10709
  const undo = {
10698
10710
  name: _t("Undo"),
@@ -19370,9 +19382,7 @@ const insertRow = {
19370
19382
  const number = getRowsNumber(env);
19371
19383
  return number === 1 ? _t("Insert row") : _t("Insert %s rows", number.toString());
19372
19384
  },
19373
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
19374
- IS_ONLY_ONE_RANGE(env) &&
19375
- env.model.getters.getActiveCols().size === 0,
19385
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
19376
19386
  icon: "o-spreadsheet-Icon.INSERT_ROW",
19377
19387
  };
19378
19388
  const rowInsertRowBefore = {
@@ -19381,9 +19391,7 @@ const rowInsertRowBefore = {
19381
19391
  return number === 1 ? _t("Insert row above") : _t("Insert %s rows above", number.toString());
19382
19392
  },
19383
19393
  execute: INSERT_ROWS_BEFORE_ACTION,
19384
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
19385
- IS_ONLY_ONE_RANGE(env) &&
19386
- env.model.getters.getActiveCols().size === 0,
19394
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
19387
19395
  icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
19388
19396
  };
19389
19397
  const topBarInsertRowsBefore = {
@@ -19414,9 +19422,7 @@ const rowInsertRowsAfter = {
19414
19422
  const number = getRowsNumber(env);
19415
19423
  return number === 1 ? _t("Insert row below") : _t("Insert %s rows below", number.toString());
19416
19424
  },
19417
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
19418
- IS_ONLY_ONE_RANGE(env) &&
19419
- env.model.getters.getActiveCols().size === 0,
19425
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
19420
19426
  icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
19421
19427
  };
19422
19428
  const topBarInsertRowsAfter = {
@@ -19434,9 +19440,7 @@ const insertCol = {
19434
19440
  const number = getColumnsNumber(env);
19435
19441
  return number === 1 ? _t("Insert column") : _t("Insert %s columns", number.toString());
19436
19442
  },
19437
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
19438
- IS_ONLY_ONE_RANGE(env) &&
19439
- env.model.getters.getActiveRows().size === 0,
19443
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
19440
19444
  icon: "o-spreadsheet-Icon.INSERT_COL",
19441
19445
  };
19442
19446
  const colInsertColsBefore = {
@@ -19447,9 +19451,7 @@ const colInsertColsBefore = {
19447
19451
  : _t("Insert %s columns left", number.toString());
19448
19452
  },
19449
19453
  execute: INSERT_COLUMNS_BEFORE_ACTION,
19450
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
19451
- IS_ONLY_ONE_RANGE(env) &&
19452
- env.model.getters.getActiveRows().size === 0,
19454
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
19453
19455
  icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
19454
19456
  };
19455
19457
  const topBarInsertColsBefore = {
@@ -19482,9 +19484,7 @@ const colInsertColsAfter = {
19482
19484
  : _t("Insert %s columns right", number.toString());
19483
19485
  },
19484
19486
  execute: INSERT_COLUMNS_AFTER_ACTION,
19485
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
19486
- IS_ONLY_ONE_RANGE(env) &&
19487
- env.model.getters.getActiveRows().size === 0,
19487
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
19488
19488
  icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
19489
19489
  };
19490
19490
  const topBarInsertColsAfter = {
@@ -23825,7 +23825,7 @@ css /* scss */ `
23825
23825
  }
23826
23826
  .o-input-count {
23827
23827
  width: fit-content;
23828
- padding: 4 0 4 4;
23828
+ padding: 4px 0 4px 4px;
23829
23829
  }
23830
23830
  }
23831
23831
  }
@@ -29758,6 +29758,7 @@ class Grid extends Component {
29758
29758
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29759
29759
  const target = this.env.model.getters.getSelectedZones();
29760
29760
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29761
+ const isCutOperation = this.env.model.getters.isCutOperation();
29761
29762
  if (clipboardString === content) {
29762
29763
  // the paste actually comes from o-spreadsheet itself
29763
29764
  interactivePaste(this.env, target);
@@ -29765,7 +29766,7 @@ class Grid extends Component {
29765
29766
  else {
29766
29767
  interactivePasteFromOS(this.env, target, content);
29767
29768
  }
29768
- if (this.env.model.getters.isCutOperation()) {
29769
+ if (isCutOperation) {
29769
29770
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29770
29771
  }
29771
29772
  }
@@ -34059,6 +34060,14 @@ class BordersPlugin extends CorePlugin {
34059
34060
  // ---------------------------------------------------------------------------
34060
34061
  // Command Handling
34061
34062
  // ---------------------------------------------------------------------------
34063
+ allowDispatch(cmd) {
34064
+ switch (cmd.type) {
34065
+ case "SET_BORDER":
34066
+ return this.checkBordersUnchanged(cmd);
34067
+ default:
34068
+ return "Success" /* CommandResult.Success */;
34069
+ }
34070
+ }
34062
34071
  handle(cmd) {
34063
34072
  switch (cmd.type) {
34064
34073
  case "ADD_MERGE":
@@ -34478,6 +34487,14 @@ class BordersPlugin extends CorePlugin {
34478
34487
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34479
34488
  }
34480
34489
  }
34490
+ checkBordersUnchanged(cmd) {
34491
+ const currentBorder = this.getCellBorder(cmd);
34492
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
34493
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
34494
+ return "NoChanges" /* CommandResult.NoChanges */;
34495
+ }
34496
+ return "Success" /* CommandResult.Success */;
34497
+ }
34481
34498
  // ---------------------------------------------------------------------------
34482
34499
  // Import/Export
34483
34500
  // ---------------------------------------------------------------------------
@@ -34946,6 +34963,9 @@ const functionCache = {};
34946
34963
  // -----------------------------------------------------------------------------
34947
34964
  function compile(formula) {
34948
34965
  const tokens = rangeTokenize(formula);
34966
+ return compileTokens(tokens);
34967
+ }
34968
+ function compileTokens(tokens) {
34949
34969
  const { dependencies, constantValues } = formulaArguments(tokens);
34950
34970
  const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
34951
34971
  if (!functionCache[cacheKey]) {
@@ -35312,11 +35332,11 @@ class CellPlugin extends CorePlugin {
35312
35332
  for (const sheet of Object.keys(this.cells)) {
35313
35333
  for (const cell of Object.values(this.cells[sheet] || {})) {
35314
35334
  if (cell.isFormula) {
35315
- for (const range of cell.dependencies) {
35335
+ for (const range of cell.compiledFormula.dependencies) {
35316
35336
  if (!sheetId || range.sheetId === sheetId) {
35317
35337
  const change = applyChange(range);
35318
35338
  if (change.changeType !== "NONE") {
35319
- this.history.update("cells", sheet, cell.id, "dependencies", cell.dependencies.indexOf(range), change.range);
35339
+ this.history.update("cells", sheet, cell.id, "compiledFormula", "dependencies", cell.compiledFormula.dependencies.indexOf(range), change.range);
35320
35340
  }
35321
35341
  }
35322
35342
  }
@@ -35330,8 +35350,9 @@ class CellPlugin extends CorePlugin {
35330
35350
  allowDispatch(cmd) {
35331
35351
  switch (cmd.type) {
35332
35352
  case "UPDATE_CELL":
35353
+ return this.checkCellOutOfSheet(cmd);
35333
35354
  case "CLEAR_CELL":
35334
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35355
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35335
35356
  default:
35336
35357
  return "Success" /* CommandResult.Success */;
35337
35358
  }
@@ -35500,22 +35521,17 @@ class CellPlugin extends CorePlugin {
35500
35521
  */
35501
35522
  getCellById(cellId) {
35502
35523
  // this must be as fast as possible
35503
- for (const sheetId in this.cells) {
35504
- const sheet = this.cells[sheetId];
35505
- const cell = sheet[cellId];
35506
- if (cell) {
35507
- return cell;
35508
- }
35509
- }
35510
- return undefined;
35524
+ const position = this.getters.getCellPosition(cellId);
35525
+ const sheet = this.cells[position.sheetId];
35526
+ return sheet[cellId];
35511
35527
  }
35512
35528
  /*
35513
35529
  * Reconstructs the original formula string based on a normalized form and its dependencies
35514
35530
  */
35515
- getFormulaCellContent(sheetId, cell, dependencies) {
35516
- const ranges = dependencies || cell.dependencies;
35531
+ getFormulaCellContent(sheetId, compiledFormula, dependencies) {
35532
+ const ranges = dependencies || compiledFormula.dependencies;
35517
35533
  let rangeIndex = 0;
35518
- return concat(cell.compiledFormula.tokens.map((token) => {
35534
+ return concat(compiledFormula.tokens.map((token) => {
35519
35535
  if (token.type === "REFERENCE") {
35520
35536
  const range = ranges[rangeIndex++];
35521
35537
  return this.getters.getRangeString(range, sheetId);
@@ -35526,12 +35542,9 @@ class CellPlugin extends CorePlugin {
35526
35542
  /*
35527
35543
  * Constructs a formula string based on an initial formula and a translation vector
35528
35544
  */
35529
- getTranslatedCellFormula(sheetId, offsetX, offsetY, compiledFormula, dependencies) {
35530
- const adaptedDependencies = this.getters.createAdaptedRanges(dependencies, offsetX, offsetY, sheetId);
35531
- return this.getFormulaCellContent(sheetId, {
35532
- compiledFormula,
35533
- dependencies: adaptedDependencies,
35534
- });
35545
+ getTranslatedCellFormula(sheetId, offsetX, offsetY, compiledFormula) {
35546
+ const adaptedDependencies = this.getters.createAdaptedRanges(compiledFormula.dependencies, offsetX, offsetY, sheetId);
35547
+ return this.getFormulaCellContent(sheetId, compiledFormula, adaptedDependencies);
35535
35548
  }
35536
35549
  getCellStyle(position) {
35537
35550
  return this.getters.getCell(position)?.style || {};
@@ -35711,8 +35724,10 @@ class CellPlugin extends CorePlugin {
35711
35724
  style,
35712
35725
  format,
35713
35726
  isFormula: true,
35714
- compiledFormula,
35715
- dependencies: [],
35727
+ compiledFormula: {
35728
+ ...compiledFormula,
35729
+ dependencies: [],
35730
+ },
35716
35731
  };
35717
35732
  }
35718
35733
  /**
@@ -35721,7 +35736,7 @@ class CellPlugin extends CorePlugin {
35721
35736
  */
35722
35737
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
35723
35738
  const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
35724
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getFormulaCellContent.bind(this));
35739
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
35725
35740
  }
35726
35741
  createErrorFormula(id, content, format, style, error) {
35727
35742
  return {
@@ -35731,46 +35746,72 @@ class CellPlugin extends CorePlugin {
35731
35746
  format,
35732
35747
  isFormula: true,
35733
35748
  compiledFormula: {
35734
- dependencies: [],
35735
35749
  tokens: tokenize(content),
35750
+ dependencies: [],
35736
35751
  execute: function () {
35737
35752
  throw error;
35738
35753
  },
35739
35754
  },
35740
- dependencies: [],
35741
35755
  };
35742
35756
  }
35743
- checkCellOutOfSheet(sheetId, col, row) {
35757
+ checkCellOutOfSheet(cmd) {
35758
+ const { sheetId, col, row } = cmd;
35744
35759
  const sheet = this.getters.tryGetSheet(sheetId);
35745
35760
  if (!sheet)
35746
35761
  return "InvalidSheetId" /* CommandResult.InvalidSheetId */;
35747
35762
  const sheetZone = this.getters.getSheetZone(sheetId);
35748
35763
  return isInside(col, row, sheetZone) ? "Success" /* CommandResult.Success */ : "TargetOutOfSheet" /* CommandResult.TargetOutOfSheet */;
35749
35764
  }
35765
+ checkUselessClearCell(cmd) {
35766
+ const cell = this.getters.getCell(cmd);
35767
+ if (!cell)
35768
+ return "NoChanges" /* CommandResult.NoChanges */;
35769
+ if (!cell.content && !cell.style && !cell.format) {
35770
+ return "NoChanges" /* CommandResult.NoChanges */;
35771
+ }
35772
+ return "Success" /* CommandResult.Success */;
35773
+ }
35750
35774
  }
35751
35775
  class FormulaCellWithDependencies {
35752
35776
  id;
35753
- compiledFormula;
35754
35777
  format;
35755
35778
  style;
35756
- dependencies;
35757
35779
  sheetId;
35758
- getFormulaCellContent;
35780
+ getRangeString;
35759
35781
  isFormula = true;
35760
- constructor(id, compiledFormula, format, style, dependencies, sheetId, getFormulaCellContent) {
35782
+ compiledFormula;
35783
+ constructor(id, compiledFormula, format, style, dependencies, sheetId, getRangeString) {
35761
35784
  this.id = id;
35762
- this.compiledFormula = compiledFormula;
35763
35785
  this.format = format;
35764
35786
  this.style = style;
35765
- this.dependencies = dependencies;
35766
35787
  this.sheetId = sheetId;
35767
- this.getFormulaCellContent = getFormulaCellContent;
35788
+ this.getRangeString = getRangeString;
35789
+ let rangeIndex = 0;
35790
+ const tokens = compiledFormula.tokens.map((token) => {
35791
+ if (token.type === "REFERENCE") {
35792
+ const index = rangeIndex++;
35793
+ return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
35794
+ }
35795
+ return token;
35796
+ });
35797
+ this.compiledFormula = {
35798
+ ...compiledFormula,
35799
+ dependencies,
35800
+ tokens,
35801
+ };
35768
35802
  }
35769
35803
  get content() {
35770
- return this.getFormulaCellContent(this.sheetId, {
35771
- dependencies: this.dependencies,
35772
- compiledFormula: this.compiledFormula,
35773
- });
35804
+ return concat(this.compiledFormula.tokens.map((token) => token.value));
35805
+ }
35806
+ }
35807
+ class RangeReferenceToken {
35808
+ getValue;
35809
+ type = "REFERENCE";
35810
+ constructor(getValue) {
35811
+ this.getValue = getValue;
35812
+ }
35813
+ get value() {
35814
+ return this.getValue();
35774
35815
  }
35775
35816
  }
35776
35817
 
@@ -36419,6 +36460,9 @@ class DataValidationPlugin extends CorePlugin {
36419
36460
  return this.rules[sheetId].find((rule) => rule.id === id);
36420
36461
  }
36421
36462
  getValidationRuleForCell({ sheetId, col, row }) {
36463
+ if (!this.rules[sheetId]) {
36464
+ return undefined;
36465
+ }
36422
36466
  for (const rule of this.rules[sheetId]) {
36423
36467
  for (const range of rule.ranges) {
36424
36468
  if (isInside(col, row, range.zone)) {
@@ -36915,6 +36959,13 @@ class FiltersPlugin extends CorePlugin {
36915
36959
  }
36916
36960
  onDeleteColumnsRows(cmd) {
36917
36961
  for (const table of this.getFilterTables(cmd.sheetId)) {
36962
+ // Remove the filter tables whose data filter headers are in the removed rows.
36963
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
36964
+ const tables = { ...this.tables[cmd.sheetId] };
36965
+ delete tables[table.id];
36966
+ this.history.update("tables", cmd.sheetId, tables);
36967
+ continue;
36968
+ }
36918
36969
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
36919
36970
  if (!zone) {
36920
36971
  const tables = { ...this.tables[cmd.sheetId] };
@@ -39009,10 +39060,13 @@ class SheetPlugin extends CorePlugin {
39009
39060
  // begin with the end.
39010
39061
  rows.sort((a, b) => b - a);
39011
39062
  for (let group of groupConsecutive(rows)) {
39063
+ // indexes are sorted in the descending order
39064
+ const from = group[group.length - 1];
39065
+ const to = group[0];
39012
39066
  // Move the cells.
39013
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
39014
- // Effectively delete the element and recompute the left-right/top-bottom.
39015
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
39067
+ this.moveCellOnRowsDeletion(sheet, from, to);
39068
+ // Effectively delete the rows
39069
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39016
39070
  }
39017
39071
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39018
39072
  if (count) {
@@ -39034,8 +39088,6 @@ class SheetPlugin extends CorePlugin {
39034
39088
  this.addEmptyRows(sheet, quantity);
39035
39089
  // Move the cells.
39036
39090
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39037
- // Recompute the left-right/top-bottom.
39038
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39039
39091
  if (index < sheet.panes.ySplit) {
39040
39092
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39041
39093
  }
@@ -39136,35 +39188,20 @@ class SheetPlugin extends CorePlugin {
39136
39188
  }
39137
39189
  }
39138
39190
  }
39139
- updateRowsStructureOnDeletion(index, sheet) {
39191
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39140
39192
  const rows = [];
39141
- const cellsQueue = sheet.rows.map((row) => row.cells);
39193
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39142
39194
  for (let i in sheet.rows) {
39143
- if (Number(i) === index) {
39195
+ const row = Number(i);
39196
+ if (row >= deleteFromRow && row <= deleteToRow) {
39144
39197
  continue;
39145
39198
  }
39146
39199
  rows.push({
39147
- cells: cellsQueue.shift(),
39200
+ cells: cellsQueue.pop(),
39148
39201
  });
39149
39202
  }
39150
39203
  this.history.update("sheets", sheet.id, "rows", rows);
39151
39204
  }
39152
- /**
39153
- * Update the rows of the sheet after an addition:
39154
- * - Rename the rows
39155
- *
39156
- * @param sheet Sheet on which the deletion occurs
39157
- * @param addedRow Index of the added row
39158
- * @param rowsToAdd Number of the rows to add
39159
- */
39160
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
39161
- const rows = [];
39162
- const cellsQueue = sheet.rows.map((row) => row.cells);
39163
- sheet.rows.forEach(() => rows.push({
39164
- cells: cellsQueue.shift(),
39165
- }));
39166
- this.history.update("sheets", sheet.id, "rows", rows);
39167
- }
39168
39205
  /**
39169
39206
  * Add empty rows at the end of the rows
39170
39207
  *
@@ -39691,6 +39728,7 @@ class CompilationParametersBuilder {
39691
39728
  getters;
39692
39729
  computeCell;
39693
39730
  evalContext;
39731
+ rangeCache = {};
39694
39732
  constructor(context, getters, computeCell) {
39695
39733
  this.getters = getters;
39696
39734
  this.computeCell = computeCell;
@@ -39776,17 +39814,24 @@ class CompilationParametersBuilder {
39776
39814
  if (!_zone) {
39777
39815
  return [[]];
39778
39816
  }
39817
+ const { top, left, bottom, right } = zone;
39818
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
39819
+ if (cacheKey in this.rangeCache) {
39820
+ return this.rangeCache[cacheKey];
39821
+ }
39779
39822
  const height = _zone.bottom - _zone.top + 1;
39780
39823
  const width = _zone.right - _zone.left + 1;
39781
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
39824
+ const matrix = new Array(width);
39782
39825
  // Performance issue: nested loop is faster than a map here
39783
39826
  for (let col = _zone.left; col <= _zone.right; col++) {
39827
+ const colIndex = col - _zone.left;
39828
+ matrix[colIndex] = new Array(height);
39784
39829
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39785
- const colIndex = col - _zone.left;
39786
39830
  const rowIndex = row - _zone.top;
39787
39831
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39788
39832
  }
39789
39833
  }
39834
+ this.rangeCache[cacheKey] = matrix;
39790
39835
  return matrix;
39791
39836
  }
39792
39837
  }
@@ -39963,6 +40008,13 @@ class Evaluator {
39963
40008
  return (this.evaluatedCells.get(this.encodePosition(position)) ||
39964
40009
  createEvaluatedCell("", { locale: this.getters.getLocale() }));
39965
40010
  }
40011
+ getSpreadPositionsOf(position) {
40012
+ const positionId = this.encodePosition(position);
40013
+ if (!this.spreadingRelations.isArrayFormula(positionId)) {
40014
+ return [];
40015
+ }
40016
+ return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.decodePosition.bind(this));
40017
+ }
39966
40018
  getArrayFormulaSpreadingOn(position) {
39967
40019
  const positionId = this.encodePosition(position);
39968
40020
  const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
@@ -39985,6 +40037,7 @@ class Evaluator {
39985
40037
  this.formulaDependencies().addDependencies(positionId, dependencies);
39986
40038
  }
39987
40039
  updateCompilationParameters() {
40040
+ // rebuild the compilation parameters (with a clean cache)
39988
40041
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
39989
40042
  }
39990
40043
  evaluateCells(positions) {
@@ -40028,6 +40081,16 @@ class Evaluator {
40028
40081
  this.evaluatedCells = new Map();
40029
40082
  this.evaluate(this.getAllCells());
40030
40083
  }
40084
+ evaluateFormula(sheetId, formulaString) {
40085
+ const compiledFormula = compile(formulaString);
40086
+ const ranges = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
40087
+ this.updateCompilationParameters();
40088
+ const array = compiledFormula.execute(ranges, ...this.compilationParams);
40089
+ if (isMatrix(array)) {
40090
+ return matrixMap(array, (cell) => cell.value);
40091
+ }
40092
+ return array.value;
40093
+ }
40031
40094
  getAllCells() {
40032
40095
  const positionIds = new JetSet();
40033
40096
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40054,6 +40117,7 @@ class Evaluator {
40054
40117
  this.nextPositionsToUpdate = cells;
40055
40118
  let currentIteration = 0;
40056
40119
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40120
+ this.updateCompilationParameters();
40057
40121
  const positionIds = Array.from(this.nextPositionsToUpdate);
40058
40122
  this.nextPositionsToUpdate.clear();
40059
40123
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40121,7 +40185,7 @@ class Evaluator {
40121
40185
  const position = this.compilationParams[2].getters.getCellPosition(cellId);
40122
40186
  return toXC(position.col, position.row);
40123
40187
  };
40124
- const formulaReturn = cellData.compiledFormula.execute(cellData.dependencies, ...this.compilationParams);
40188
+ const formulaReturn = cellData.compiledFormula.execute(cellData.compiledFormula.dependencies, ...this.compilationParams);
40125
40189
  if (!isMatrix(formulaReturn)) {
40126
40190
  return createEvaluatedCell(formulaReturn.value, {
40127
40191
  format: cellData.format || formulaReturn.format,
@@ -40221,7 +40285,7 @@ class Evaluator {
40221
40285
  return [];
40222
40286
  }
40223
40287
  const dependencies = [];
40224
- for (const range of cell.dependencies) {
40288
+ for (const range of cell.compiledFormula.dependencies) {
40225
40289
  if (range.invalidSheetName || range.invalidXc) {
40226
40290
  continue;
40227
40291
  }
@@ -40417,7 +40481,6 @@ class PositionBitsEncoder {
40417
40481
  // of other cells depending on it, at the next iteration.
40418
40482
  //#endregion
40419
40483
  class EvaluationPlugin extends UIPlugin {
40420
- config;
40421
40484
  static getters = [
40422
40485
  "evaluateFormula",
40423
40486
  "getCorrespondingFormulaCell",
@@ -40427,15 +40490,14 @@ class EvaluationPlugin extends UIPlugin {
40427
40490
  "getEvaluatedCell",
40428
40491
  "getEvaluatedCells",
40429
40492
  "getEvaluatedCellsInZone",
40493
+ "getSpreadPositionsOf",
40494
+ "getArrayFormulaSpreadingOn",
40430
40495
  ];
40431
40496
  shouldRebuildDependenciesGraph = true;
40432
40497
  evaluator;
40433
- compilationParams;
40434
40498
  positionsToUpdate = [];
40435
40499
  constructor(config) {
40436
40500
  super(config);
40437
- this.config = config;
40438
- this.compilationParams = this.getCompilationParameters();
40439
40501
  this.evaluator = new Evaluator(config.custom, this.getters);
40440
40502
  }
40441
40503
  // ---------------------------------------------------------------------------
@@ -40460,10 +40522,6 @@ class EvaluationPlugin extends UIPlugin {
40460
40522
  case "EVALUATE_CELLS":
40461
40523
  this.evaluator.evaluateAllCells();
40462
40524
  break;
40463
- case "UPDATE_LOCALE":
40464
- this.compilationParams = this.getCompilationParameters();
40465
- this.evaluator.updateCompilationParameters();
40466
- break;
40467
40525
  }
40468
40526
  }
40469
40527
  finalize() {
@@ -40481,16 +40539,7 @@ class EvaluationPlugin extends UIPlugin {
40481
40539
  // Getters
40482
40540
  // ---------------------------------------------------------------------------
40483
40541
  evaluateFormula(sheetId, formulaString) {
40484
- const compiledFormula = compile(formulaString);
40485
- const ranges = [];
40486
- for (let xc of compiledFormula.dependencies) {
40487
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
40488
- }
40489
- const array = compiledFormula.execute(ranges, ...this.compilationParams);
40490
- if (isMatrix(array)) {
40491
- return array.map((col) => col.map((row) => row.value));
40492
- }
40493
- return array.value;
40542
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40494
40543
  }
40495
40544
  /**
40496
40545
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40536,6 +40585,12 @@ class EvaluationPlugin extends UIPlugin {
40536
40585
  getEvaluatedCellsInZone(sheetId, zone) {
40537
40586
  return positions(zone).map(({ col, row }) => this.getters.getEvaluatedCell({ sheetId, col, row }));
40538
40587
  }
40588
+ getSpreadPositionsOf(position) {
40589
+ return this.evaluator.getSpreadPositionsOf(position);
40590
+ }
40591
+ getArrayFormulaSpreadingOn(position) {
40592
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40593
+ }
40539
40594
  // ---------------------------------------------------------------------------
40540
40595
  // Export
40541
40596
  // ---------------------------------------------------------------------------
@@ -40573,13 +40628,13 @@ class EvaluationPlugin extends UIPlugin {
40573
40628
  */
40574
40629
  getCorrespondingFormulaCell(position) {
40575
40630
  const cell = this.getters.getCell(position);
40576
- if (cell && cell.content) {
40577
- if (cell.isFormula && !isBadExpression(cell.content)) {
40578
- return cell;
40579
- }
40631
+ if (cell && cell.isFormula) {
40632
+ return isBadExpression(cell.compiledFormula.tokens) ? undefined : cell;
40633
+ }
40634
+ else if (cell && cell.content) {
40580
40635
  return undefined;
40581
40636
  }
40582
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40637
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40583
40638
  if (spreadingFormulaPosition === undefined) {
40584
40639
  return undefined;
40585
40640
  }
@@ -40589,13 +40644,10 @@ class EvaluationPlugin extends UIPlugin {
40589
40644
  }
40590
40645
  return undefined;
40591
40646
  }
40592
- getCompilationParameters() {
40593
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40594
- }
40595
40647
  }
40596
- function isBadExpression(formula) {
40648
+ function isBadExpression(tokens) {
40597
40649
  try {
40598
- compile(formula);
40650
+ compileTokens(tokens);
40599
40651
  return false;
40600
40652
  }
40601
40653
  catch (error) {
@@ -40764,22 +40816,20 @@ class EvaluationChartPlugin extends UIPlugin {
40764
40816
  invalidateCFEvaluationCommands.has(cmd.type) ||
40765
40817
  cmd.type === "EVALUATE_CELLS" ||
40766
40818
  cmd.type === "UPDATE_CELL") {
40767
- if (cmd.type !== "UNDO" && cmd.type !== "REDO") {
40768
- for (const chartId in this.charts) {
40769
- this.history.update("charts", chartId, undefined);
40770
- }
40819
+ for (const chartId in this.charts) {
40820
+ this.charts[chartId] = undefined;
40771
40821
  }
40772
40822
  }
40773
40823
  switch (cmd.type) {
40774
40824
  case "UPDATE_CHART":
40775
40825
  case "CREATE_CHART":
40776
40826
  case "DELETE_FIGURE":
40777
- this.history.update("charts", cmd.id, undefined);
40827
+ this.charts[cmd.id] = undefined;
40778
40828
  break;
40779
40829
  case "DELETE_SHEET":
40780
40830
  for (let chartId in this.charts) {
40781
40831
  if (!this.getters.isChartDefined(chartId)) {
40782
- this.history.update("charts", chartId, undefined);
40832
+ this.charts[chartId] = undefined;
40783
40833
  }
40784
40834
  }
40785
40835
  break;
@@ -40791,8 +40841,7 @@ class EvaluationChartPlugin extends UIPlugin {
40791
40841
  if (!chart) {
40792
40842
  throw new Error(`No chart for the given id: ${figureId}`);
40793
40843
  }
40794
- const runtime = this.createRuntimeChart(chart);
40795
- this.history.update("charts", figureId, runtime);
40844
+ this.charts[figureId] = this.createRuntimeChart(chart);
40796
40845
  }
40797
40846
  return this.charts[figureId];
40798
40847
  }
@@ -40944,7 +40993,10 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
40944
40993
  const values = cf.rule.values.map((value, i) => {
40945
40994
  const compiledFormula = formulas[i];
40946
40995
  if (compiledFormula) {
40947
- return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, compiledFormula, compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
40996
+ return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
40997
+ ...compiledFormula,
40998
+ dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
40999
+ });
40948
41000
  }
40949
41001
  return value;
40950
41002
  });
@@ -41316,7 +41368,10 @@ class EvaluationDataValidationPlugin extends UIPlugin {
41316
41368
  }
41317
41369
  try {
41318
41370
  const formula = compile(value);
41319
- const translatedFormula = this.getters.getTranslatedCellFormula(sheetId, offset.col, offset.row, formula, formula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
41371
+ const translatedFormula = this.getters.getTranslatedCellFormula(sheetId, offset.col, offset.row, {
41372
+ ...formula,
41373
+ dependencies: formula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
41374
+ });
41320
41375
  const evaluated = this.getters.evaluateFormula(sheetId, translatedFormula);
41321
41376
  return evaluated && !isMatrix(evaluated) ? evaluated.toString() : "";
41322
41377
  }
@@ -43287,11 +43342,26 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43287
43342
  let cellsInRow = [];
43288
43343
  for (let col of columnsIndex) {
43289
43344
  const position = { col, row, sheetId };
43345
+ const spreader = getters.getArrayFormulaSpreadingOn(position);
43346
+ let cell = getters.getCell(position);
43347
+ const evaluatedCell = getters.getEvaluatedCell(position);
43348
+ if (spreader) {
43349
+ const isSpreaderCopied = rowsIndex.includes(spreader.row) && columnsIndex.includes(spreader.col);
43350
+ const content = isSpreaderCopied
43351
+ ? ""
43352
+ : formatValue(evaluatedCell.value, { locale: getters.getLocale() });
43353
+ cell = {
43354
+ id: cell?.id || "",
43355
+ style: cell?.style,
43356
+ format: evaluatedCell.format,
43357
+ content,
43358
+ isFormula: false,
43359
+ };
43360
+ }
43290
43361
  cellsInRow.push({
43291
- cell: getters.getCell(position),
43292
- style: getters.getCellComputedStyle(position),
43293
- evaluatedCell: getters.getEvaluatedCell(position),
43362
+ cell,
43294
43363
  border: getters.getCellBorder(position) || undefined,
43364
+ evaluatedCell,
43295
43365
  position,
43296
43366
  });
43297
43367
  }
@@ -43413,13 +43483,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43413
43483
  });
43414
43484
  }
43415
43485
  this.pasteCopiedTables(target);
43416
- this.cells.forEach((row) => {
43417
- row.forEach((c) => {
43418
- if (c.cell) {
43419
- c.cell = undefined;
43420
- }
43421
- });
43422
- });
43423
43486
  }
43424
43487
  /**
43425
43488
  * The clipped zone is copied as many times as it fits in the target.
@@ -43491,7 +43554,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43491
43554
  clearClippedZones() {
43492
43555
  for (const row of this.cells) {
43493
43556
  for (const cell of row) {
43494
- if (cell.cell) {
43557
+ if (cell?.cell) {
43495
43558
  this.dispatch("CLEAR_CELL", cell.position);
43496
43559
  }
43497
43560
  }
@@ -43517,6 +43580,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43517
43580
  const rowCells = this.cells[r];
43518
43581
  for (let c = 0; c < width; c++) {
43519
43582
  const origin = rowCells[c];
43583
+ if (!origin) {
43584
+ continue;
43585
+ }
43520
43586
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43521
43587
  // TODO: refactor this part. the "Paste merge" action is also executed with
43522
43588
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43540,53 +43606,42 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43540
43606
  pasteCell(origin, target, operation, clipboardOption) {
43541
43607
  const { sheetId, col, row } = target;
43542
43608
  const targetCell = this.getters.getEvaluatedCell(target);
43543
- if (clipboardOption?.pasteOption !== "onlyValue") {
43544
- const targetBorders = this.getters.getCellBorder(target);
43545
- const originBorders = origin.border;
43546
- const border = {
43547
- top: targetBorders?.top || originBorders?.top,
43548
- bottom: targetBorders?.bottom || originBorders?.bottom,
43549
- left: targetBorders?.left || originBorders?.left,
43550
- right: targetBorders?.right || originBorders?.right,
43551
- };
43552
- this.dispatch("SET_BORDER", { sheetId, col, row, border });
43609
+ if (clipboardOption?.pasteOption === "onlyValue") {
43610
+ const locale = this.getters.getLocale();
43611
+ const content = formatValue(origin.evaluatedCell.value, { locale });
43612
+ this.dispatch("UPDATE_CELL", { ...target, content });
43613
+ return;
43553
43614
  }
43554
- if (origin.cell) {
43555
- if (clipboardOption?.pasteOption === "onlyFormat") {
43556
- this.dispatch("UPDATE_CELL", {
43557
- ...target,
43558
- style: origin.cell.style,
43559
- format: origin.evaluatedCell.format,
43560
- });
43561
- return;
43562
- }
43563
- if (clipboardOption?.pasteOption === "onlyValue") {
43564
- const locale = this.getters.getLocale();
43565
- const content = formatValue(origin.evaluatedCell.value, { locale });
43566
- this.dispatch("UPDATE_CELL", { ...target, content });
43567
- return;
43568
- }
43569
- let content = origin.cell.content;
43570
- if (origin.cell.isFormula && operation === "COPY") {
43571
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula, origin.cell.dependencies);
43572
- }
43615
+ const targetBorders = this.getters.getCellBorder(target);
43616
+ const originBorders = origin.border;
43617
+ const border = {
43618
+ top: targetBorders?.top || originBorders?.top,
43619
+ bottom: targetBorders?.bottom || originBorders?.bottom,
43620
+ left: targetBorders?.left || originBorders?.left,
43621
+ right: targetBorders?.right || originBorders?.right,
43622
+ };
43623
+ this.dispatch("SET_BORDER", { sheetId, col, row, border });
43624
+ if (clipboardOption?.pasteOption === "onlyFormat") {
43625
+ this.dispatch("UPDATE_CELL", {
43626
+ ...target,
43627
+ style: origin.cell?.style ?? null,
43628
+ format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
43629
+ });
43630
+ return;
43631
+ }
43632
+ const content = origin.cell && origin.cell.isFormula && operation === "COPY"
43633
+ ? this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula)
43634
+ : origin.cell?.content;
43635
+ if (content !== "" || origin.cell?.format || origin.cell?.style) {
43573
43636
  this.dispatch("UPDATE_CELL", {
43574
43637
  ...target,
43575
43638
  content,
43576
- style: origin.cell.style || null,
43577
- format: origin.cell.format,
43639
+ style: origin.cell?.style || null,
43640
+ format: origin.cell?.format,
43578
43641
  });
43579
43642
  }
43580
43643
  else if (targetCell) {
43581
- if (clipboardOption?.pasteOption === "onlyValue") {
43582
- this.dispatch("UPDATE_CELL", { ...target, content: "" });
43583
- }
43584
- else if (clipboardOption?.pasteOption === "onlyFormat") {
43585
- this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" });
43586
- }
43587
- else {
43588
- this.dispatch("CLEAR_CELL", target);
43589
- }
43644
+ this.dispatch("CLEAR_CELL", target);
43590
43645
  }
43591
43646
  }
43592
43647
  /**
@@ -43648,22 +43703,28 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43648
43703
  return (this.cells
43649
43704
  .map((cells) => {
43650
43705
  return cells
43651
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43706
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43652
43707
  ? c.cell?.content || ""
43653
- : c.evaluatedCell?.formattedValue || "")
43708
+ : c?.evaluatedCell?.formattedValue || "")
43654
43709
  .join("\t");
43655
43710
  })
43656
43711
  .join("\n") || "\t");
43657
43712
  }
43658
43713
  getHTMLContent() {
43659
43714
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43715
+ if (!this.cells[0][0]) {
43716
+ return "";
43717
+ }
43660
43718
  return this.getters.getCellText(this.cells[0][0].position);
43661
43719
  }
43662
43720
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43663
43721
  for (const row of this.cells) {
43664
43722
  htmlTable += "<tr>";
43665
43723
  for (const cell of row) {
43666
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43724
+ if (!cell) {
43725
+ continue;
43726
+ }
43727
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43667
43728
  const cellText = this.getters.getCellText(cell.position);
43668
43729
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43669
43730
  }
@@ -44100,20 +44161,29 @@ class FindAndReplacePlugin extends UIPlugin {
44100
44161
  const sheetId = this.getters.getActiveSheetId();
44101
44162
  const cells = this.getters.getCells(sheetId);
44102
44163
  const matches = [];
44103
- if (this.toSearch) {
44164
+ if (this.toSearch && this.currentSearchRegex) {
44104
44165
  for (const cell of Object.values(cells)) {
44105
44166
  const { col, row } = this.getters.getCellPosition(cell.id);
44167
+ const cellPosition = { sheetId, col, row };
44106
44168
  const isColHidden = this.getters.isColHidden(sheetId, col);
44107
44169
  const isRowHidden = this.getters.isRowHidden(sheetId, row);
44108
44170
  if (isColHidden || isRowHidden) {
44109
44171
  continue;
44110
44172
  }
44111
- if (cell &&
44112
- this.currentSearchRegex &&
44113
- this.currentSearchRegex.test(this.getSearchableString({ sheetId, col, row }))) {
44173
+ if (this.currentSearchRegex.test(this.getSearchableString(cellPosition))) {
44114
44174
  const match = { col, row, selected: false };
44115
44175
  matches.push(match);
44116
44176
  }
44177
+ for (const spreadPosition of this.getters.getSpreadPositionsOf(cellPosition)) {
44178
+ if (this.currentSearchRegex.test(this.getSearchableString(spreadPosition))) {
44179
+ const match = {
44180
+ col: spreadPosition.col,
44181
+ row: spreadPosition.row,
44182
+ selected: false,
44183
+ };
44184
+ matches.push(match);
44185
+ }
44186
+ }
44117
44187
  }
44118
44188
  }
44119
44189
  return matches.sort(this.sortByRowThenColumn);
@@ -44172,16 +44242,19 @@ class FindAndReplacePlugin extends UIPlugin {
44172
44242
  return;
44173
44243
  }
44174
44244
  const sheetId = this.getters.getActiveSheetId();
44175
- const cell = this.getters.getCell({ sheetId, ...selectedMatch });
44176
- const { col, row } = selectedMatch;
44245
+ const position = { sheetId, ...selectedMatch };
44246
+ const cell = this.getters.getCell(position);
44247
+ if (!cell?.content) {
44248
+ return;
44249
+ }
44177
44250
  if (cell?.isFormula && !this.searchOptions.searchFormulas) {
44178
44251
  return;
44179
44252
  }
44180
44253
  const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
44181
- const toReplace = this.getSearchableString({ sheetId, col, row });
44254
+ const toReplace = this.getSearchableString(position);
44182
44255
  const content = toReplace.replace(replaceRegex, replaceWith);
44183
44256
  const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
44184
- this.dispatch("UPDATE_CELL", { sheetId, col, row, content: canonicalContent });
44257
+ this.dispatch("UPDATE_CELL", { ...position, content: canonicalContent });
44185
44258
  }
44186
44259
  /**
44187
44260
  * Replace the value of the currently selected match
@@ -45756,7 +45829,7 @@ class SortPlugin extends UIPlugin {
45756
45829
  if (cell.isFormula) {
45757
45830
  const position = this.getters.getCellPosition(cell.id);
45758
45831
  // we only have a vertical offset
45759
- content = this.getters.getTranslatedCellFormula(sheetId, 0, newRow - position.row, cell.compiledFormula, cell.dependencies);
45832
+ content = this.getters.getTranslatedCellFormula(sheetId, 0, newRow - position.row, cell.compiledFormula);
45760
45833
  }
45761
45834
  newCellValues.style = cell.style;
45762
45835
  newCellValues.content = content;
@@ -46781,11 +46854,14 @@ class ClipboardPlugin extends UIPlugin {
46781
46854
  }
46782
46855
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46783
46856
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
46857
+ if (this.state.operation === "CUT") {
46858
+ this.state = undefined;
46859
+ }
46784
46860
  this.lastPasteState = this.state;
46785
46861
  if (this.paintFormatStatus === "oneOff") {
46786
46862
  this.paintFormatStatus = "inactive";
46787
- this.status = "invisible";
46788
46863
  }
46864
+ this.status = "invisible";
46789
46865
  break;
46790
46866
  case "COPY_PASTE_CELLS_ABOVE":
46791
46867
  {
@@ -47819,6 +47895,8 @@ class FilterEvaluationPlugin extends UIPlugin {
47819
47895
  case "EVALUATE_CELLS":
47820
47896
  case "ACTIVATE_SHEET":
47821
47897
  case "REMOVE_FILTER_TABLE":
47898
+ case "ADD_COLUMNS_ROWS":
47899
+ case "REMOVE_COLUMNS_ROWS":
47822
47900
  this.isEvaluationDirty = true;
47823
47901
  break;
47824
47902
  case "START":
@@ -55507,19 +55585,22 @@ class Model extends EventBus {
55507
55585
  * Check if the given command is allowed by all the plugins and the history.
55508
55586
  */
55509
55587
  checkDispatchAllowed(command) {
55510
- if (isCoreCommand(command)) {
55511
- return this.checkDispatchAllowedCoreCommand(command);
55588
+ const results = isCoreCommand(command)
55589
+ ? this.checkDispatchAllowedCoreCommand(command)
55590
+ : this.checkDispatchAllowedLocalCommand(command);
55591
+ if (results.some((r) => r !== "Success" /* CommandResult.Success */)) {
55592
+ return new DispatchResult(results.flat());
55512
55593
  }
55513
- return this.checkDispatchAllowedLocalCommand(command);
55594
+ return DispatchResult.Success;
55514
55595
  }
55515
55596
  checkDispatchAllowedCoreCommand(command) {
55516
55597
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55517
55598
  results.push(this.range.allowDispatch(command));
55518
- return new DispatchResult(results.flat());
55599
+ return results;
55519
55600
  }
55520
55601
  checkDispatchAllowedLocalCommand(command) {
55521
55602
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55522
- return new DispatchResult(results.flat());
55603
+ return results;
55523
55604
  }
55524
55605
  finalize() {
55525
55606
  this.status = 3 /* Status.Finalizing */;
@@ -55828,9 +55909,9 @@ const constants = {
55828
55909
  DEFAULT_LOCALE,
55829
55910
  };
55830
55911
 
55831
- export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
55912
+ 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 };
55832
55913
 
55833
55914
 
55834
- __info__.version = "17.0.2";
55835
- __info__.date = "2023-11-03T12:22:44.945Z";
55836
- __info__.hash = "3184728";
55915
+ __info__.version = "17.0.4";
55916
+ __info__.date = "2023-11-24T13:13:42.575Z";
55917
+ __info__.hash = "7346d0a";