@odoo/o-spreadsheet 16.3.17 → 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.
@@ -1022,6 +1022,22 @@ function lazy(fn) {
1022
1022
  lazyValue.map = (callback) => lazy(() => callback(lazyValue()));
1023
1023
  return lazyValue;
1024
1024
  }
1025
+ /**
1026
+ * Creates a version of the function that's memoized on the value of its first
1027
+ * argument, if any.
1028
+ */
1029
+ function memoize(func) {
1030
+ const cache = new Map();
1031
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1032
+ return {
1033
+ [funcName](...args) {
1034
+ if (!cache.has(args[0])) {
1035
+ cache.set(args[0], func(...args));
1036
+ }
1037
+ return cache.get(args[0]);
1038
+ },
1039
+ }[funcName];
1040
+ }
1025
1041
  /**
1026
1042
  * Find the next defined value after the given index in an array of strings. If there is no defined value
1027
1043
  * after the index, return the closest defined value before the index. Return an empty string if no
@@ -3694,12 +3710,12 @@ function toString(value) {
3694
3710
  }
3695
3711
  }
3696
3712
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3697
- function normalizeString(str) {
3713
+ const normalizeString = memoize(function normalizeString(str) {
3698
3714
  return str
3699
3715
  .toLowerCase()
3700
3716
  .normalize("NFD")
3701
3717
  .replace(/[\u0300-\u036f]/g, "");
3702
- }
3718
+ });
3703
3719
  /**
3704
3720
  * Normalize a value.
3705
3721
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -4430,6 +4446,7 @@ exports.CommandResult = void 0;
4430
4446
  CommandResult[CommandResult["SplitWillOverwriteContent"] = 90] = "SplitWillOverwriteContent";
4431
4447
  CommandResult[CommandResult["NoSplitSeparatorInSelection"] = 91] = "NoSplitSeparatorInSelection";
4432
4448
  CommandResult[CommandResult["NoActiveSheet"] = 92] = "NoActiveSheet";
4449
+ CommandResult[CommandResult["NoChanges"] = 93] = "NoChanges";
4433
4450
  })(exports.CommandResult || (exports.CommandResult = {}));
4434
4451
 
4435
4452
  const borderStyles = ["thin", "medium", "thick", "dashed", "dotted"];
@@ -28995,6 +29012,14 @@ class BordersPlugin extends CorePlugin {
28995
29012
  // ---------------------------------------------------------------------------
28996
29013
  // Command Handling
28997
29014
  // ---------------------------------------------------------------------------
29015
+ allowDispatch(cmd) {
29016
+ switch (cmd.type) {
29017
+ case "SET_BORDER":
29018
+ return this.checkBordersUnchanged(cmd);
29019
+ default:
29020
+ return 0 /* CommandResult.Success */;
29021
+ }
29022
+ }
28998
29023
  handle(cmd) {
28999
29024
  switch (cmd.type) {
29000
29025
  case "ADD_MERGE":
@@ -29414,6 +29439,14 @@ class BordersPlugin extends CorePlugin {
29414
29439
  this.setBorders(sheetId, [{ ...zone, left: right }], "right", bordersTopLeft.right);
29415
29440
  }
29416
29441
  }
29442
+ checkBordersUnchanged(cmd) {
29443
+ const currentBorder = this.getCellBorder(cmd);
29444
+ const areAllNewBordersUndefined = !cmd.border?.bottom && !cmd.border?.left && !cmd.border?.right && !cmd.border?.top;
29445
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
29446
+ return 93 /* CommandResult.NoChanges */;
29447
+ }
29448
+ return 0 /* CommandResult.Success */;
29449
+ }
29417
29450
  // ---------------------------------------------------------------------------
29418
29451
  // Import/Export
29419
29452
  // ---------------------------------------------------------------------------
