@odoo/o-spreadsheet 17.1.4 → 17.1.5

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.1.4
6
- * @date 2024-02-09T13:09:13.430Z
7
- * @hash 16d3ece
5
+ * @version 17.1.5
6
+ * @date 2024-02-16T14:23:54.651Z
7
+ * @hash 29d8ad6
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -452,8 +452,8 @@
452
452
  * If the given item does not exist in the dictionary, it creates one with a new id.
453
453
  */
454
454
  function getItemId(item, itemsDic) {
455
- for (let [key, value] of Object.entries(itemsDic)) {
456
- if (deepEquals(value, item)) {
455
+ for (const key in itemsDic) {
456
+ if (deepEquals(itemsDic[key], item)) {
457
457
  return parseInt(key, 10);
458
458
  }
459
459
  }
@@ -552,11 +552,13 @@
552
552
  if (typeof o1 !== "object")
553
553
  return o1 === o2;
554
554
  // Objects can have different keys if the values are undefined
555
- const keys = new Set();
556
- Object.keys(o1).forEach((key) => keys.add(key));
557
- Object.keys(o2).forEach((key) => keys.add(key));
558
- for (let key of keys) {
559
- if (typeof o1[key] !== typeof o1[key])
555
+ for (const key in o2) {
556
+ if (!(key in o1) && o2[key] !== undefined) {
557
+ return false;
558
+ }
559
+ }
560
+ for (const key in o1) {
561
+ if (typeof o1[key] !== typeof o2[key])
560
562
  return false;
561
563
  if (typeof o1[key] === "object") {
562
564
  if (!deepEquals(o1[key], o2[key]))
@@ -626,18 +628,18 @@
626
628
  return true;
627
629
  }
628
630
  class JetSet extends Set {
629
- add(...iterable) {
631
+ addMany(iterable) {
630
632
  for (const element of iterable) {
631
633
  super.add(element);
632
634
  }
633
635
  return this;
634
636
  }
635
- delete(...iterable) {
636
- let deleted = false;
637
+ deleteMany(iterable) {
638
+ let wasDeleted = false;
637
639
  for (const element of iterable) {
638
- deleted ||= super.delete(element);
640
+ wasDeleted ||= super.delete(element);
639
641
  }
640
- return deleted;
642
+ return wasDeleted;
641
643
  }
642
644
  }
643
645
  /**
@@ -1935,7 +1937,7 @@
1935
1937
  * Static helper which returns a successful DispatchResult
1936
1938
  */
1937
1939
  static get Success() {
1938
- return new DispatchResult();
1940
+ return SUCCESS;
1939
1941
  }
1940
1942
  get isSuccessful() {
1941
1943
  return this.reasons.length === 0;
@@ -1948,6 +1950,7 @@
1948
1950
  return this.reasons.includes(reason);
1949
1951
  }
1950
1952
  }
1953
+ const SUCCESS = new DispatchResult();
1951
1954
  exports.CommandResult = void 0;
1952
1955
  (function (CommandResult) {
1953
1956
  CommandResult["Success"] = "Success";
@@ -7654,6 +7657,9 @@
7654
7657
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7655
7658
  return false;
7656
7659
  }
7660
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
7661
+ return false;
7662
+ }
7657
7663
  try {
7658
7664
  formatValue(1, { locale, format: "#,##0.00" });
7659
7665
  formatValue(1, { locale, format: locale.dateFormat });
@@ -7744,7 +7750,7 @@
7744
7750
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7745
7751
  return content;
7746
7752
  }
7747
- return content.replace(locale.decimalSeparator, ".");
7753
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7748
7754
  }
7749
7755
  /**
7750
7756
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -10839,6 +10845,12 @@
10839
10845
  let { col, row } = env.model.getters.getActivePosition();
10840
10846
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
10841
10847
  };
10848
+ const INSERT_LINK_NAME = (env) => {
10849
+ const sheetId = env.model.getters.getActiveSheetId();
10850
+ const { col, row } = env.model.getters.getActivePosition();
10851
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
10852
+ return cell && cell.link ? _t("Edit link") : _t("Insert link");
10853
+ };
10842
10854
  //------------------------------------------------------------------------------
10843
10855
  // Filters action
10844
10856
  //------------------------------------------------------------------------------
@@ -20083,7 +20095,7 @@
20083
20095
  })
20084
20096
  .add("insert_link", {
20085
20097
  ...insertLink,
20086
- name: _t("Insert link"),
20098
+ name: INSERT_LINK_NAME,
20087
20099
  sequence: 150,
20088
20100
  separator: true,
20089
20101
  });
@@ -26526,6 +26538,17 @@
26526
26538
  onValueSelected: Function,
26527
26539
  onValueHovered: Function,
26528
26540
  };
26541
+ autoCompleteListRef = owl.useRef("autoCompleteList");
26542
+ setup() {
26543
+ owl.useEffect(() => {
26544
+ const selectedIndex = this.props.selectedIndex;
26545
+ if (selectedIndex === undefined) {
26546
+ return;
26547
+ }
26548
+ const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
26549
+ selectedElement?.scrollIntoView?.({ block: "nearest" });
26550
+ }, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
26551
+ }
26529
26552
  }
26530
26553
 
26531
26554
  class ContentEditableHelper {
@@ -26970,6 +26993,7 @@
26970
26993
  position: absolute;
26971
26994
  margin: 1px 4px;
26972
26995
  pointer-events: none;
26996
+ overflow: auto;
26973
26997
 
26974
26998
  .o-semi-bold {
26975
26999
  /** FIXME: to remove in favor of Bootstrap
@@ -27027,7 +27051,10 @@
27027
27051
  if (this.props.delimitation && this.props.rect) {
27028
27052
  const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
27029
27053
  const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
27054
+ assistantStyle["max-height"] = `${remainingHeight}px`;
27030
27055
  if (cellY > remainingHeight) {
27056
+ const availableSpaceAbove = cellY;
27057
+ assistantStyle["max-height"] = `${availableSpaceAbove}px`;
27031
27058
  // render top
27032
27059
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
27033
27060
  assistantStyle.top = `-3px`;
@@ -35146,22 +35173,7 @@
35146
35173
  }
35147
35174
  }
35148
35175
  export(data) {
35149
- // Borders
35150
- let borderId = 0;
35151
35176
  const borders = {};
35152
- /**
35153
- * Get the id of the given border. If the border does not exist, it creates
35154
- * one.
35155
- */
35156
- function getBorderId(border) {
35157
- for (let [key, value] of Object.entries(borders)) {
35158
- if (deepEquals(value, border)) {
35159
- return parseInt(key, 10);
35160
- }
35161
- }
35162
- borders[++borderId] = border;
35163
- return borderId;
35164
- }
35165
35177
  for (let sheet of data.sheets) {
35166
35178
  for (let col = 0; col < sheet.colNumber; col++) {
35167
35179
  for (let row = 0; row < sheet.rowNumber; row++) {
@@ -35169,7 +35181,7 @@
35169
35181
  if (border) {
35170
35182
  const xc = toXC(col, row);
35171
35183
  const cell = sheet.cells[xc];
35172
- const borderId = getBorderId(border);
35184
+ const borderId = getItemId(border, borders);
35173
35185
  if (cell) {
35174
35186
  cell.border = borderId;
35175
35187
  }
@@ -35958,9 +35970,9 @@
35958
35970
  allowDispatch(cmd) {
35959
35971
  switch (cmd.type) {
35960
35972
  case "UPDATE_CELL":
35961
- return this.checkCellOutOfSheet(cmd);
35973
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35962
35974
  case "CLEAR_CELL":
35963
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35975
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35964
35976
  default:
35965
35977
  return "Success" /* CommandResult.Success */;
35966
35978
  }
@@ -36110,8 +36122,8 @@
36110
36122
  }
36111
36123
  removeDefaultStyleValues(style) {
36112
36124
  const cleanedStyle = { ...style };
36113
- for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
36114
- if (cleanedStyle[property] === defaultValue) {
36125
+ for (const property in DEFAULT_STYLE) {
36126
+ if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
36115
36127
  delete cleanedStyle[property];
36116
36128
  }
36117
36129
  }
@@ -36267,10 +36279,7 @@
36267
36279
  else {
36268
36280
  style = before ? before.style : undefined;
36269
36281
  }
36270
- const locale = this.getters.getLocale();
36271
- let format = ("format" in after ? after.format : before && before.format) ||
36272
- detectDateFormat(afterContent, locale) ||
36273
- detectNumberFormat(afterContent);
36282
+ const format = "format" in after ? after.format : before && before.format;
36274
36283
  /* Read the following IF as:
36275
36284
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
36276
36285
  * - the command says the new content is empty and has no border/format/style
@@ -36311,6 +36320,7 @@
36311
36320
  }
36312
36321
  createLiteralCell(id, content, format, style) {
36313
36322
  const locale = this.getters.getLocale();
36323
+ format = format || detectDateFormat(content, locale) || detectNumberFormat(content);
36314
36324
  if (format !== PLAIN_TEXT_FORMAT && !isEvaluationError(content)) {
36315
36325
  content = toString(parseLiteral(content, locale));
36316
36326
  }
@@ -36383,6 +36393,18 @@
36383
36393
  }
36384
36394
  return "Success" /* CommandResult.Success */;
36385
36395
  }
36396
+ checkUselessUpdateCell(cmd) {
36397
+ const cell = this.getters.getCell(cmd);
36398
+ const hasContent = "content" in cmd || "formula" in cmd;
36399
+ const hasStyle = "style" in cmd;
36400
+ const hasFormat = "format" in cmd;
36401
+ if ((!hasContent || cell?.content === cmd.content) &&
36402
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
36403
+ (!hasFormat || cell?.format === cmd.format)) {
36404
+ return "NoChanges" /* CommandResult.NoChanges */;
36405
+ }
36406
+ return "Success" /* CommandResult.Success */;
36407
+ }
36386
36408
  }
36387
36409
  class FormulaCellWithDependencies {
36388
36410
  id;
@@ -41167,7 +41189,15 @@
41167
41189
  if (!this.rTrees[sheetId]) {
41168
41190
  return;
41169
41191
  }
41170
- this.rTrees[sheetId].remove(item, deepEquals);
41192
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
41193
+ }
41194
+ rtreeItemComparer(left, right) {
41195
+ return (left.data == right.data &&
41196
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
41197
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
41198
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
41199
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
41200
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
41171
41201
  }
41172
41202
  }
41173
41203
  /**
@@ -41245,7 +41275,7 @@
41245
41275
  const queue = Array.from(ranges).reverse();
41246
41276
  while (queue.length > 0) {
41247
41277
  const range = queue.pop();
41248
- visited.add(...this.encoder.encodeBoundingBox(range));
41278
+ visited.addMany(this.encoder.encodeBoundingBox(range));
41249
41279
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
41250
41280
  for (const positionId of impactedPositionIds) {
41251
41281
  if (!visited.has(positionId)) {
@@ -41253,7 +41283,7 @@
41253
41283
  }
41254
41284
  }
41255
41285
  }
41256
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41286
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41257
41287
  return visited;
41258
41288
  }
41259
41289
  }
@@ -41393,9 +41423,9 @@
41393
41423
  const cells = positions.map((p) => this.encoder.encode(p));
41394
41424
  const cellsToCompute = new JetSet(cells);
41395
41425
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
41396
- cellsToCompute.add(...this.getCellsDependingOn(cells));
41397
- cellsToCompute.add(...arrayFormulasPositionIds);
41398
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
41426
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
41427
+ cellsToCompute.addMany(arrayFormulasPositionIds);
41428
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
41399
41429
  this.evaluate(cellsToCompute);
41400
41430
  }
41401
41431
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -41409,7 +41439,7 @@
41409
41439
  }
41410
41440
  if (!content) {
41411
41441
  // The previous content could have blocked some array formulas
41412
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41442
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41413
41443
  }
41414
41444
  }
41415
41445
  return impactedPositionIds;
@@ -41461,7 +41491,7 @@
41461
41491
  }
41462
41492
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
41463
41493
  const cells = new JetSet(arrayFormulas);
41464
- cells.add(...this.getCellsDependingOn(arrayFormulas));
41494
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
41465
41495
  return cells;
41466
41496
  }
41467
41497
  nextPositionsToUpdate = new JetSet();
@@ -41599,7 +41629,7 @@
41599
41629
  this.setEvaluatedCell(positionId, evaluatedCell);
41600
41630
  // check if formula dependencies present in the spread zone
41601
41631
  // if so, they need to be recomputed
41602
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
41632
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41603
41633
  };
41604
41634
  }
41605
41635
  invalidateSpreading(positionId) {
@@ -41614,8 +41644,8 @@
41614
41644
  continue;
41615
41645
  }
41616
41646
  this.evaluatedCells.delete(child);
41617
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
41618
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
41647
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
41648
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
41619
41649
  }
41620
41650
  this.spreadingRelations.removeNode(positionId);
41621
41651
  }
@@ -45484,7 +45514,7 @@
45484
45514
  }
45485
45515
  finalize() {
45486
45516
  if (this.isSearchDirty) {
45487
- this.refreshSearch();
45517
+ this.refreshSearch(false);
45488
45518
  this.isSearchDirty = false;
45489
45519
  }
45490
45520
  }
@@ -45528,10 +45558,10 @@
45528
45558
  /**
45529
45559
  * refresh the matches according to the current search options
45530
45560
  */
45531
- refreshSearch() {
45561
+ refreshSearch(jumpToMatchSheet = true) {
45532
45562
  this.selectedMatchIndex = null;
45533
45563
  this.findMatches();
45534
- this.selectNextCell(Direction.current);
45564
+ this.selectNextCell(Direction.current, jumpToMatchSheet);
45535
45565
  }
45536
45566
  /**
45537
45567
  * Updates the regex based on the current searchOptions and
@@ -45613,7 +45643,7 @@
45613
45643
  * It is also used to keep coherence between the selected searchMatch
45614
45644
  * and selectedMatchIndex.
45615
45645
  */
45616
- selectNextCell(indexChange) {
45646
+ selectNextCell(indexChange, jumpToMatchSheet = true) {
45617
45647
  const matches = this.searchMatches;
45618
45648
  if (!matches.length) {
45619
45649
  this.selectedMatchIndex = null;
@@ -45639,7 +45669,7 @@
45639
45669
  this.selectedMatchIndex = nextIndex;
45640
45670
  const selectedMatch = matches[nextIndex];
45641
45671
  // Switch to the sheet where the match is located
45642
- if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
45672
+ if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
45643
45673
  this.dispatch("ACTIVATE_SHEET", {
45644
45674
  sheetIdFrom: this.getters.getActiveSheetId(),
45645
45675
  sheetIdTo: selectedMatch.sheetId,
@@ -47211,7 +47241,7 @@
47211
47241
  "getCellText",
47212
47242
  "getCellMultiLineText",
47213
47243
  "getContiguousZone",
47214
- "isCellEmpty",
47244
+ "isEvaluatedCellEmpty",
47215
47245
  ];
47216
47246
  ctx = document.createElement("canvas").getContext("2d");
47217
47247
  // ---------------------------------------------------------------------------
@@ -47339,14 +47369,23 @@
47339
47369
  return zone;
47340
47370
  }
47341
47371
  /**
47342
- * Check if a cell is empty. If the cell is part of a merge,
47343
- * check if the merge containing the cell is empty.
47372
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
47373
+ * the check applies to the main cell of the merge.
47344
47374
  */
47345
- isCellEmpty(position) {
47375
+ isEvaluatedCellEmpty(position) {
47346
47376
  const mainPosition = this.getters.getMainCellPosition(position);
47347
47377
  const cell = this.getters.getEvaluatedCell(mainPosition);
47348
47378
  return cell.type === CellValueType.empty;
47349
47379
  }
47380
+ /**
47381
+ * Checks if a cell is empty (i.e. does not have a content or a formula does not spread over it).
47382
+ * If the cell is part of a merge, the check applies to the main cell of the merge.
47383
+ */
47384
+ isCellEmpty(position) {
47385
+ const mainPosition = this.getters.getMainCellPosition(position);
47386
+ return !(this.getters.getCorrespondingFormulaCell(mainPosition) ||
47387
+ this.getters.getCell(mainPosition)?.content);
47388
+ }
47350
47389
  getColMaxWidth(sheetId, index) {
47351
47390
  const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
47352
47391
  const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
@@ -54888,8 +54927,8 @@
54888
54927
  let currentPosition = startPosition;
54889
54928
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
54890
54929
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
54891
- let mode = !this.getters.isCellEmpty({ ...currentPosition, sheetId }) &&
54892
- !this.getters.isCellEmpty({ ...nextCellPosition, sheetId })
54930
+ let mode = !this.getters.isEvaluatedCellEmpty({ ...currentPosition, sheetId }) &&
54931
+ !this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId })
54893
54932
  ? "endOfCluster"
54894
54933
  : "nextCluster";
54895
54934
  while (true) {
@@ -54899,7 +54938,7 @@
54899
54938
  currentPosition.row === nextCellPosition.row) {
54900
54939
  break;
54901
54940
  }
54902
- const isNextCellEmpty = this.getters.isCellEmpty({ ...nextCellPosition, sheetId });
54941
+ const isNextCellEmpty = this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId });
54903
54942
  if (mode === "endOfCluster" && isNextCellEmpty) {
54904
54943
  break;
54905
54944
  }
@@ -57184,9 +57223,9 @@
57184
57223
  exports.tokenize = tokenize;
57185
57224
 
57186
57225
 
57187
- __info__.version = "17.1.4";
57188
- __info__.date = "2024-02-09T13:09:13.430Z";
57189
- __info__.hash = "16d3ece";
57226
+ __info__.version = "17.1.5";
57227
+ __info__.date = "2024-02-16T14:23:54.651Z";
57228
+ __info__.hash = "29d8ad6";
57190
57229
 
57191
57230
 
57192
57231
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);