@odoo/o-spreadsheet 16.3.16 → 16.3.18

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.
@@ -1019,6 +1019,22 @@
1019
1019
  lazyValue.map = (callback) => lazy(() => callback(lazyValue()));
1020
1020
  return lazyValue;
1021
1021
  }
1022
+ /**
1023
+ * Creates a version of the function that's memoized on the value of its first
1024
+ * argument, if any.
1025
+ */
1026
+ function memoize(func) {
1027
+ const cache = new Map();
1028
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1029
+ return {
1030
+ [funcName](...args) {
1031
+ if (!cache.has(args[0])) {
1032
+ cache.set(args[0], func(...args));
1033
+ }
1034
+ return cache.get(args[0]);
1035
+ },
1036
+ }[funcName];
1037
+ }
1022
1038
  /**
1023
1039
  * Find the next defined value after the given index in an array of strings. If there is no defined value
1024
1040
  * after the index, return the closest defined value before the index. Return an empty string if no
@@ -3691,12 +3707,12 @@
3691
3707
  }
3692
3708
  }
3693
3709
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3694
- function normalizeString(str) {
3710
+ const normalizeString = memoize(function normalizeString(str) {
3695
3711
  return str
3696
3712
  .toLowerCase()
3697
3713
  .normalize("NFD")
3698
3714
  .replace(/[\u0300-\u036f]/g, "");
3699
- }
3715
+ });
3700
3716
  /**
3701
3717
  * Normalize a value.
3702
3718
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -4427,6 +4443,7 @@
4427
4443
  CommandResult[CommandResult["SplitWillOverwriteContent"] = 90] = "SplitWillOverwriteContent";
4428
4444
  CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 91] = "NoSplitSeparatorInSelection";
4429
4445
  CommandResult[CommandResult["NoActiveSheet"] = 92] = "NoActiveSheet";
4446
+ CommandResult[CommandResult["NoChanges"] = 93] = "NoChanges";
4430
4447
  })(exports.CommandResult || (exports.CommandResult = {}));
4431
4448
 
4432
4449
  const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
@@ -9385,6 +9402,17 @@
9385
9402
  };
9386
9403
  const IS_ONLY_ONE_RANGE = (env) => {
9387
9404
  return env.model.getters.getSelectedZones().length === 1;
9405
+ };
9406
+ const CAN_INSERT_HEADER = (env, dimension) => {
9407
+ if (!IS_ONLY_ONE_RANGE(env)) {
9408
+ return false;
9409
+ }
9410
+ const activeHeaders = dimension === "COL" ? env.model.getters.getActiveCols() : env.model.getters.getActiveRows();
9411
+ const ortogonalActiveHeaders = dimension === "COL" ? env.model.getters.getActiveRows() : env.model.getters.getActiveCols();
9412
+ const sheetId = env.model.getters.getActiveSheetId();
9413
+ const zone = env.model.getters.getSelectedZone();
9414
+ const allSheetSelected = isEqual(zone, env.model.getters.getSheetZone(sheetId));
9415
+ return isConsecutive(activeHeaders) && (ortogonalActiveHeaders.size === 0 || allSheetSelected);
9388
9416
  };
9389
9417
 
9390
9418
  const undo = {
@@ -16319,17 +16347,13 @@
16319
16347
 
16320
16348
  const insertRow = {
16321
16349
  name: MENU_INSERT_ROWS_NAME,
16322
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16323
- IS_ONLY_ONE_RANGE(env) &&
16324
- env.model.getters.getActiveCols().size === 0,
16350
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
16325
16351
  icon: "o-spreadsheet-Icon.INSERT_ROW",
16326
16352
  };
16327
16353
  const rowInsertRowBefore = {
16328
16354
  name: ROW_INSERT_ROWS_BEFORE_NAME,
16329
16355
  execute: INSERT_ROWS_BEFORE_ACTION,
16330
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16331
- IS_ONLY_ONE_RANGE(env) &&
16332
- env.model.getters.getActiveCols().size === 0,
16356
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
16333
16357
  icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
16334
16358
  };
16335
16359
  const topBarInsertRowsBefore = {
@@ -16345,9 +16369,7 @@
16345
16369
  const rowInsertRowsAfter = {
16346
16370
  execute: INSERT_ROWS_AFTER_ACTION,
16347
16371
  name: ROW_INSERT_ROWS_AFTER_NAME,
16348
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
16349
- IS_ONLY_ONE_RANGE(env) &&
16350
- env.model.getters.getActiveCols().size === 0,
16372
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
16351
16373
  icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
16352
16374
  };
16353
16375
  const topBarInsertRowsAfter = {
@@ -16356,17 +16378,13 @@
16356
16378
  };
16357
16379
  const insertCol = {
16358
16380
  name: MENU_INSERT_COLUMNS_NAME,
16359
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16360
- IS_ONLY_ONE_RANGE(env) &&
16361
- env.model.getters.getActiveRows().size === 0,
16381
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
16362
16382
  icon: "o-spreadsheet-Icon.INSERT_COL",
16363
16383
  };
16364
16384
  const colInsertColsBefore = {
16365
16385
  name: COLUMN_INSERT_COLUMNS_BEFORE_NAME,
16366
16386
  execute: INSERT_COLUMNS_BEFORE_ACTION,
16367
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16368
- IS_ONLY_ONE_RANGE(env) &&
16369
- env.model.getters.getActiveRows().size === 0,
16387
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
16370
16388
  icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
16371
16389
  };
16372
16390
  const topBarInsertColsBefore = {
@@ -16382,9 +16400,7 @@
16382
16400
  const colInsertColsAfter = {
16383
16401
  name: COLUMN_INSERT_COLUMNS_AFTER_NAME,
16384
16402
  execute: INSERT_COLUMNS_AFTER_ACTION,
16385
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
16386
- IS_ONLY_ONE_RANGE(env) &&
16387
- env.model.getters.getActiveRows().size === 0,
16403
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
16388
16404
  icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
16389
16405
  };
16390
16406
  const topBarInsertColsAfter = {
@@ -19711,7 +19727,7 @@
19711
19727
  }
19712
19728
  .o-input-count {
19713
19729
  width: fit-content;
19714
- padding: 4 0 4 4;
19730
+ padding: 4px 0 4px 4px;
19715
19731
  }
19716
19732
  }
19717
19733
  }
@@ -28993,6 +29009,14 @@
28993
29009
  // ---------------------------------------------------------------------------
28994
29010
  // Command Handling
28995
29011
  // ---------------------------------------------------------------------------
29012
+ allowDispatch(cmd) {
29013
+ switch (cmd.type) {
29014
+ case "SET_BORDER":
29015
+ return this.checkBordersUnchanged(cmd);
29016
+ default:
29017
+ return 0 /* CommandResult.Success */;
29018
+ }
29019
+ }
28996
29020
  handle(cmd) {
28997
29021
  switch (cmd.type) {
28998
29022
  case "ADD_MERGE":
@@ -29412,6 +29436,14 @@
29412
29436
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
29413
29437
  }
29414
29438
  }