@@ -29521,8 +29554,9 @@ class CellPlugin extends CorePlugin {
29521
29554
  allowDispatch(cmd) {
29522
29555
  switch (cmd.type) {
29523
29556
  case "UPDATE_CELL":
29557
+ return this.checkCellOutOfSheet(cmd);
29524
29558
  case "CLEAR_CELL":
29525
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
29559
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
29526
29560
  default:
29527
29561
  return 0 /* CommandResult.Success */;
29528
29562
  }
@@ -29681,14 +29715,9 @@ class CellPlugin extends CorePlugin {
29681
29715
  */
29682
29716
  getCellById(cellId) {
29683
29717
  // this must be as fast as possible
29684
- for (const sheetId in this.cells) {
29685
- const sheet = this.cells[sheetId];
29686
- const cell = sheet[cellId];
29687
- if (cell) {
29688
- return cell;
29689
- }
29690
- }
29691
- return undefined;
29718
+ const position = this.getters.getCellPosition(cellId);
29719
+ const sheet = this.cells[position.sheetId];
29720
+ return sheet[cellId];
29692
29721
  }
29693
29722
  /*
29694
29723
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -29925,13 +29954,23 @@ class CellPlugin extends CorePlugin {
29925
29954
  dependencies: [],
29926
29955
  };
29927
29956
  }
29928
- checkCellOutOfSheet(sheetId, col, row) {
29957
+ checkCellOutOfSheet(cmd) {
29958
+ const { sheetId, col, row } = cmd;
29929
29959
  const sheet = this.getters.tryGetSheet(sheetId);
29930
29960
  if (!sheet)
29931
29961
  return 27 /* CommandResult.InvalidSheetId */;
29932
29962
  const sheetZone = this.getters.getSheetZone(sheetId);
29933
29963
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
29934
29964
  }
29965
+ checkUselessClearCell(cmd) {
29966
+ const cell = this.getters.getCell(cmd);
29967
+ if (!cell)
29968
+ return 93 /* CommandResult.NoChanges */;
29969
+ if (!cell.content && !cell.style && !cell.format) {
29970
+ return 93 /* CommandResult.NoChanges */;
29971
+ }
29972
+ return 0 /* CommandResult.Success */;
29973
+ }
29935
29974
  }
29936
29975
 
29937
29976
  class ChartPlugin extends CorePlugin {
@@ -33034,10 +33073,13 @@ class SheetPlugin extends CorePlugin {
33034
33073
  // begin with the end.
33035
33074
  rows.sort((a, b) => b - a);
33036
33075
  for (let group of groupConsecutive(rows)) {
33076
+ // indexes are sorted in the descending order
33077
+ const from = group[group.length - 1];
33078
+ const to = group[0];
33037
33079
  // Move the cells.
33038
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
33039
- // Effectively delete the element and recompute the left-right/top-bottom.
33040
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
33080
+ this.moveCellOnRowsDeletion(sheet, from, to);
33081
+ // Effectively delete the rows
33082
+ this.updateRowsStructureOnDeletion(sheet, from, to);
33041
33083
  }
33042
33084
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
33043
33085
  if (count) {
@@ -33059,8 +33101,6 @@ class SheetPlugin extends CorePlugin {
33059
33101
  this.addEmptyRows(sheet, quantity);
33060
33102
  // Move the cells.
33061
33103
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
33062
- // Recompute the left-right/top-bottom.
33063
- this.updateRowsStructureOnAddition(sheet, row, quantity);
33064
33104
  if (index < sheet.panes.ySplit) {
33065
33105
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
33066
33106
  }
@@ -33161,35 +33201,20 @@ class SheetPlugin extends CorePlugin {
33161
33201
  }
33162
33202
  }
33163
33203
  }
33164
- updateRowsStructureOnDeletion(index, sheet) {
33204
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
33165
33205
  const rows = [];
33166
- const cellsQueue = sheet.rows.map((row) => row.cells);
33206
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
33167
33207
  for (let i in sheet.rows) {
33168
- if (Number(i) === index) {
33208
+ const row = Number(i);
33209
+ if (row >= deleteFromRow && row <= deleteToRow) {
33169
33210
  continue;
33170
33211
  }
33171
33212
  rows.push({
33172
- cells: cellsQueue.shift(),
33213
+ cells: cellsQueue.pop(),
33173
33214
  });
33174
33215
  }
33175
33216
  this.history.update("sheets", sheet.id, "rows", rows);
33176
33217
  }
33177
- /**
33178
- * Update the rows of the sheet after an addition:
33179
- * - Rename the rows
33180
- *
33181
- * @param sheet Sheet on which the deletion occurs
33182
- * @param addedRow Index of the added row
33183
- * @param rowsToAdd Number of the rows to add
33184
- */
33185
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
33186
- const rows = [];
33187
- const cellsQueue = sheet.rows.map((row) => row.cells);
33188
- sheet.rows.forEach(() => rows.push({
33189
- cells: cellsQueue.shift(),
33190
- }));
33191
- this.history.update("sheets", sheet.id, "rows", rows);
33192
- }
33193
33218
  /**
33194
33219
  * Add empty rows at the end of the rows
33195
33220
  *
@@ -33640,6 +33665,7 @@ class EvaluationPlugin extends UIPlugin {
33640
33665
  }
33641
33666
  return evaluatedCell;
33642
33667
  };
33668
+ const rangeCache = {};
33643
33669
  /**
33644
33670
  * Return the values of the cell(s) used in reference, but always in the format of a range even
33645
33671
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -33656,21 +33682,26 @@ class EvaluationPlugin extends UIPlugin {
33656
33682
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
33657
33683
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
33658
33684
  const sheetZone = getters.getSheetZone(sheetId);
33659
- const result = [];
33660
33685
  const zone = intersection(range.zone, sheetZone);
33661
33686
  if (!zone) {
33662
- result.push([]);
33663
- return result;
33687
+ return [[]];
33688
+ }
33689
+ const { top, left, bottom, right } = zone;
33690
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
33691
+ if (cacheKey in rangeCache) {
33692
+ return rangeCache[cacheKey];
33664
33693
  }
33694
+ const result = new Array(right - left + 1);
33665
33695
  // Performance issue: nested loop is faster than a map here
33666
- for (let col = zone.left; col <= zone.right; col++) {
33667
- const rowValues = [];
33668
- for (let row = zone.top; row <= zone.bottom; row++) {
33696
+ for (let col = left; col <= right; col++) {
33697
+ const rowValues = new Array(bottom - top + 1);
33698
+ for (let row = top; row <= bottom; row++) {
33669
33699
  const cell = evalContext.getters.getCell({ sheetId: range.sheetId, col, row });
33670
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
33700
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
33671
33701
  }
33672
- result.push(rowValues);
33702
+ result[col - left] = rowValues;
33673
33703
  }
33704
+ rangeCache[cacheKey] = result;
33674
33705
  return result;
33675
33706
  }
33676
33707
  /**
@@ -44942,9 +44973,9 @@ function revertChanges(revisions) {
44942
44973
  * Apply the changes of the given HistoryChange to the state
44943
44974
  */
44944
44975
  function applyChange(change, target) {
44945
- let val = change.root;
44946
- let key = change.path[change.path.length - 1];
44947
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
44976
+ let val = change.path[0];
44977
+ const key = change.path[change.path.length - 1];
44978
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
44948
44979
  const p = change.path[pathIndex];
44949
44980
  if (val[p] === undefined) {
44950
44981
  const nextPath = change.path[pathIndex + 1];
@@ -45611,13 +45642,14 @@ class StateObserver {
45611
45642
  }
45612
45643
  addChange(...args) {
45613
45644
  const val = args.pop();
45614
- const [root, ...path] = args;
45645
+ const root = args[0];
45615
45646
  let value = root;
45616
- let key = path[path.length - 1];
45617
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
45618
- const p = path[pathIndex];
45647
+ let key = args[args.length - 1];
45648
+ const pathLength = args.length - 2;
45649
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
45650
+ const p = args[pathIndex];
45619
45651
  if (value[p] === undefined) {
45620
- const nextPath = path[pathIndex + 1];
45652
+ const nextPath = args[pathIndex + 1];
45621
45653
  value[p] = createEmptyStructure(nextPath);
45622
45654
  }
45623
45655
  value = value[p];
@@ -45626,8 +45658,7 @@ class StateObserver {
45626
45658
  return;
45627
45659
  }
45628
45660
  this.changes.push({
45629
- root,
45630
- path,
45661
+ path: args,
45631
45662
  before: value[key],
45632
45663
  after: val,
45633
45664
  });
@@ -47477,19 +47508,22 @@ class Model extends EventBus {
47477
47508
  * Check if the given command is allowed by all the plugins and the history.
47478
47509
  */
47479
47510
  checkDispatchAllowed(command) {
47480
- if (isCoreCommand(command)) {
47481
- return this.checkDispatchAllowedCoreCommand(command);
47511
+ const results = isCoreCommand(command)
47512
+ ? this.checkDispatchAllowedCoreCommand(command)
47513
+ : this.checkDispatchAllowedLocalCommand(command);
47514
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
47515
+ return new DispatchResult(results.flat());
47482
47516
  }
47483
- return this.checkDispatchAllowedLocalCommand(command);
47517
+ return DispatchResult.Success;
47484
47518
  }
47485
47519
  checkDispatchAllowedCoreCommand(command) {
47486
47520
  const results = this.corePlugins.map((handler) => handler.allowDispatch(command));
47487
47521
  results.push(this.range.allowDispatch(command));
47488
- return new DispatchResult(results.flat());
47522
+ return results;
47489
47523
  }
47490
47524
  checkDispatchAllowedLocalCommand(command) {
47491
47525
  const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
47492
- return new DispatchResult(results.flat());
47526
+ return results;
47493
47527
  }
47494
47528
  finalize() {
47495
47529
  this.status = 3 /* Status.Finalizing */;
@@ -47805,6 +47839,6 @@ exports.setTranslationMethod = setTranslationMethod;
47805
47839
  exports.tokenize = tokenize;
47806
47840
 
47807
47841
 
47808
- __info__.version = '16.3.17';
47809
- __info__.date = '2023-11-16T11:59:29.130Z';
47810
- __info__.hash = '4049460';
47842
+ __info__.version = '16.3.18';
47843
+ __info__.date = '2023-11-24T13:10:15.278Z';
47844
+ __info__.hash = '45f745e';
@@ -1282,7 +1282,7 @@ declare class StateObserver {
1282
1282
  commands: CoreCommand[];
1283
1283
  };
1284
1284
  addCommand(command: CoreCommand): void;
1285
- addChange(...args: any[]): void;
1285
+ addChange(...args: [...HistoryChange["path"], any]): void;
1286
1286
  }
1287
1287
 
1288
1288
  interface Validator {
@@ -2358,7 +2358,8 @@ declare const enum CommandResult {
2358
2358
  EmptySplitSeparator = 89,
2359
2359
  SplitWillOverwriteContent = 90,
2360
2360
  NoSplitSeparatorInSelection = 91,
2361
- NoActiveSheet = 92
2361
+ NoActiveSheet = 92,
2362
+ NoChanges = 93
2362
2363
  }
2363
2364
  interface CommandHandler<T> {
2364
2365
  allowDispatch(command: T): CommandResult | CommandResult[];
@@ -2960,7 +2961,8 @@ interface BordersPluginState {
2960
2961
  declare class BordersPlugin extends CorePlugin<BordersPluginState> implements BordersPluginState {
2961
2962
  static getters: readonly ["getCellBorder", "getBordersColors"];
2962
2963
  readonly borders: BordersPluginState["borders"];
2963
- handle(cmd: Command): void;
2964
+ allowDispatch(cmd: CoreCommand): CommandResult.Success | CommandResult.NoChanges;
2965
+ handle(cmd: CoreCommand): void;
2964
2966
  /**
2965
2967
  * Move borders according to the inserted columns.
2966
2968
  * Ensure borders continuity.
@@ -3058,6 +3060,7 @@ declare class BordersPlugin extends CorePlugin<BordersPluginState> implements Bo
3058
3060
  * Compute the borders to add to the given zone merged.
3059
3061
  */
3060
3062
  private addBordersToMerge;
3063
+ private checkBordersUnchanged;
3061
3064
  import(data: WorkbookData): void;
3062
3065
  export(data: WorkbookData): void;
3063
3066
  exportForExcel(data: ExcelWorkbookData): void;
@@ -3082,7 +3085,7 @@ declare class CellPlugin extends CorePlugin<CoreState> implements CoreState {
3082
3085
  };
3083
3086
  };
3084
3087
  adaptRanges(applyChange: ApplyRangeChange, sheetId?: UID): void;
3085
- allowDispatch(cmd: CoreCommand): CommandResult;
3088
+ allowDispatch(cmd: CoreCommand): CommandResult | CommandResult[];
3086
3089
  handle(cmd: CoreCommand): void;
3087
3090
  /**
3088
3091
  * Set a format to all the cells in a zone
@@ -3155,6 +3158,7 @@ declare class CellPlugin extends CorePlugin<CoreState> implements CoreState {
3155
3158
  private createFormulaCellWithDependencies;
3156
3159
  private createErrorFormula;
3157
3160
  private checkCellOutOfSheet;
3161
+ private checkUselessClearCell;
3158
3162
  }
3159
3163
 
3160
3164
  /**
@@ -3672,15 +3676,6 @@ declare class SheetPlugin extends CorePlugin<SheetState$1> implements SheetState
3672
3676
  */
3673
3677
  private moveCellOnRowsDeletion;
3674
3678
  private updateRowsStructureOnDeletion;
3675
- /**
3676
- * Update the rows of the sheet after an addition:
3677
- * - Rename the rows
3678
- *
3679
- * @param sheet Sheet on which the deletion occurs
3680
- * @param addedRow Index of the added row
3681
- * @param rowsToAdd Number of the rows to add
3682
- */
3683
- private updateRowsStructureOnAddition;
3684
3679
  /**
3685
3680
  * Add empty rows at the end of the rows
3686
3681
  *
@@ -4805,8 +4800,7 @@ type Getters = {
4805
4800
  } & LocalHistoryGetters & CoreGetters & AutofillGetters & AutomaticSumGetters & ClipboardGetters & EditionGetters & EvaluationGetters & EvaluationChartGetters & EvaluationConditionalFormatGetters & FindAndReplaceGetters & HighlightGetters & CustomColorsGetters & RendererGetters & SelectionGetters & SelectionInputGetters & CollaborativeGetters & SortGetters & UIOptionsGetters & SheetUIGetters & ViewportGetters & CellPopoverPluginGetters & FilterEvaluationGetters & HeaderVisibilityIUIGetters & SplitToColumnsGetters;
4806
4801
 
4807
4802
  interface HistoryChange {
4808
- root: any;
4809
- path: (string | number)[];
4803
+ path: [any, ...(number | string)[]];
4810
4804
  before: any;
4811
4805
  after: any;
4812
4806
  }