@odoo/o-spreadsheet 16.2.21 → 16.2.23

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.
@@ -607,13 +607,6 @@ function getYearFrac(startDate, endDate, _dayCountConvention) {
607
607
  }
608
608
 
609
609
  //------------------------------------------------------------------------------
610
- /**
611
- * Stringify an object, like JSON.stringify, except that the first level of keys
612
- * is ordered.
613
- */
614
- function stringify(obj) {
615
- return JSON.stringify(obj, Object.keys(obj).sort());
616
- }
617
610
  /**
618
611
  * Remove quotes from a quoted string
619
612
  * ```js
@@ -913,7 +906,7 @@ function isObjectEmptyRecursive(argument) {
913
906
  */
914
907
  function getItemId(item, itemsDic) {
915
908
  for (let [key, value] of Object.entries(itemsDic)) {
916
- if (stringify(value) === stringify(item)) {
909
+ if (deepEquals(value, item)) {
917
910
  return parseInt(key, 10);
918
911
  }
919
912
  }
@@ -979,6 +972,22 @@ function lazy(fn) {
979
972
  lazyValue.map = (callback) => lazy(() => callback(lazyValue()));
980
973
  return lazyValue;
981
974
  }
975
+ /**
976
+ * Creates a version of the function that's memoized on the value of its first
977
+ * argument, if any.
978
+ */
979
+ function memoize(func) {
980
+ const cache = new Map();
981
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
982
+ return {
983
+ [funcName](...args) {
984
+ if (!cache.has(args[0])) {
985
+ cache.set(args[0], func(...args));
986
+ }
987
+ return cache.get(args[0]);
988
+ },
989
+ }[funcName];
990
+ }
982
991
  /**
983
992
  * Find the next defined value after the given index in an array of strings. If there is no defined value
984
993
  * after the index, return the closest defined value before the index. Return an empty string if no
@@ -3576,12 +3585,12 @@ function toString(value) {
3576
3585
  }
3577
3586
  }
3578
3587
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
3579
- function normalizeString(str) {
3588
+ const normalizeString = memoize(function normalizeString(str) {
3580
3589
  return str
3581
3590
  .toLowerCase()
3582
3591
  .normalize("NFD")
3583
3592
  .replace(/[\u0300-\u036f]/g, "");
3584
- }
3593
+ });
3585
3594
  /**
3586
3595
  * Normalize a value.
3587
3596
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -4305,6 +4314,7 @@ exports.CommandResult = void 0;
4305
4314
  CommandResult[CommandResult["ChartDoesNotExist"] = 85] = "ChartDoesNotExist";
4306
4315
  CommandResult[CommandResult["InvalidHeaderIndex"] = 86] = "InvalidHeaderIndex";
4307
4316
  CommandResult[CommandResult["InvalidQuantity"] = 87] = "InvalidQuantity";
4317
+ CommandResult[CommandResult["NoChanges"] = 88] = "NoChanges";
4308
4318
  })(exports.CommandResult || (exports.CommandResult = {}));
4309
4319
 
4310
4320
  function isMatrix(x) {
@@ -18736,7 +18746,7 @@ css /* scss */ `
18736
18746
  }
18737
18747
  .o-input-count {
18738
18748
  width: fit-content;
18739
- padding: 4 0 4 4;
18749
+ padding: 4px 0 4px 4px;
18740
18750
  }
18741
18751
  }
18742
18752
  }
@@ -27520,6 +27530,14 @@ class BordersPlugin extends CorePlugin {
27520
27530
  // ---------------------------------------------------------------------------
27521
27531
  // Command Handling
27522
27532
  // ---------------------------------------------------------------------------
27533
+ allowDispatch(cmd) {
27534
+ switch (cmd.type) {
27535
+ case "SET_BORDER":
27536
+ return this.checkBordersUnchanged(cmd);
27537
+ default:
27538
+ return 0 /* CommandResult.Success */;
27539
+ }
27540
+ }
27523
27541
  handle(cmd) {
27524
27542
  switch (cmd.type) {
27525
27543
  case "ADD_MERGE":
@@ -27915,6 +27933,15 @@ class BordersPlugin extends CorePlugin {
27915
27933
  this.setBorders(sheetId, [{ ...zone, left: right }], "right");
27916
27934
  }
27917
27935
  }
27936
+ checkBordersUnchanged(cmd) {
27937
+ var _a, _b, _c, _d;
27938
+ const currentBorder = this.getCellBorder(cmd);
27939
+ const areAllNewBordersUndefined = !((_a = cmd.border) === null || _a === void 0 ? void 0 : _a.bottom) && !((_b = cmd.border) === null || _b === void 0 ? void 0 : _b.left) && !((_c = cmd.border) === null || _c === void 0 ? void 0 : _c.right) && !((_d = cmd.border) === null || _d === void 0 ? void 0 : _d.top);
27940
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
27941
+ return 88 /* CommandResult.NoChanges */;
27942
+ }
27943
+ return 0 /* CommandResult.Success */;
27944
+ }
27918
27945
  // ---------------------------------------------------------------------------
27919
27946
  // Import/Export
27920
27947
  // ---------------------------------------------------------------------------
@@ -27950,7 +27977,7 @@ class BordersPlugin extends CorePlugin {
27950
27977
  */
27951
27978
  function getBorderId(border) {
27952
27979
  for (let [key, value] of Object.entries(borders)) {
27953
- if (stringify(value) === stringify(border)) {
27980
+ if (deepEquals(value, border)) {
27954
27981
  return parseInt(key, 10);
27955
27982
  }
27956
27983
  }
@@ -28017,8 +28044,9 @@ class CellPlugin extends CorePlugin {
28017
28044
  allowDispatch(cmd) {
28018
28045
  switch (cmd.type) {
28019
28046
  case "UPDATE_CELL":
28047
+ return this.checkCellOutOfSheet(cmd);
28020
28048
  case "CLEAR_CELL":
28021
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
28049
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28022
28050
  default:
28023
28051
  return 0 /* CommandResult.Success */;
28024
28052
  }
@@ -28177,14 +28205,9 @@ class CellPlugin extends CorePlugin {
28177
28205
  */
28178
28206
  getCellById(cellId) {
28179
28207
  // this must be as fast as possible
28180
- for (const sheetId in this.cells) {
28181
- const sheet = this.cells[sheetId];
28182
- const cell = sheet[cellId];
28183
- if (cell) {
28184
- return cell;
28185
- }
28186
- }
28187
- return undefined;
28208
+ const position = this.getters.getCellPosition(cellId);
28209
+ const sheet = this.cells[position.sheetId];
28210
+ return sheet[cellId];
28188
28211
  }
28189
28212
  /*
28190
28213
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -28422,13 +28445,23 @@ class CellPlugin extends CorePlugin {
28422
28445
  dependencies: [],
28423
28446
  };
28424
28447
  }
28425
- checkCellOutOfSheet(sheetId, col, row) {
28448
+ checkCellOutOfSheet(cmd) {
28449
+ const { sheetId, col, row } = cmd;
28426
28450
  const sheet = this.getters.tryGetSheet(sheetId);
28427
28451
  if (!sheet)
28428
28452
  return 27 /* CommandResult.InvalidSheetId */;
28429
28453
  const sheetZone = this.getters.getSheetZone(sheetId);
28430
28454
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
28431
28455
  }
28456
+ checkUselessClearCell(cmd) {
28457
+ const cell = this.getters.getCell(cmd);
28458
+ if (!cell)
28459
+ return 88 /* CommandResult.NoChanges */;
28460
+ if (!cell.content && !cell.style && !cell.format) {
28461
+ return 88 /* CommandResult.NoChanges */;
28462
+ }
28463
+ return 0 /* CommandResult.Success */;
28464
+ }
28432
28465
  }
28433
28466
  CellPlugin.getters = [
28434
28467
  "zoneToXC",
@@ -29331,6 +29364,13 @@ class FiltersPlugin extends CorePlugin {
29331
29364
  }
29332
29365
  onDeleteColumnsRows(cmd) {
29333
29366
  for (const table of this.getFilterTables(cmd.sheetId)) {
29367
+ // Remove the filter tables whose data filter headers are in the removed rows.
29368
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
29369
+ const tables = { ...this.tables[cmd.sheetId] };
29370
+ delete tables[table.id];
29371
+ this.history.update("tables", cmd.sheetId, tables);
29372
+ continue;
29373
+ }
29334
29374
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
29335
29375
  if (!zone) {
29336
29376
  const tables = { ...this.tables[cmd.sheetId] };
@@ -31487,10 +31527,13 @@ class SheetPlugin extends CorePlugin {
31487
31527
  // begin with the end.
31488
31528
  rows.sort((a, b) => b - a);
31489
31529
  for (let group of groupConsecutive(rows)) {
31530
+ // indexes are sorted in the descending order
31531
+ const from = group[group.length - 1];
31532
+ const to = group[0];
31490
31533
  // Move the cells.
31491
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
31492
- // Effectively delete the element and recompute the left-right/top-bottom.
31493
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
31534
+ this.moveCellOnRowsDeletion(sheet, from, to);
31535
+ // Effectively delete the rows
31536
+ this.updateRowsStructureOnDeletion(sheet, from, to);
31494
31537
  }
31495
31538
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
31496
31539
  if (count) {
@@ -31512,8 +31555,6 @@ class SheetPlugin extends CorePlugin {
31512
31555
  this.addEmptyRows(sheet, quantity);
31513
31556
  // Move the cells.
31514
31557
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
31515
- // Recompute the left-right/top-bottom.
31516
- this.updateRowsStructureOnAddition(sheet, row, quantity);
31517
31558
  if (index < sheet.panes.ySplit) {
31518
31559
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
31519
31560
  }
@@ -31614,35 +31655,20 @@ class SheetPlugin extends CorePlugin {
31614
31655
  }
31615
31656
  }
31616
31657
  }
31617
- updateRowsStructureOnDeletion(index, sheet) {
31658
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
31618
31659
  const rows = [];
31619
- const cellsQueue = sheet.rows.map((row) => row.cells);
31660
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
31620
31661
  for (let i in sheet.rows) {
31621
- if (Number(i) === index) {
31662
+ const row = Number(i);
31663
+ if (row >= deleteFromRow && row <= deleteToRow) {
31622
31664
  continue;
31623
31665
  }
31624
31666
  rows.push({
31625
- cells: cellsQueue.shift(),
31667
+ cells: cellsQueue.pop(),
31626
31668
  });
31627
31669
  }
31628
31670
  this.history.update("sheets", sheet.id, "rows", rows);
31629
31671
  }
31630
- /**
31631
- * Update the rows of the sheet after an addition:
31632
- * - Rename the rows
31633
- *
31634
- * @param sheet Sheet on which the deletion occurs
31635
- * @param addedRow Index of the added row
31636
- * @param rowsToAdd Number of the rows to add
31637
- */
31638
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
31639
- const rows = [];
31640
- const cellsQueue = sheet.rows.map((row) => row.cells);
31641
- sheet.rows.forEach(() => rows.push({
31642
- cells: cellsQueue.shift(),
31643
- }));
31644
- this.history.update("sheets", sheet.id, "rows", rows);
31645
- }
31646
31672
  /**
31647
31673
  * Add empty rows at the end of the rows
31648
31674
  *
@@ -32126,6 +32152,7 @@ class EvaluationPlugin extends UIPlugin {
32126
32152
  }
32127
32153
  return evaluatedCell;
32128
32154
  };
32155
+ const rangeCache = {};
32129
32156
  /**
32130
32157
  * Return the values of the cell(s) used in reference, but always in the format of a range even
32131
32158
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -32142,21 +32169,26 @@ class EvaluationPlugin extends UIPlugin {
32142
32169
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
32143
32170
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
32144
32171
  const sheetZone = getters.getSheetZone(sheetId);
32145
- const result = [];
32146
32172
  const zone = intersection(range.zone, sheetZone);
32147
32173
  if (!zone) {
32148
- result.push([]);
32149
- return result;
32174
+ return [[]];
32175
+ }
32176
+ const { top, left, bottom, right } = zone;
32177
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
32178
+ if (cacheKey in rangeCache) {
32179
+ return rangeCache[cacheKey];
32150
32180
  }
32181
+ const result = new Array(right - left + 1);
32151
32182
  // Performance issue: nested loop is faster than a map here
32152
- for (let col = zone.left; col <= zone.right; col++) {
32153
- const rowValues = [];
32154
- for (let row = zone.top; row <= zone.bottom; row++) {
32183
+ for (let col = left; col <= right; col++) {
32184
+ const rowValues = new Array(bottom - top + 1);
32185
+ for (let row = top; row <= bottom; row++) {
32155
32186
  const cell = evalContext.getters.getCell({ sheetId: range.sheetId, col, row });
32156
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
32187
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
32157
32188
  }
32158
- result.push(rowValues);
32189
+ result[col - left] = rowValues;
32159
32190
  }
32191
+ rangeCache[cacheKey] = result;
32160
32192
  return result;
32161
32193
  }
32162
32194
  /**
@@ -32703,6 +32735,8 @@ class FilterEvaluationPlugin extends UIPlugin {
32703
32735
  case "EVALUATE_CELLS":
32704
32736
  case "ACTIVATE_SHEET":
32705
32737
  case "REMOVE_FILTER_TABLE":
32738
+ case "ADD_COLUMNS_ROWS":
32739
+ case "REMOVE_COLUMNS_ROWS":
32706
32740
  this.isEvaluationDirty = true;
32707
32741
  break;
32708
32742
  case "START":
@@ -42681,9 +42715,9 @@ function revertChanges(revisions) {
42681
42715
  * Apply the changes of the given HistoryChange to the state
42682
42716
  */
42683
42717
  function applyChange(change, target) {
42684
- let val = change.root;
42685
- let key = change.path[change.path.length - 1];
42686
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
42718
+ let val = change.path[0];
42719
+ const key = change.path[change.path.length - 1];
42720
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
42687
42721
  const p = change.path[pathIndex];
42688
42722
  if (val[p] === undefined) {
42689
42723
  const nextPath = change.path[pathIndex + 1];
@@ -43440,13 +43474,14 @@ class StateObserver {
43440
43474
  }
43441
43475
  addChange(...args) {
43442
43476
  const val = args.pop();
43443
- const [root, ...path] = args;
43477
+ const root = args[0];
43444
43478
  let value = root;
43445
- let key = path[path.length - 1];
43446
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
43447
- const p = path[pathIndex];
43479
+ let key = args[args.length - 1];
43480
+ const pathLength = args.length - 2;
43481
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
43482
+ const p = args[pathIndex];
43448
43483
  if (value[p] === undefined) {
43449
- const nextPath = path[pathIndex + 1];
43484
+ const nextPath = args[pathIndex + 1];
43450
43485
  value[p] = createEmptyStructure(nextPath);
43451
43486
  }
43452
43487
  value = value[p];
@@ -43455,8 +43490,7 @@ class StateObserver {
43455
43490
  return;
43456
43491
  }
43457
43492
  this.changes.push({
43458
- root,
43459
- path,
43493
+ path: args,
43460
43494
  before: value[key],
43461
43495
  after: val,
43462
43496
  });
@@ -45308,7 +45342,10 @@ class Model extends EventBus {
45308
45342
  */
45309
45343
  checkDispatchAllowed(command) {
45310
45344
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
45311
- return new DispatchResult(results.flat());
45345
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
45346
+ return new DispatchResult(results.flat());
45347
+ }
45348
+ return DispatchResult.Success;
45312
45349
  }
45313
45350
  finalize() {
45314
45351
  this.status = 3 /* Status.Finalizing */;
@@ -45535,6 +45572,6 @@ exports.setTranslationMethod = setTranslationMethod;
45535
45572
  exports.tokenize = tokenize;
45536
45573
 
45537
45574
 
45538
- __info__.version = '16.2.21';
45539
- __info__.date = '2023-11-02T09:31:50.545Z';
45540
- __info__.hash = 'cc30e85';
45575
+ __info__.version = '16.2.23';
45576
+ __info__.date = '2023-11-24T13:11:02.651Z';
45577
+ __info__.hash = '3c723d3';
@@ -1080,7 +1080,7 @@ declare class StateObserver {
1080
1080
  commands: CoreCommand[];
1081
1081
  };
1082
1082
  addCommand(command: CoreCommand): void;
1083
- addChange(...args: any[]): void;
1083
+ addChange(...args: [...HistoryChange["path"], any]): void;
1084
1084
  }
1085
1085
 
1086
1086
  interface Validator {
@@ -2140,7 +2140,8 @@ declare const enum CommandResult {
2140
2140
  DuplicatedChartId = 84,
2141
2141
  ChartDoesNotExist = 85,
2142
2142
  InvalidHeaderIndex = 86,
2143
- InvalidQuantity = 87
2143
+ InvalidQuantity = 87,
2144
+ NoChanges = 88
2144
2145
  }
2145
2146
  interface CommandHandler<T> {
2146
2147
  allowDispatch(command: T): CommandResult | CommandResult[];
@@ -2933,7 +2934,8 @@ interface BordersPluginState {
2933
2934
  declare class BordersPlugin extends CorePlugin<BordersPluginState> implements BordersPluginState {
2934
2935
  static getters: readonly ["getCellBorder"];
2935
2936
  readonly borders: BordersPluginState["borders"];
2936
- handle(cmd: Command): void;
2937
+ allowDispatch(cmd: CoreCommand): CommandResult.Success | CommandResult.NoChanges;
2938
+ handle(cmd: CoreCommand): void;
2937
2939
  /**
2938
2940
  * Move borders according to the inserted columns.
2939
2941
  * Ensure borders continuity.
@@ -3030,6 +3032,7 @@ declare class BordersPlugin extends CorePlugin<BordersPluginState> implements Bo
3030
3032
  * Compute the borders to add to the given zone merged.
3031
3033
  */
3032
3034
  private addBordersToMerge;
3035
+ private checkBordersUnchanged;
3033
3036
  import(data: WorkbookData): void;
3034
3037
  export(data: WorkbookData): void;
3035
3038
  exportForExcel(data: ExcelWorkbookData): void;
@@ -3054,7 +3057,7 @@ declare class CellPlugin extends CorePlugin<CoreState> implements CoreState {
3054
3057
  };
3055
3058
  };
3056
3059
  adaptRanges(applyChange: ApplyRangeChange, sheetId?: UID): void;
3057
- allowDispatch(cmd: CoreCommand): CommandResult;
3060
+ allowDispatch(cmd: CoreCommand): CommandResult | CommandResult[];
3058
3061
  handle(cmd: CoreCommand): void;
3059
3062
  /**
3060
3063
  * Set a format to all the cells in a zone
@@ -3127,6 +3130,7 @@ declare class CellPlugin extends CorePlugin<CoreState> implements CoreState {
3127
3130
  private createFormulaCellWithDependencies;
3128
3131
  private createErrorFormula;
3129
3132
  private checkCellOutOfSheet;
3133
+ private checkUselessClearCell;
3130
3134
  }
3131
3135
 
3132
3136
  /**
@@ -3635,15 +3639,6 @@ declare class SheetPlugin extends CorePlugin<SheetState$1> implements SheetState
3635
3639
  */
3636
3640
  private moveCellOnRowsDeletion;
3637
3641
  private updateRowsStructureOnDeletion;
3638
- /**
3639
- * Update the rows of the sheet after an addition:
3640
- * - Rename the rows
3641
- *
3642
- * @param sheet Sheet on which the deletion occurs
3643
- * @param addedRow Index of the added row
3644
- * @param rowsToAdd Number of the rows to add
3645
- */
3646
- private updateRowsStructureOnAddition;
3647
3642
  /**
3648
3643
  * Add empty rows at the end of the rows
3649
3644
  *
@@ -4699,8 +4694,7 @@ type Getters = {
4699
4694
  } & LocalHistoryGetters & CoreGetters & SessionGetters & AutofillGetters & AutomaticSumGetters & ClipboardGetters & EditionGetters & EvaluationGetters & EvaluationChartGetters & EvaluationConditionalFormatGetters & FindAndReplaceGetters & HighlightGetters & CustomColorsGetters & RendererGetters & SelectionGetters & SelectionInputGetters & SelectionMultiUserGetters & SortGetters & UIOptionsGetters & SheetUIGetters & ViewportGetters & CellPopoverPluginGetters & FilterEvaluationGetters & HeaderVisibilityIUIGetters;
4700
4695
 
4701
4696
  interface HistoryChange {
4702
- root: any;
4703
- path: (string | number)[];
4697
+ path: [any, ...(number | string)[]];
4704
4698
  before: any;
4705
4699
  after: any;
4706
4700
  }