@odoo/o-spreadsheet 16.1.26 → 16.1.28

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.
@@ -626,13 +626,6 @@ function getYearFrac(startDate, endDate, _dayCountConvention) {
626
626
  }
627
627
 
628
628
  //------------------------------------------------------------------------------
629
- /**
630
- * Stringify an object, like JSON.stringify, except that the first level of keys
631
- * is ordered.
632
- */
633
- function stringify(obj) {
634
- return JSON.stringify(obj, Object.keys(obj).sort());
635
- }
636
629
  /**
637
630
  * Remove quotes from a quoted string
638
631
  * ```js
@@ -928,7 +921,7 @@ function isObjectEmptyRecursive(argument) {
928
921
  */
929
922
  function getItemId(item, itemsDic) {
930
923
  for (let [key, value] of Object.entries(itemsDic)) {
931
- if (stringify(value) === stringify(item)) {
924
+ if (deepEquals(value, item)) {
932
925
  return parseInt(key, 10);
933
926
  }
934
927
  }
@@ -1064,6 +1057,22 @@ function transpose2dArray(matrix) {
1064
1057
  if (!matrix.length)
1065
1058
  return matrix;
1066
1059
  return matrix[0].map((_, i) => matrix.map((row) => row[i]));
1060
+ }
1061
+ /**
1062
+ * Creates a version of the function that's memoized on the value of its first
1063
+ * argument, if any.
1064
+ */
1065
+ function memoize(func) {
1066
+ const cache = new Map();
1067
+ const funcName = func.name ? func.name + " (memoized)" : "memoized";
1068
+ return {
1069
+ [funcName](...args) {
1070
+ if (!cache.has(args[0])) {
1071
+ cache.set(args[0], func(...args));
1072
+ }
1073
+ return cache.get(args[0]);
1074
+ },
1075
+ }[funcName];
1067
1076
  }
1068
1077
 
1069
1078
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
@@ -3660,6 +3669,7 @@ exports.CommandResult = void 0;
3660
3669
  CommandResult[CommandResult["ChartDoesNotExist"] = 85] = "ChartDoesNotExist";
3661
3670
  CommandResult[CommandResult["InvalidHeaderIndex"] = 86] = "InvalidHeaderIndex";
3662
3671
  CommandResult[CommandResult["InvalidQuantity"] = 87] = "InvalidQuantity";
3672
+ CommandResult[CommandResult["NoChanges"] = 88] = "NoChanges";
3663
3673
  })(exports.CommandResult || (exports.CommandResult = {}));
3664
3674
 
3665
3675
  function isMatrix(x) {
@@ -8023,12 +8033,12 @@ function toString(value) {
8023
8033
  }
8024
8034
  }