29439
+ checkBordersUnchanged(cmd) {
29440
+ const currentBorder = this.getCellBorder(cmd);
29441
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
29442
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
29443
+ return 93 /* CommandResult.NoChanges */;
29444
+ }
29445
+ return 0 /* CommandResult.Success */;
29446
+ }
29415
29447
  // ---------------------------------------------------------------------------
29416
29448
  // Import/Export
29417
29449
  // ---------------------------------------------------------------------------
@@ -29519,8 +29551,9 @@
29519
29551
  allowDispatch(cmd) {
29520
29552
  switch (cmd.type) {
29521
29553
  case "UPDATE_CELL":
29554
+ return this.checkCellOutOfSheet(cmd);
29522
29555
  case "CLEAR_CELL":
29523
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
29556
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
29524
29557
  default:
29525
29558
  return 0 /* CommandResult.Success */;
29526
29559
  }
@@ -29679,14 +29712,9 @@
29679
29712
  */
29680
29713
  getCellById(cellId) {
29681
29714
  // this must be as fast as possible
29682
- for (const sheetId in this.cells) {
29683
- const sheet = this.cells[sheetId];
29684
- const cell = sheet[cellId];
29685
- if (cell) {
29686
- return cell;
29687
- }
29688
- }
29689
- return undefined;
29715
+ const position = this.getters.getCellPosition(cellId);
29716
+ const sheet = this.cells[position.sheetId];
29717
+ return sheet[cellId];
29690
29718
  }
29691
29719
  /*
29692
29720
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -29923,13 +29951,23 @@
29923
29951
  dependencies: [],
29924
29952
  };
29925
29953
  }
29926
- checkCellOutOfSheet(sheetId, col, row) {
29954
+ checkCellOutOfSheet(cmd) {
29955
+ const { sheetId, col, row } = cmd;
29927
29956
  const sheet = this.getters.tryGetSheet(sheetId);
29928
29957
  if (!sheet)
29929
29958
  return 27 /* CommandResult.InvalidSheetId */;
29930
29959
  const sheetZone = this.getters.getSheetZone(sheetId);
29931
29960
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
29932
29961
  }
