@odoo/o-spreadsheet 16.4.33 → 16.4.36

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.
@@ -3233,8 +3233,11 @@
3233
3233
  /**
3234
3234
  * Create a range from a xc. If the xc is empty, this function returns undefined.
3235
3235
  */
3236
- function createRange(getters, sheetId, range) {
3237
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
3236
+ function createValidRange(getters, sheetId, xc) {
3237
+ if (!xc)
3238
+ return;
3239
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
3240
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
3238
3241
  }
3239
3242
  /**
3240
3243
  * Spread multiple colrows zone to one row/col zone and add a many new input range as needed.
@@ -5701,6 +5704,7 @@
5701
5704
  };
5702
5705
 
5703
5706
  const macRegex = /Mac/i;
5707
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
5704
5708
  /**
5705
5709
  * Return true if the event was triggered from
5706
5710
  * a child element.
@@ -5739,6 +5743,31 @@
5739
5743
  function getOpenedMenus() {
5740
5744
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5741
5745
  }
5746
+ const letterRegex = /^[a-zA-Z]$/;
5747
+ /**
5748
+ * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
5749
+ *
5750
+ * @argument ev - The keyboard event to transform
5751
+ * @argument mode - Use either ev.key of ev.code to get the string shortcut
5752
+ *
5753
+ * @example
5754
+ * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
5755
+ * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
5756
+ */
5757
+ function keyboardEventToShortcutString(ev, mode = "key") {
5758
+ let keyDownString = "";
5759
+ if (!MODIFIER_KEYS.includes(ev.key)) {
5760
+ if (isCtrlKey(ev))
5761
+ keyDownString += "Ctrl+";
5762
+ if (ev.altKey)
5763
+ keyDownString += "Alt+";
5764
+ if (ev.shiftKey)
5765
+ keyDownString += "Shift+";
5766
+ }
5767
+ const key = mode === "key" ? ev.key : ev.code;
5768
+ keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
5769
+ return keyDownString;
5770
+ }
5742
5771
  function isMacOS() {
5743
5772
  return Boolean(macRegex.test(navigator.userAgent));
5744
5773
  }
@@ -5757,8 +5786,7 @@
5757
5786
  */
5758
5787
  function useSpreadsheetRect() {
5759
5788
  const position = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
5760
- let spreadsheetElement = document.querySelector(".o-spreadsheet");
5761
- updatePosition();
5789
+ let spreadsheetElement = null;
5762
5790
  function updatePosition() {
5763
5791
  if (!spreadsheetElement) {
5764
5792
  spreadsheetElement = document.querySelector(".o-spreadsheet");
@@ -16539,7 +16567,7 @@
16539
16567
  constructor(definition, sheetId, getters) {
16540
16568
  super(definition, sheetId, getters);
16541
16569
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
16542
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
16570
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
16543
16571
  this.background = definition.background;
16544
16572
  this.verticalAxisPosition = definition.verticalAxisPosition;
16545
16573
  this.legendPosition = definition.legendPosition;
@@ -16784,7 +16812,7 @@
16784
16812
  type = "gauge";
16785
16813
  constructor(definition, sheetId, getters) {
16786
16814
  super(definition, sheetId, getters);
16787
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
16815
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
16788
16816
  this.sectionRule = definition.sectionRule;
16789
16817
  this.background = definition.background;
16790
16818
  }
@@ -17123,7 +17151,7 @@
17123
17151
  constructor(definition, sheetId, getters) {
17124
17152
  super(definition, sheetId, getters);
17125
17153
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
17126
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
17154
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
17127
17155
  this.background = definition.background;
17128
17156
  this.verticalAxisPosition = definition.verticalAxisPosition;
17129
17157
  this.legendPosition = definition.legendPosition;
@@ -17421,7 +17449,7 @@
17421
17449
  constructor(definition, sheetId, getters) {
17422
17450
  super(definition, sheetId, getters);
17423
17451
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
17424
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
17452
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
17425
17453
  this.background = definition.background;
17426
17454
  this.legendPosition = definition.legendPosition;
17427
17455
  this.aggregated = definition.aggregated;
@@ -17589,8 +17617,8 @@
17589
17617
  type = "scorecard";
17590
17618
  constructor(definition, sheetId, getters) {
17591
17619
  super(definition, sheetId, getters);
17592
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
17593
- this.baseline = createRange(getters, sheetId, definition.baseline);
17620
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
17621
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
17594
17622
  this.baselineMode = definition.baselineMode;
17595
17623
  this.baselineDescr = definition.baselineDescr;
17596
17624
  this.background = definition.background;
@@ -20949,7 +20977,7 @@
20949
20977
  }
20950
20978
  const getters = this.env.model.getters;
20951
20979
  const sheetId = getters.getActiveSheetId();
20952
- const labelRange = createRange(getters, sheetId, this.labelRange);
20980
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
20953
20981
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
20954
20982
  if (dataSets.length) {
20955
20983
  return dataSets[0].dataRange.zone.top + 1;
@@ -23414,14 +23442,14 @@
23414
23442
  }
23415
23443
  onKeyDown(ev) {
23416
23444
  const figure = this.props.figure;
23417
- switch (ev.key) {
23445
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
23446
+ switch (keyDownShortcut) {
23418
23447
  case "Delete":
23419
23448
  this.env.model.dispatch("DELETE_FIGURE", {
23420
23449
  sheetId: this.env.model.getters.getActiveSheetId(),
23421
23450
  id: figure.id,
23422
23451
  });
23423
23452
  this.props.onFigureDeleted();
23424
- ev.stopPropagation();
23425
23453
  ev.preventDefault();
23426
23454
  ev.stopPropagation();
23427
23455
  break;
@@ -23442,7 +23470,22 @@
23442
23470
  x: figure.x + delta[0],
23443
23471
  y: figure.y + delta[1],
23444
23472
  });
23473
+ ev.preventDefault();
23445
23474
  ev.stopPropagation();
23475
+ break;
23476
+ case "Ctrl+A":
23477
+ // Maybe in the future we will implement a way to select all figures
23478
+ ev.preventDefault();
23479
+ ev.stopPropagation();
23480
+ break;
23481
+ case "Ctrl+Y":
23482
+ case "Ctrl+Z":
23483
+ if (keyDownShortcut === "Ctrl+Y") {
23484
+ this.env.model.dispatch("REQUEST_REDO");
23485
+ }
23486
+ else if (keyDownShortcut === "Ctrl+Z") {
23487
+ this.env.model.dispatch("REQUEST_UNDO");
23488
+ }
23446
23489
  ev.preventDefault();
23447
23490
  ev.stopPropagation();
23448
23491
  break;
@@ -24167,7 +24210,7 @@
24167
24210
  Enter: this.processEnterKey,
24168
24211
  Escape: this.processEscapeKey,
24169
24212
  F2: () => console.warn("Not implemented"),
24170
- F4: this.processF4Key,
24213
+ F4: (ev) => this.processF4Key(ev),
24171
24214
  Tab: (ev) => this.processTabKey(ev),
24172
24215
  };
24173
24216
  keyCodeMapping = {
@@ -24269,9 +24312,10 @@
24269
24312
  processEscapeKey() {
24270
24313
  this.env.model.dispatch("STOP_EDITION", { cancel: true });
24271
24314
  }
24272
- processF4Key() {
24315
+ processF4Key(ev) {
24273
24316
  this.env.model.dispatch("CYCLE_EDITION_REFERENCES");
24274
24317
  this.processContent();
24318
+ ev.stopPropagation();
24275
24319
  }
24276
24320
  processNumpadDecimal(ev) {
24277
24321
  ev.stopPropagation();
@@ -24312,8 +24356,15 @@
24312
24356
  }
24313
24357
  onPaste(ev) {
24314
24358
  if (this.env.model.getters.getEditionMode() !== "inactive") {
24359
+ // let the browser clipboard work
24315
24360
  ev.stopPropagation();
24316
24361
  }
24362
+ else {
24363
+ // the user meant to paste in the sheet, not open the composer with the pasted content
24364
+ // While we're not editing, we still have the focus and should therefore prevent
24365
+ // the native "paste" to occur.
24366
+ ev.preventDefault();
24367
+ }
24317
24368
  }
24318
24369
  /*
24319
24370
  * Triggered automatically by the content-editable between the keydown and key up
@@ -24322,10 +24373,6 @@
24322
24373
  if (!this.shouldProcessInputEvents) {
24323
24374
  return;
24324
24375
  }
24325
- if (ev.inputType === "insertFromPaste" &&
24326
- this.env.model.getters.getEditionMode() === "inactive") {
24327
- return;
24328
- }
24329
24376
  ev.stopPropagation();
24330
24377
  let content;
24331
24378
  if (this.env.model.getters.getEditionMode() === "inactive") {
@@ -24756,10 +24803,7 @@
24756
24803
  }
24757
24804
  get containerStyle() {
24758
24805
  if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
24759
- return `
24760
- position: absolute;
24761
- z-index: -1000;
24762
- `;
24806
+ return `z-index: -1000;`;
24763
24807
  }
24764
24808
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
24765
24809
  const cell = this.env.model.getters.getActiveCell();
@@ -33987,7 +34031,12 @@
33987
34031
  * if they have at least a common cell
33988
34032
  */
33989
34033
  doesIntersectMerge(sheetId, zone) {
33990
- return positions(zone).some(({ col, row }) => this.getMerge({ sheetId, col, row }) !== undefined);
34034
+ for (const merge of this.getMerges(sheetId)) {
34035
+ if (overlap(zone, merge)) {
34036
+ return true;
34037
+ }
34038
+ }
34039
+ return false;
33991
34040
  }
33992
34041
  /**
33993
34042
  * Returns true if two columns have at least one merge in common
@@ -34584,6 +34633,9 @@
34584
34633
  if (range.invalidXc) {
34585
34634
  return range.invalidXc;
34586
34635
  }
34636
+ if (!this.getters.tryGetSheet(range.sheetId)) {
34637
+ return INCORRECT_RANGE_STRING;
34638
+ }
34587
34639
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
34588
34640
  return INCORRECT_RANGE_STRING;
34589
34641
  }
@@ -37635,16 +37687,22 @@
37635
37687
  let newContent = undefined;
37636
37688
  let newFormat = undefined;
37637
37689
  let isExported = true;
37690
+ const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
37638
37691
  const formulaCell = this.getCorrespondingFormulaCell(position);
37639
37692
  if (formulaCell) {
37640
37693
  isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
37641
37694
  isFormula = isExported;
37642
37695
  if (!isExported) {
37643
- newContent = value.toString();
37644
- newFormat = evaluatedCell.format;
37696
+ // If the cell contains a non-exported formula and that is evaluates to
37697
+ // nothing* ,we don't export it.
37698
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
37699
+ // the empty string.
37700
+ if (value !== "") {
37701
+ newContent = value.toString();
37702
+ newFormat = evaluatedCell.format;
37703
+ }
37645
37704
  }
37646
37705
  }
37647
- const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
37648
37706
  const exportedCellData = exportedSheetData.cells[xc] || {};
37649
37707
  const format = newFormat
37650
37708
  ? getItemId(newFormat, data.formats)
@@ -43428,7 +43486,7 @@
43428
43486
  case "ADD_COLUMNS_ROWS": {
43429
43487
  this.status = "invisible";
43430
43488
  // If we add a col/row inside or before the cut area, we invalidate the clipboard
43431
- if (this.state?.operation !== "CUT") {
43489
+ if (this.state?.operation !== "CUT" || cmd.sheetId !== this.state?.sheetId) {
43432
43490
  return;
43433
43491
  }
43434
43492
  const isClipboardDirty = this.state.isColRowDirtyingClipboard(cmd.position === "before" ? cmd.base : cmd.base + 1, cmd.dimension);
@@ -43440,7 +43498,7 @@
43440
43498
  case "REMOVE_COLUMNS_ROWS": {
43441
43499
  this.status = "invisible";
43442
43500
  // If we remove a col/row inside or before the cut area, we invalidate the clipboard
43443
- if (this.state?.operation !== "CUT") {
43501
+ if (this.state?.operation !== "CUT" || cmd.sheetId !== this.state?.sheetId) {
43444
43502
  return;
43445
43503
  }
43446
43504
  for (let el of cmd.elements) {
@@ -46515,9 +46573,11 @@
46515
46573
  if (ev.key === "Enter") {
46516
46574
  ev.preventDefault();
46517
46575
  this.stopEdition();
46576
+ this.env.focusableElement.focus();
46518
46577
  }
46519
46578
  if (ev.key === "Escape") {
46520
46579
  this.cancelEdition();
46580
+ this.env.focusableElement.focus();
46521
46581
  }
46522
46582
  }
46523
46583
  onClickSheetName(ev) {
@@ -48279,6 +48339,10 @@
48279
48339
  });
48280
48340
  owl.useExternalListener(window, "resize", () => this.render(true));
48281
48341
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
48342
+ // For some reason, the wheel event is not properly registered inside templates
48343
+ // in Chromium-based browsers based on chromium 125
48344
+ // This hack ensures the event declared in the template is properly registered/working
48345
+ owl.useExternalListener(document.body, "wheel", () => { });
48282
48346
  this.bindModelEvents();
48283
48347
  owl.onMounted(() => {
48284
48348
  this.checkViewportSize();
@@ -51129,7 +51193,11 @@
51129
51193
  let cellNode = escapeXml ``;
51130
51194
  // Either formula or static value inside the cell
51131
51195
  if (cell.isFormula) {
51132
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
51196
+ const res = addFormula(cell);
51197
+ if (!res) {
51198
+ continue;
51199
+ }
51200
+ ({ attrs: additionalAttrs, node: cellNode } = res);
51133
51201
  }
51134
51202
  else if (cell.content && isMarkdownLink(cell.content)) {
51135
51203
  const { label } = parseMarkdownLink(cell.content);
@@ -52126,9 +52194,9 @@
52126
52194
  Object.defineProperty(exports, '__esModule', { value: true });
52127
52195
 
52128
52196
 
52129
- __info__.version = '16.4.33';
52130
- __info__.date = '2024-05-15T09:17:20.683Z';
52131
- __info__.hash = '53b07e5';
52197
+ __info__.version = '16.4.36';
52198
+ __info__.date = '2024-06-10T09:33:43.623Z';
52199
+ __info__.hash = '3b15ed5';
52132
52200
 
52133
52201
 
52134
52202
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);