@odoo/o-spreadsheet 17.0.3 → 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.3
6
- * @date 2023-11-16T11:59:49.524Z
7
- * @hash 8bd0fb2
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) {
@@ -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;
@@ -29757,6 +29758,7 @@ class Grid extends Component {
29757
29758
  const content = clipboardData.getData(ClipboardMIMEType.PlainText);
29758
29759
  const target = this.env.model.getters.getSelectedZones();
29759
29760
  const clipboardString = this.env.model.getters.getClipboardTextContent();
29761
+ const isCutOperation = this.env.model.getters.isCutOperation();
29760
29762
  if (clipboardString === content) {
29761
29763
  // the paste actually comes from o-spreadsheet itself
29762
29764
  interactivePaste(this.env, target);
@@ -29764,7 +29766,7 @@ class Grid extends Component {
29764
29766
  else {
29765
29767
  interactivePasteFromOS(this.env, target, content);
29766
29768
  }
29767
- if (this.env.model.getters.isCutOperation()) {
29769
+ if (isCutOperation) {
29768
29770
  await this.env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
29769
29771
  }
29770
29772
  }
@@ -34058,6 +34060,14 @@ class BordersPlugin extends CorePlugin {
34058
34060
  // ---------------------------------------------------------------------------
34059
34061
  // Command Handling
34060
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
+ }
34061
34071
  handle(cmd) {
34062
34072
  switch (cmd.type) {
34063
34073
  case "ADD_MERGE":
@@ -34477,6 +34487,14 @@ class BordersPlugin extends CorePlugin {
34477
34487
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
34478
34488
  }
34479
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
+ }
34480
34498
  // ---------------------------------------------------------------------------
34481
34499
  // Import/Export
34482
34500
  // ---------------------------------------------------------------------------
@@ -35332,8 +35350,9 @@ class CellPlugin extends CorePlugin {
35332
35350
  allowDispatch(cmd) {
35333
35351
  switch (cmd.type) {
35334
35352
  case "UPDATE_CELL":
35353
+ return this.checkCellOutOfSheet(cmd);
35335
35354
  case "CLEAR_CELL":
35336
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
35355
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35337
35356
  default:
35338
35357
  return "Success" /* CommandResult.Success */;
35339
35358
  }
@@ -35502,14 +35521,9 @@ class CellPlugin extends CorePlugin {
35502
35521
  */
35503
35522
  getCellById(cellId) {
35504
35523
  // this must be as fast as possible
35505
- for (const sheetId in this.cells) {
35506
- const sheet = this.cells[sheetId];
35507
- const cell = sheet[cellId];
35508
- if (cell) {
35509
- return cell;
35510
- }
35511
- }
35512
- return undefined;
35524
+ const position = this.getters.getCellPosition(cellId);
35525
+ const sheet = this.cells[position.sheetId];
35526
+ return sheet[cellId];
35513
35527
  }
35514
35528
  /*
35515
35529
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -35740,13 +35754,23 @@ class CellPlugin extends CorePlugin {
35740
35754
  },
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;
@@ -36436,6 +36460,9 @@ class DataValidationPlugin extends CorePlugin {
36436
36460
  return this.rules[sheetId].find((rule) => rule.id === id);
36437
36461
  }
36438
36462
  getValidationRuleForCell({ sheetId, col, row }) {
36463
+ if (!this.rules[sheetId]) {
36464
+ return undefined;
36465
+ }
36439
36466
  for (const rule of this.rules[sheetId]) {
36440
36467
  for (const range of rule.ranges) {
36441
36468
  if (isInside(col, row, range.zone)) {
@@ -39033,10 +39060,13 @@ class SheetPlugin extends CorePlugin {
39033
39060
  // begin with the end.
39034
39061
  rows.sort((a, b) => b - a);
39035
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];
39036
39066
  // Move the cells.
39037
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
39038
- // Effectively delete the element and recompute the left-right/top-bottom.
39039
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
39067
+ this.moveCellOnRowsDeletion(sheet, from, to);
39068
+ // Effectively delete the rows
39069
+ this.updateRowsStructureOnDeletion(sheet, from, to);
39040
39070
  }
39041
39071
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
39042
39072
  if (count) {
@@ -39058,8 +39088,6 @@ class SheetPlugin extends CorePlugin {
39058
39088
  this.addEmptyRows(sheet, quantity);
39059
39089
  // Move the cells.
39060
39090
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
39061
- // Recompute the left-right/top-bottom.
39062
- this.updateRowsStructureOnAddition(sheet, row, quantity);
39063
39091
  if (index < sheet.panes.ySplit) {
39064
39092
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
39065
39093
  }
@@ -39160,35 +39188,20 @@ class SheetPlugin extends CorePlugin {
39160
39188
  }
39161
39189
  }
39162
39190
  }
39163
- updateRowsStructureOnDeletion(index, sheet) {
39191
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
39164
39192
  const rows = [];
39165
- const cellsQueue = sheet.rows.map((row) => row.cells);
39193
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
39166
39194
  for (let i in sheet.rows) {
39167
- if (Number(i) === index) {
39195
+ const row = Number(i);
39196
+ if (row >= deleteFromRow && row <= deleteToRow) {
39168
39197
  continue;
39169
39198
  }
39170
39199
  rows.push({
39171
- cells: cellsQueue.shift(),
39200
+ cells: cellsQueue.pop(),
39172
39201
  });
39173
39202
  }
39174
39203
  this.history.update("sheets", sheet.id, "rows", rows);
39175
39204
  }
39176
- /**
39177
- * Update the rows of the sheet after an addition:
39178
- * - Rename the rows
39179
- *
39180
- * @param sheet Sheet on which the deletion occurs
39181
- * @param addedRow Index of the added row
39182
- * @param rowsToAdd Number of the rows to add
39183
- */
39184
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
39185
- const rows = [];
39186
- const cellsQueue = sheet.rows.map((row) => row.cells);
39187
- sheet.rows.forEach(() => rows.push({
39188
- cells: cellsQueue.shift(),
39189
- }));
39190
- this.history.update("sheets", sheet.id, "rows", rows);
39191
- }
39192
39205
  /**
39193
39206
  * Add empty rows at the end of the rows
39194
39207
  *
@@ -39715,6 +39728,7 @@ class CompilationParametersBuilder {
39715
39728
  getters;
39716
39729
  computeCell;
39717
39730
  evalContext;
39731
+ rangeCache = {};
39718
39732
  constructor(context, getters, computeCell) {
39719
39733
  this.getters = getters;
39720
39734
  this.computeCell = computeCell;
@@ -39800,17 +39814,24 @@ class CompilationParametersBuilder {
39800
39814
  if (!_zone) {
39801
39815
  return [[]];
39802
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
+ }
39803
39822
  const height = _zone.bottom - _zone.top + 1;
39804
39823
  const width = _zone.right - _zone.left + 1;
39805
- const matrix = Array.from({ length: width }, () => Array.from({ length: height }));
39824
+ const matrix = new Array(width);
39806
39825
  // Performance issue: nested loop is faster than a map here
39807
39826
  for (let col = _zone.left; col <= _zone.right; col++) {
39827
+ const colIndex = col - _zone.left;
39828
+ matrix[colIndex] = new Array(height);
39808
39829
  for (let row = _zone.top; row <= _zone.bottom; row++) {
39809
- const colIndex = col - _zone.left;
39810
39830
  const rowIndex = row - _zone.top;
39811
39831
  matrix[colIndex][rowIndex] = this.readCell({ sheetId, col, row });
39812
39832
  }
39813
39833
  }
39834
+ this.rangeCache[cacheKey] = matrix;
39814
39835
  return matrix;
39815
39836
  }
39816
39837
  }
@@ -40016,6 +40037,7 @@ class Evaluator {
40016
40037
  this.formulaDependencies().addDependencies(positionId, dependencies);
40017
40038
  }
40018
40039
  updateCompilationParameters() {
40040
+ // rebuild the compilation parameters (with a clean cache)
40019
40041
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40020
40042
  }
40021
40043
  evaluateCells(positions) {
@@ -40059,6 +40081,16 @@ class Evaluator {
40059
40081
  this.evaluatedCells = new Map();
40060
40082
  this.evaluate(this.getAllCells());
40061
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
+ }
40062
40094
  getAllCells() {
40063
40095
  const positionIds = new JetSet();
40064
40096
  for (const sheetId of this.getters.getSheetIds()) {
@@ -40085,6 +40117,7 @@ class Evaluator {
40085
40117
  this.nextPositionsToUpdate = cells;
40086
40118
  let currentIteration = 0;
40087
40119
  while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
40120
+ this.updateCompilationParameters();
40088
40121
  const positionIds = Array.from(this.nextPositionsToUpdate);
40089
40122
  this.nextPositionsToUpdate.clear();
40090
40123
  for (let i = 0; i < positionIds.length; ++i) {
@@ -40448,7 +40481,6 @@ class PositionBitsEncoder {
40448
40481
  // of other cells depending on it, at the next iteration.
40449
40482
  //#endregion
40450
40483
  class EvaluationPlugin extends UIPlugin {
40451
- config;
40452
40484
  static getters = [
40453
40485
  "evaluateFormula",
40454
40486
  "getCorrespondingFormulaCell",
@@ -40459,15 +40491,13 @@ class EvaluationPlugin extends UIPlugin {
40459
40491
  "getEvaluatedCells",
40460
40492
  "getEvaluatedCellsInZone",
40461
40493
  "getSpreadPositionsOf",
40494
+ "getArrayFormulaSpreadingOn",
40462
40495
  ];
40463
40496
  shouldRebuildDependenciesGraph = true;
40464
40497
  evaluator;
40465
- compilationParams;
40466
40498
  positionsToUpdate = [];
40467
40499
  constructor(config) {
40468
40500
  super(config);
40469
- this.config = config;
40470
- this.compilationParams = this.getCompilationParameters();
40471
40501
  this.evaluator = new Evaluator(config.custom, this.getters);
40472
40502
  }
40473
40503
  // ---------------------------------------------------------------------------
@@ -40492,10 +40522,6 @@ class EvaluationPlugin extends UIPlugin {
40492
40522
  case "EVALUATE_CELLS":
40493
40523
  this.evaluator.evaluateAllCells();
40494
40524
  break;
40495
- case "UPDATE_LOCALE":
40496
- this.compilationParams = this.getCompilationParameters();
40497
- this.evaluator.updateCompilationParameters();
40498
- break;
40499
40525
  }
40500
40526
  }
40501
40527
  finalize() {
@@ -40513,16 +40539,7 @@ class EvaluationPlugin extends UIPlugin {
40513
40539
  // Getters
40514
40540
  // ---------------------------------------------------------------------------
40515
40541
  evaluateFormula(sheetId, formulaString) {
40516
- const compiledFormula = compile(formulaString);
40517
- const ranges = [];
40518
- for (let xc of compiledFormula.dependencies) {
40519
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
40520
- }
40521
- const array = compiledFormula.execute(ranges, ...this.compilationParams);
40522
- if (isMatrix(array)) {
40523
- return array.map((col) => col.map((row) => row.value));
40524
- }
40525
- return array.value;
40542
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
40526
40543
  }
40527
40544
  /**
40528
40545
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -40571,6 +40588,9 @@ class EvaluationPlugin extends UIPlugin {
40571
40588
  getSpreadPositionsOf(position) {
40572
40589
  return this.evaluator.getSpreadPositionsOf(position);
40573
40590
  }
40591
+ getArrayFormulaSpreadingOn(position) {
40592
+ return this.evaluator.getArrayFormulaSpreadingOn(position);
40593
+ }
40574
40594
  // ---------------------------------------------------------------------------
40575
40595
  // Export
40576
40596
  // ---------------------------------------------------------------------------
@@ -40614,7 +40634,7 @@ class EvaluationPlugin extends UIPlugin {
40614
40634
  else if (cell && cell.content) {
40615
40635
  return undefined;
40616
40636
  }
40617
- const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
40637
+ const spreadingFormulaPosition = this.getArrayFormulaSpreadingOn(position);
40618
40638
  if (spreadingFormulaPosition === undefined) {
40619
40639
  return undefined;
40620
40640
  }
@@ -40624,9 +40644,6 @@ class EvaluationPlugin extends UIPlugin {
40624
40644
  }
40625
40645
  return undefined;
40626
40646
  }
40627
- getCompilationParameters() {
40628
- return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40629
- }
40630
40647
  }
40631
40648
  function isBadExpression(tokens) {
40632
40649
  try {
@@ -43325,11 +43342,26 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43325
43342
  let cellsInRow = [];
43326
43343
  for (let col of columnsIndex) {
43327
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
+ }
43328
43361
  cellsInRow.push({
43329
- cell: getters.getCell(position),
43330
- style: getters.getCellComputedStyle(position),
43331
- evaluatedCell: getters.getEvaluatedCell(position),
43362
+ cell,
43332
43363
  border: getters.getCellBorder(position) || undefined,
43364
+ evaluatedCell,
43333
43365
  position,
43334
43366
  });
43335
43367
  }
@@ -43451,13 +43483,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43451
43483
  });
43452
43484
  }
43453
43485
  this.pasteCopiedTables(target);
43454
- this.cells.forEach((row) => {
43455
- row.forEach((c) => {
43456
- if (c.cell) {
43457
- c.cell = undefined;
43458
- }
43459
- });
43460
- });
43461
43486
  }
43462
43487
  /**
43463
43488
  * The clipped zone is copied as many times as it fits in the target.
@@ -43529,7 +43554,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43529
43554
  clearClippedZones() {
43530
43555
  for (const row of this.cells) {
43531
43556
  for (const cell of row) {
43532
- if (cell.cell) {
43557
+ if (cell?.cell) {
43533
43558
  this.dispatch("CLEAR_CELL", cell.position);
43534
43559
  }
43535
43560
  }
@@ -43555,6 +43580,9 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43555
43580
  const rowCells = this.cells[r];
43556
43581
  for (let c = 0; c < width; c++) {
43557
43582
  const origin = rowCells[c];
43583
+ if (!origin) {
43584
+ continue;
43585
+ }
43558
43586
  const position = { col: col + c, row: row + r, sheetId: sheetId };
43559
43587
  // TODO: refactor this part. the "Paste merge" action is also executed with
43560
43588
  // MOVE_RANGES in pasteFromCut. Adding a condition on the operation type here
@@ -43578,53 +43606,42 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43578
43606
  pasteCell(origin, target, operation, clipboardOption) {
43579
43607
  const { sheetId, col, row } = target;
43580
43608
  const targetCell = this.getters.getEvaluatedCell(target);
43581
- if (clipboardOption?.pasteOption !== "onlyValue") {
43582
- const targetBorders = this.getters.getCellBorder(target);
43583
- const originBorders = origin.border;
43584
- const border = {
43585
- top: targetBorders?.top || originBorders?.top,
43586
- bottom: targetBorders?.bottom || originBorders?.bottom,
43587
- left: targetBorders?.left || originBorders?.left,
43588
- right: targetBorders?.right || originBorders?.right,
43589
- };
43590
- 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;
43591
43614
  }
43592
- if (origin.cell) {
43593
- if (clipboardOption?.pasteOption === "onlyFormat") {
43594
- this.dispatch("UPDATE_CELL", {
43595
- ...target,
43596
- style: origin.cell.style,
43597
- format: origin.evaluatedCell.format,
43598
- });
43599
- return;
43600
- }
43601
- if (clipboardOption?.pasteOption === "onlyValue") {
43602
- const locale = this.getters.getLocale();
43603
- const content = formatValue(origin.evaluatedCell.value, { locale });
43604
- this.dispatch("UPDATE_CELL", { ...target, content });
43605
- return;
43606
- }
43607
- let content = origin.cell.content;
43608
- if (origin.cell.isFormula && operation === "COPY") {
43609
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula);
43610
- }
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) {
43611
43636
  this.dispatch("UPDATE_CELL", {
43612
43637
  ...target,
43613
43638
  content,
43614
- style: origin.cell.style || null,
43615
- format: origin.cell.format,
43639
+ style: origin.cell?.style || null,
43640
+ format: origin.cell?.format,
43616
43641
  });
43617
43642
  }
43618
43643
  else if (targetCell) {
43619
- if (clipboardOption?.pasteOption === "onlyValue") {
43620
- this.dispatch("UPDATE_CELL", { ...target, content: "" });
43621
- }
43622
- else if (clipboardOption?.pasteOption === "onlyFormat") {
43623
- this.dispatch("UPDATE_CELL", { ...target, style: null, format: "" });
43624
- }
43625
- else {
43626
- this.dispatch("CLEAR_CELL", target);
43627
- }
43644
+ this.dispatch("CLEAR_CELL", target);
43628
43645
  }
43629
43646
  }
43630
43647
  /**
@@ -43686,22 +43703,28 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43686
43703
  return (this.cells
43687
43704
  .map((cells) => {
43688
43705
  return cells
43689
- .map((c) => this.getters.shouldShowFormulas() && c.cell?.isFormula
43706
+ .map((c) => this.getters.shouldShowFormulas() && c?.cell?.isFormula
43690
43707
  ? c.cell?.content || ""
43691
- : c.evaluatedCell?.formattedValue || "")
43708
+ : c?.evaluatedCell?.formattedValue || "")
43692
43709
  .join("\t");
43693
43710
  })
43694
43711
  .join("\n") || "\t");
43695
43712
  }
43696
43713
  getHTMLContent() {
43697
43714
  if (this.cells.length === 1 && this.cells[0].length === 1) {
43715
+ if (!this.cells[0][0]) {
43716
+ return "";
43717
+ }
43698
43718
  return this.getters.getCellText(this.cells[0][0].position);
43699
43719
  }
43700
43720
  let htmlTable = '<table border="1" style="border-collapse:collapse">';
43701
43721
  for (const row of this.cells) {
43702
43722
  htmlTable += "<tr>";
43703
43723
  for (const cell of row) {
43704
- const cssStyle = cssPropertiesToCss(cellStyleToCss(cell.style));
43724
+ if (!cell) {
43725
+ continue;
43726
+ }
43727
+ const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
43705
43728
  const cellText = this.getters.getCellText(cell.position);
43706
43729
  htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
43707
43730
  }
@@ -46831,11 +46854,14 @@ class ClipboardPlugin extends UIPlugin {
46831
46854
  }
46832
46855
  const pasteOption = cmd.pasteOption || (this.paintFormatStatus !== "inactive" ? "onlyFormat" : undefined);
46833
46856
  this.state.paste(cmd.target, { pasteOption, shouldPasteCF: true, selectTarget: true });
46857
+ if (this.state.operation === "CUT") {
46858
+ this.state = undefined;
46859
+ }
46834
46860
  this.lastPasteState = this.state;
46835
46861
  if (this.paintFormatStatus === "oneOff") {
46836
46862
  this.paintFormatStatus = "inactive";
46837
- this.status = "invisible";
46838
46863
  }
46864
+ this.status = "invisible";
46839
46865
  break;
46840
46866
  case "COPY_PASTE_CELLS_ABOVE":
46841
46867
  {
@@ -55559,19 +55585,22 @@ class Model extends EventBus {
55559
55585
  * Check if the given command is allowed by all the plugins and the history.
55560
55586
  */
55561
55587
  checkDispatchAllowed(command) {
55562
- if (isCoreCommand(command)) {
55563
- 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());
55564
55593
  }
55565
- return this.checkDispatchAllowedLocalCommand(command);
55594
+ return DispatchResult.Success;
55566
55595
  }
55567
55596
  checkDispatchAllowedCoreCommand(command) {
55568
55597
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
55569
55598
  results.push(this.range.allowDispatch(command));
55570
- return new DispatchResult(results.flat());
55599
+ return results;
55571
55600
  }
55572
55601
  checkDispatchAllowedLocalCommand(command) {
55573
55602
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
55574
- return new DispatchResult(results.flat());
55603
+ return results;
55575
55604
  }
55576
55605
  finalize() {
55577
55606
  this.status = 3 /* Status.Finalizing */;
@@ -55883,6 +55912,6 @@ const constants = {
55883
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 };
55884
55913
 
55885
55914
 
55886
- __info__.version = "17.0.3";
55887
- __info__.date = "2023-11-16T11:59:49.524Z";
55888
- __info__.hash = "8bd0fb2";
55915
+ __info__.version = "17.0.4";
55916
+ __info__.date = "2023-11-24T13:13:42.575Z";
55917
+ __info__.hash = "7346d0a";