29962
+ checkUselessClearCell(cmd) {
29963
+ const cell = this.getters.getCell(cmd);
29964
+ if (!cell)
29965
+ return 93 /* CommandResult.NoChanges */;
29966
+ if (!cell.content && !cell.style && !cell.format) {
29967
+ return 93 /* CommandResult.NoChanges */;
29968
+ }
29969
+ return 0 /* CommandResult.Success */;
29970
+ }
29933
29971
  }
29934
29972
 
29935
29973
  class ChartPlugin extends CorePlugin {
@@ -30845,6 +30883,13 @@
30845
30883
  }
30846
30884
  onDeleteColumnsRows(cmd) {
30847
30885
  for (const table of this.getFilterTables(cmd.sheetId)) {
30886
+ // Remove the filter tables whose data filter headers are in the removed rows.
30887
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
30888
+ const tables = { ...this.tables[cmd.sheetId] };
30889
+ delete tables[table.id];
30890
+ this.history.update("tables", cmd.sheetId, tables);
30891
+ continue;
30892
+ }
30848
30893
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
30849
30894
  if (!zone) {
30850
30895
  const tables = { ...this.tables[cmd.sheetId] };
@@ -33025,10 +33070,13 @@
33025
33070
  // begin with the end.
33026
33071
  rows.sort((a, b) => b - a);
33027
33072
  for (let group of groupConsecutive(rows)) {
33073
+ // indexes are sorted in the descending order
33074
+ const from = group[group.length - 1];
33075
+ const to = group[0];
33028
33076
  // Move the cells.
33029
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
33030
- // Effectively delete the element and recompute the left-right/top-bottom.
33031
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
33077
+ this.moveCellOnRowsDeletion(sheet, from, to);
33078
+ // Effectively delete the rows
33079
+ this.updateRowsStructureOnDeletion(sheet, from, to);
33032
33080
  }
33033
33081
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
33034
33082
  if (count) {
@@ -33050,8 +33098,6 @@
33050
33098
  this.addEmptyRows(sheet, quantity);
33051
33099
  // Move the cells.
33052
33100
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
33053
- // Recompute the left-right/top-bottom.
33054
- this.updateRowsStructureOnAddition(sheet, row, quantity);
33055
33101
  if (index < sheet.panes.ySplit) {
33056
33102
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
33057
33103
  }
@@ -33152,35 +33198,20 @@
33152
33198
  }
33153
33199
  }
33154
33200
  }
33155
- updateRowsStructureOnDeletion(index, sheet) {
33201
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
33156
33202
  const rows = [];
33157
- const cellsQueue = sheet.rows.map((row) => row.cells);
33203
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
33158
33204
  for (let i in sheet.rows) {
33159
- if (Number(i) === index) {
33205
+ const row = Number(i);
33206
+ if (row >= deleteFromRow && row <= deleteToRow) {
33160
33207
  continue;
33161
33208
  }
33162
33209
  rows.push({
33163
- cells: cellsQueue.shift(),
33210
+ cells: cellsQueue.pop(),
33164
33211
  });
33165
33212
  }
33166
33213
  this.history.update("sheets", sheet.id, "rows", rows);
33167
33214
  }
33168
- /**
33169
- * Update the rows of the sheet after an addition:
33170
- * - Rename the rows
33171
- *
33172
- * @param sheet Sheet on which the deletion occurs
33173
- * @param addedRow Index of the added row
33174
- * @param rowsToAdd Number of the rows to add
33175
- */
33176
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
33177
- const rows = [];
33178
- const cellsQueue = sheet.rows.map((row) => row.cells);
33179
- sheet.rows.forEach(() => rows.push({
33180
- cells: cellsQueue.shift(),
33181
- }));
33182
- this.history.update("sheets", sheet.id, "rows", rows);
33183
- }
33184
33215
  /**
33185
33216
  * Add empty rows at the end of the rows
33186
33217
  *
@@ -33631,6 +33662,7 @@
33631
33662
  }
33632
33663
  return evaluatedCell;
33633
33664
  };
33665
+ const rangeCache = {};
33634
33666
  /**
33635
33667
  * Return the values of the cell(s) used in reference, but always in the format of a range even
33636
33668
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -33647,21 +33679,26 @@
33647
33679
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
33648
33680
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
33649
33681
  const sheetZone = getters.getSheetZone(sheetId);
33650
- const result = [];
33651
33682
  const zone = intersection(range.zone, sheetZone);
33652
33683
  if (!zone) {
33653
- result.push([]);
33654
- return result;
33684
+ return [[]];
33685
+ }
33686
+ const { top, left, bottom, right } = zone;
33687
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
33688
+ if (cacheKey in rangeCache) {
33689
+ return rangeCache[cacheKey];
33655
33690
  }
33691
+ const result = new Array(right - left + 1);
33656
33692
  // Performance issue: nested loop is faster than a map here
33657
- for (let col = zone.left; col <= zone.right; col++) {
33658
- const rowValues = [];
33659
- for (let row = zone.top; row <= zone.bottom; row++) {
33693
+ for (let col = left; col <= right; col++) {
33694
+ const rowValues = new Array(bottom - top + 1);
33695
+ for (let row = top; row <= bottom; row++) {
33660
33696
  const cell = evalContext.getters.getCell({ sheetId: range.sheetId, col, row });
33661
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
33697
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
33662
33698
  }
33663
- result.push(rowValues);
33699
+ result[col - left] = rowValues;
33664
33700
  }
33701
+ rangeCache[cacheKey] = result;
33665
33702
  return result;
33666
33703
  }
33667
33704
  /**
@@ -34193,6 +34230,8 @@
34193
34230
  case "EVALUATE_CELLS":
34194
34231
  case "ACTIVATE_SHEET":
34195
34232
  case "REMOVE_FILTER_TABLE":
34233
+ case "ADD_COLUMNS_ROWS":
34234
+ case "REMOVE_COLUMNS_ROWS":
34196
34235
  this.isEvaluationDirty = true;
34197
34236
  break;
34198
34237
  case "START":
@@ -44931,9 +44970,9 @@
44931
44970
  * Apply the changes of the given HistoryChange to the state
44932
44971
  */