8025
8035
  /** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
8026
- function normalizeString(str) {
8036
+ const normalizeString = memoize(function normalizeString(str) {
8027
8037
  return str
8028
8038
  .toLowerCase()
8029
8039
  .normalize("NFD")
8030
8040
  .replace(/[\u0300-\u036f]/g, "");
8031
- }
8041
+ });
8032
8042
  /**
8033
8043
  * Normalize a value.
8034
8044
  * If the cell value is a string, this will set it to lowercase and replacing accent letters with plain letters
@@ -21761,7 +21771,7 @@ css /* scss */ `
21761
21771
  }
21762
21772
  .o-input-count {
21763
21773
  width: fit-content;
21764
- padding: 4 0 4 4;
21774
+ padding: 4px 0 4px 4px;
21765
21775
  }
21766
21776
  }
21767
21777
  }
@@ -27945,6 +27955,14 @@ class BordersPlugin extends CorePlugin {
27945
27955
  // ---------------------------------------------------------------------------
27946
27956
  // Command Handling
27947
27957
  // ---------------------------------------------------------------------------
27958
+ allowDispatch(cmd) {
27959
+ switch (cmd.type) {
27960
+ case "SET_BORDER":
27961
+ return this.checkBordersUnchanged(cmd);
27962
+ default:
27963
+ return 0 /* CommandResult.Success */;
27964
+ }
27965
+ }
27948
27966
  handle(cmd) {
27949
27967
  switch (cmd.type) {
27950
27968
  case "ADD_MERGE":
@@ -28340,6 +28358,15 @@ class BordersPlugin extends CorePlugin {
28340
28358
  this.setBorders(sheetId, [{ ...zone, left: right }], "right");
28341
28359
  }
28342
28360
  }
28361
+ checkBordersUnchanged(cmd) {
28362
+ var _a, _b, _c, _d;
28363
+ const currentBorder = this.getCellBorder(cmd);
28364
+ 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);
28365
+ if ((!currentBorder && areAllNewBordersUndefined) || deepEquals(currentBorder, cmd.border)) {
28366
+ return 88 /* CommandResult.NoChanges */;
28367
+ }
28368
+ return 0 /* CommandResult.Success */;
28369
+ }
28343
28370
  // ---------------------------------------------------------------------------
28344
28371
  // Import/Export
28345
28372
  // ---------------------------------------------------------------------------
@@ -28375,7 +28402,7 @@ class BordersPlugin extends CorePlugin {
28375
28402
  */
28376
28403
  function getBorderId(border) {
28377
28404
  for (let [key, value] of Object.entries(borders)) {
28378
- if (stringify(value) === stringify(border)) {
28405
+ if (deepEquals(value, border)) {
28379
28406
  return parseInt(key, 10);
28380
28407
  }
28381
28408
  }
@@ -28443,8 +28470,9 @@ class CellPlugin extends CorePlugin {
28443
28470
  allowDispatch(cmd) {
28444
28471
  switch (cmd.type) {
28445
28472
  case "UPDATE_CELL":
28473
+ return this.checkCellOutOfSheet(cmd);
28446
28474
  case "CLEAR_CELL":
28447
- return this.checkCellOutOfSheet(cmd.sheetId, cmd.col, cmd.row);
28475
+ return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
28448
28476
  default:
28449
28477
  return 0 /* CommandResult.Success */;
28450
28478
  }
@@ -28603,14 +28631,9 @@ class CellPlugin extends CorePlugin {
28603
28631
  */
28604
28632
  getCellById(cellId) {
28605
28633
  // this must be as fast as possible
28606
- for (const sheetId in this.cells) {
28607
- const sheet = this.cells[sheetId];
28608
- const cell = sheet[cellId];
28609
- if (cell) {
28610
- return cell;
28611
- }
28612
- }
28613
- return undefined;
28634
+ const position = this.getters.getCellPosition(cellId);
28635
+ const sheet = this.cells[position.sheetId];
28636
+ return sheet[cellId];
28614
28637
  }
28615
28638
  /*
28616
28639
  * Reconstructs the original formula string based on a normalized form and its dependencies
@@ -28851,13 +28874,23 @@ class CellPlugin extends CorePlugin {
28851
28874
  dependencies: [],
28852
28875
  };
28853
28876
  }
28854
- checkCellOutOfSheet(sheetId, col, row) {
28877
+ checkCellOutOfSheet(cmd) {
28878
+ const { sheetId, col, row } = cmd;
28855
28879
  const sheet = this.getters.tryGetSheet(sheetId);
28856
28880
  if (!sheet)
28857
28881
  return 27 /* CommandResult.InvalidSheetId */;
28858
28882
  const sheetZone = this.getters.getSheetZone(sheetId);
28859
28883
  return isInside(col, row, sheetZone) ? 0 /* CommandResult.Success */ : 18 /* CommandResult.TargetOutOfSheet */;
28860
28884
  }
28885
+ checkUselessClearCell(cmd) {
28886
+ const cell = this.getters.getCell(cmd);
28887
+ if (!cell)
28888
+ return 88 /* CommandResult.NoChanges */;
28889
+ if (!cell.content && !cell.style && !cell.format) {
28890
+ return 88 /* CommandResult.NoChanges */;
28891
+ }
28892
+ return 0 /* CommandResult.Success */;
28893
+ }
28861
28894
  }
28862
28895
  CellPlugin.getters = [
28863
28896
  "zoneToXC",
@@ -29760,6 +29793,13 @@ class FiltersPlugin extends CorePlugin {
29760
29793
  }
29761
29794
  onDeleteColumnsRows(cmd) {
29762
29795
  for (const table of this.getFilterTables(cmd.sheetId)) {
29796
+ // Remove the filter tables whose data filter headers are in the removed rows.
29797
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
29798
+ const tables = { ...this.tables[cmd.sheetId] };
29799
+ delete tables[table.id];
29800
+ this.history.update("tables", cmd.sheetId, tables);
29801
+ continue;
29802
+ }
29763
29803
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
29764
29804
  if (!zone) {
29765
29805
  const tables = { ...this.tables[cmd.sheetId] };
@@ -31911,10 +31951,13 @@ class SheetPlugin extends CorePlugin {
31911
31951
  // begin with the end.
31912
31952
  rows.sort((a, b) => b - a);
31913
31953
  for (let group of groupConsecutive(rows)) {
31954
+ // indexes are sorted in the descending order
31955
+ const from = group[group.length - 1];
31956
+ const to = group[0];
31914
31957
  // Move the cells.
31915
- this.moveCellOnRowsDeletion(sheet, group[group.length - 1], group[0]);
31916
- // Effectively delete the element and recompute the left-right/top-bottom.
31917
- group.map((row) => this.updateRowsStructureOnDeletion(row, sheet));
31958
+ this.moveCellOnRowsDeletion(sheet, from, to);
31959
+ // Effectively delete the rows
31960
+ this.updateRowsStructureOnDeletion(sheet, from, to);
31918
31961
  }
31919
31962
  const count = rows.filter((row) => row < sheet.panes.ySplit).length;
31920
31963
  if (count) {
@@ -31936,8 +31979,6 @@ class SheetPlugin extends CorePlugin {
31936
31979
  this.addEmptyRows(sheet, quantity);
31937
31980
  // Move the cells.
31938
31981
  this.moveCellsOnAddition(sheet, index, quantity, "rows");
31939
- // Recompute the left-right/top-bottom.
31940
- this.updateRowsStructureOnAddition(sheet, row, quantity);
31941
31982
  if (index < sheet.panes.ySplit) {
31942
31983
  this.setPaneDivisions(sheet.id, sheet.panes.ySplit + quantity, "ROW");
31943
31984
  }
@@ -32038,35 +32079,20 @@ class SheetPlugin extends CorePlugin {
32038
32079
  }
32039
32080
  }
32040
32081
  }
32041
- updateRowsStructureOnDeletion(index, sheet) {
32082
+ updateRowsStructureOnDeletion(sheet, deleteFromRow, deleteToRow) {
32042
32083
  const rows = [];
32043
- const cellsQueue = sheet.rows.map((row) => row.cells);
32084
+ const cellsQueue = sheet.rows.map((row) => row.cells).reverse();
32044
32085
  for (let i in sheet.rows) {
32045
- if (Number(i) === index) {
32086
+ const row = Number(i);
32087
+ if (row >= deleteFromRow && row <= deleteToRow) {
32046
32088
  continue;
32047
32089
  }
32048
32090
  rows.push({
32049
- cells: cellsQueue.shift(),
32091
+ cells: cellsQueue.pop(),
32050
32092
  });
32051
32093
  }
32052
32094
  this.history.update("sheets", sheet.id, "rows", rows);
32053
32095
  }
32054
- /**
32055
- * Update the rows of the sheet after an addition:
32056
- * - Rename the rows
32057
- *
32058
- * @param sheet Sheet on which the deletion occurs
32059
- * @param addedRow Index of the added row
32060
- * @param rowsToAdd Number of the rows to add
32061
- */
32062
- updateRowsStructureOnAddition(sheet, addedRow, rowsToAdd) {
32063
- const rows = [];
32064
- const cellsQueue = sheet.rows.map((row) => row.cells);
32065
- sheet.rows.forEach(() => rows.push({
32066
- cells: cellsQueue.shift(),
32067
- }));
32068
- this.history.update("sheets", sheet.id, "rows", rows);
32069
- }
32070
32096
  /**
32071
32097
  * Add empty rows at the end of the rows
32072
32098
  *
@@ -32523,6 +32549,7 @@ class EvaluationPlugin extends UIPlugin {
32523
32549
  }
32524
32550
  return evaluatedCell;
32525
32551
  };
32552
+ const rangeCache = {};
32526
32553
  /**
32527
32554
  * Return the values of the cell(s) used in reference, but always in the format of a range even
32528
32555
  * if a single cell is referenced. It is a list of col values. This is useful for the formulas that describe parameters as
@@ -32539,21 +32566,26 @@ class EvaluationPlugin extends UIPlugin {
32539
32566
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
32540
32567
  // e.g. A1:ZZZ9999 in a sheet with 10 cols and 10 rows should ignore everything past J10 and return a 10x10 array
32541
32568
  const sheetZone = getters.getSheetZone(sheetId);
32542
- const result = [];
32543
32569
  const zone = intersection(range.zone, sheetZone);
32544
32570
  if (!zone) {
32545
- result.push([]);
32546
- return result;
32571
+ return [[]];
32572
+ }
32573
+ const { top, left, bottom, right } = zone;
32574
+ const cacheKey = `${sheetId}-${top}-${left}-${bottom}-${right}`;
32575
+ if (cacheKey in rangeCache) {
32576
+ return rangeCache[cacheKey];
32547
32577
  }
32578
+ const result = new Array(right - left + 1);
32548
32579
  // Performance issue: nested loop is faster than a map here
32549
- for (let col = zone.left; col <= zone.right; col++) {
32550
- const rowValues = [];
32551
- for (let row = zone.top; row <= zone.bottom; row++) {
32580
+ for (let col = left; col <= right; col++) {
32581
+ const rowValues = new Array(bottom - top + 1);
32582
+ for (let row = top; row <= bottom; row++) {
32552
32583
  const cell = evalContext.getters.getCell({ sheetId: range.sheetId, col, row });
32553
- rowValues.push(cell ? getEvaluatedCell(cell) : undefined);
32584
+ rowValues[row - top] = cell ? getEvaluatedCell(cell) : undefined;
32554
32585
  }
32555
- result.push(rowValues);
32586
+ result[col - left] = rowValues;
32556
32587
  }
32588
+ rangeCache[cacheKey] = result;
32557
32589
  return result;
32558
32590
  }
32559
32591
  /**
@@ -33099,6 +33131,8 @@ class FilterEvaluationPlugin extends UIPlugin {
33099
33131
  case "EVALUATE_CELLS":
33100
33132
  case "ACTIVATE_SHEET":
33101
33133
  case "REMOVE_FILTER_TABLE":
33134
+ case "ADD_COLUMNS_ROWS":
33135
+ case "REMOVE_COLUMNS_ROWS":
33102
33136
  this.isEvaluationDirty = true;
33103
33137
  break;
33104
33138
  case "START":
@@ -41665,9 +41699,9 @@ function revertChanges(revisions) {
41665
41699
  * Apply the changes of the given HistoryChange to the state
41666
41700
  */
41667
41701
  function applyChange(change, target) {
41668
- let val = change.root;
41669
- let key = change.path[change.path.length - 1];
41670
- for (let pathIndex = 0; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
41702
+ let val = change.path[0];
41703
+ const key = change.path[change.path.length - 1];
41704
+ for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
41671
41705
  const p = change.path[pathIndex];
41672
41706
  if (val[p] === undefined) {
41673
41707
  const nextPath = change.path[pathIndex + 1];
@@ -42422,13 +42456,14 @@ class StateObserver {
42422
42456
  }
42423
42457
  addChange(...args) {
42424
42458
  const val = args.pop();
42425
- const [root, ...path] = args;
42459
+ const root = args[0];
42426
42460
  let value = root;
42427
- let key = path[path.length - 1];
42428
- for (let pathIndex = 0; pathIndex <= path.length - 2; pathIndex++) {
42429
- const p = path[pathIndex];
42461
+ let key = args[args.length - 1];
42462
+ const pathLength = args.length - 2;
42463
+ for (let pathIndex = 1; pathIndex <= pathLength; pathIndex++) {
42464
+ const p = args[pathIndex];
42430
42465
  if (value[p] === undefined) {
42431
- const nextPath = path[pathIndex + 1];
42466
+ const nextPath = args[pathIndex + 1];
42432
42467
  value[p] = createEmptyStructure(nextPath);
42433
42468
  }
42434
42469
  value = value[p];
@@ -42437,8 +42472,7 @@ class StateObserver {
42437
42472
  return;
42438
42473
  }
42439
42474
  this.changes.push({
42440
- root,
42441
- path,
42475
+ path: args,
42442
42476
  before: value[key],
42443
42477
  after: val,
42444
42478
  });
@@ -44212,7 +44246,10 @@ class Model extends EventBus {
44212
44246
  */
44213
44247
  checkDispatchAllowed(command) {
44214
44248
  const results = this.handlers.map((handler) => handler.allowDispatch(command));
44215
- return new DispatchResult(results.flat());
44249
+ if (results.some((r) => r !== 0 /* CommandResult.Success */)) {
44250
+ return new DispatchResult(results.flat());
44251
+ }
44252
+ return DispatchResult.Success;
44216
44253
  }
44217
44254
  finalize() {
44218
44255
  this.status = 3 /* Status.Finalizing */;
@@ -44440,6 +44477,6 @@ exports.setTranslationMethod = setTranslationMethod;
44440
44477
  exports.tokenize = tokenize;
44441
44478
 
44442
44479
 
44443
- __info__.version = '16.1.26';
44444
- __info__.date = '2023-11-02T09:31:23.420Z';
44445
- __info__.hash = '1ddd8d0';
44480
+ __info__.version = '16.1.28';
44481
+ __info__.date = '2023-11-24T13:11:01.992Z';
44482
+ __info__.hash = '257f369';
@@ -1194,7 +1194,7 @@ declare class StateObserver {
1194
1194
  commands: CoreCommand[];
1195
1195
  };
1196
1196
  addCommand(command: CoreCommand): void;
1197
- addChange(...args: any[]): void;
1197
+ addChange(...args: [...HistoryChange["path"], any]): void;
1198
1198
  }
1199
1199
 
1200
1200
  interface Validator {
@@ -2259,7 +2259,8 @@ declare const enum CommandResult {
2259
2259
  DuplicatedChartId = 84,
2260
2260
  ChartDoesNotExist = 85,
2261
2261
  InvalidHeaderIndex = 86,
2262
- InvalidQuantity = 87
2262
+ InvalidQuantity = 87,
2263
+ NoChanges = 88
2263
2264
  }
2264
2265
  interface CommandHandler<T> {
2265
2266
  allowDispatch(command: T): CommandResult | CommandResult[];
@@ -3043,7 +3044,8 @@ interface BordersPluginState {
3043
3044
  declare class BordersPlugin extends CorePlugin<BordersPluginState> implements BordersPluginState {
3044
3045
  static getters: readonly ["getCellBorder"];
3045
3046
  readonly borders: BordersPluginState["borders"];
3046
- handle(cmd: Command): void;
3047
+ allowDispatch(cmd: CoreCommand): CommandResult.Success | CommandResult.NoChanges;
3048
+ handle(cmd: CoreCommand): void;
3047
3049
  /**
3048
3050
  * Move borders according to the inserted columns.
3049
3051
  * Ensure borders continuity.
@@ -3140,6 +3142,7 @@ declare class BordersPlugin extends CorePlugin<BordersPluginState> implements Bo
3140
3142
  * Compute the borders to add to the given zone merged.
3141
3143
  */
3142
3144
  private addBordersToMerge;
3145
+ private checkBordersUnchanged;
3143
3146
  import(data: WorkbookData): void;
3144
3147
  export(data: WorkbookData): void;
3145
3148
  exportForExcel(data: ExcelWorkbookData): void;
@@ -3164,7 +3167,7 @@ declare class CellPlugin extends CorePlugin<CoreState> implements CoreState {
3164
3167
  };
3165
3168
  };
3166
3169
  adaptRanges(applyChange: ApplyRangeChange, sheetId?: UID): void;
3167
- allowDispatch(cmd: CoreCommand): CommandResult;
3170
+ allowDispatch(cmd: CoreCommand): CommandResult | CommandResult[];
3168
3171
  handle(cmd: CoreCommand): void;
3169
3172
  /**
3170
3173
  * Set a format to all the cells in a zone
@@ -3237,6 +3240,7 @@ declare class CellPlugin extends CorePlugin<CoreState> implements CoreState {
3237
3240
  private createFormulaCellWithDependencies;
3238
3241
  private createErrorFormula;
3239
3242
  private checkCellOutOfSheet;
3243
+ private checkUselessClearCell;
3240
3244
  }
3241
3245
 
3242
3246
  /**
@@ -3745,15 +3749,6 @@ declare class SheetPlugin extends CorePlugin<SheetState> implements SheetState {
3745
3749
  */
3746
3750
  private moveCellOnRowsDeletion;
3747
3751
  private updateRowsStructureOnDeletion;
3748
- /**
3749
- * Update the rows of the sheet after an addition:
3750
- * - Rename the rows
3751
- *
3752
- * @param sheet Sheet on which the deletion occurs
3753
- * @param addedRow Index of the added row
3754
- * @param rowsToAdd Number of the rows to add
3755
- */
3756
- private updateRowsStructureOnAddition;
3757
3752
  /**
3758
3753
  * Add empty rows at the end of the rows
3759
3754
  *
@@ -4806,8 +4801,7 @@ type Getters = {
4806
4801
  } & LocalHistoryGetters & CoreGetters & SessionGetters & AutofillGetters & AutomaticSumGetters & ClipboardGetters & EditionGetters & EvaluationGetters & EvaluationChartGetters & EvaluationConditionalFormatGetters & FindAndReplaceGetters & HighlightGetters & CustomColorsGetters & RendererGetters & SelectionGetters & SelectionInputGetters & SelectionMultiUserGetters & SortGetters & UIOptionsGetters & SheetUIGetters & ViewportGetters & CellPopoverPluginGetters & FilterEvaluationGetters & HeaderVisibilityIUIGetters;
4807
4802
 
4808
4803
  interface HistoryChange {
4809
- root: any;
4810
- path: (string | number)[];
4804
+ path: [any, ...(number | string)[]];
4811
4805
  before: any;
4812
4806
  after: any;
4813
4807
  }