44933
44972
  function applyChange(change, target) {
44934
- let val = change.root;
44935
- let key = change.path[change.path.length - 1];
44936
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
44973
+ let val = change.path[0];
44974
+ const key = change.path[change.path.length - 1];
44975
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
44937
44976
  const p = change.path[pathIndex];
44938
44977
  if (val[p] === undefined) {
44939
44978
  const nextPath = change.path[pathIndex + 1];
@@ -45600,13 +45639,14 @@
45600
45639
  }
45601
45640
  addChange(...args) {
45602
45641
  const val = args.pop();
45603
- const [root, ...path] = args;
45642
+ const root = args[0];
45604
45643
  let value = root;
45605
- let key = path[path.length - 1];
45606
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
45607
- const p = path[pathIndex];
45644
+ let key = args[args.length - 1];
45645
+ const pathLength = args.length - 2;
45646
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
45647
+ const p = args[pathIndex];
45608
45648
  if (value[p] === undefined) {
45609
- const nextPath = path[pathIndex + 1];
45649
+ const nextPath = args[pathIndex + 1];
45610
45650
  value[p] = createEmptyStructure(nextPath);
45611
45651
  }
45612
45652
  value = value[p];
@@ -45615,8 +45655,7 @@
45615
45655
  return;
45616
45656
  }
45617
45657
  this.changes.push({
45618
- root,
45619
- path,
45658
+ path: args,
45620
45659
  before: value[key],
45621
45660
  after: val,
45622
45661
  });
@@ -47466,19 +47505,22 @@
47466
47505
  * Check if the given command is allowed by all the plugins and the history.
47467
47506
  */
47468
47507
  checkDispatchAllowed(command) {
47469
- if (isCoreCommand(command)) {
47470
- return this.checkDispatchAllowedCoreCommand(command);
47508
+ const results = isCoreCommand(command)
47509
+ ? this.checkDispatchAllowedCoreCommand(command)
47510
+ : this.checkDispatchAllowedLocalCommand(command);
47511
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
47512
+ return new DispatchResult(results.flat());
47471
47513
  }
47472
- return this.checkDispatchAllowedLocalCommand(command);
47514
+ return DispatchResult.Success;
47473
47515
  }
47474
47516
  checkDispatchAllowedCoreCommand(command) {
47475
47517
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
47476
47518
  results.push(this.range.allowDispatch(command));
47477
- return new DispatchResult(results.flat());
47519
+ return results;
47478
47520
  }
47479
47521
  checkDispatchAllowedLocalCommand(command) {
47480
47522
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
47481
- return new DispatchResult(results.flat());
47523
+ return results;
47482
47524
  }
47483
47525
  finalize() {
47484
47526
  this.status = 3 /* Status.Finalizing */;
@@ -47796,9 +47838,9 @@
47796
47838
  Object.defineProperty(exports, '__esModule', { value: true });
47797
47839
 
47798
47840
 
47799
- __info__.version = '16.3.16';
47800
- __info__.date = '2023-11-02T09:32:13.671Z';
47801
- __info__.hash = '09607eb';
47841
+ __info__.version = '16.3.18';
47842
+ __info__.date = '2023-11-24T13:10:15.278Z';
47843
+ __info__.hash = '45f745e';
47802
47844
 
47803
47845
 
47804
47846
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);