@odoo/o-spreadsheet 16.3.36 → 16.3.37

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.
@@ -2488,8 +2488,11 @@ function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
2488
2488
  /**
2489
2489
  * Create a range from a xc. If the xc is empty, this function returns undefined.
2490
2490
  */
2491
- function createRange(getters, sheetId, range) {
2492
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
2491
+ function createValidRange(getters, sheetId, xc) {
2492
+ if (!xc)
2493
+ return;
2494
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
2495
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
2493
2496
  }
2494
2497
 
2495
2498
  /** Methods from Odoo Web Utils */
@@ -5262,6 +5265,7 @@ const FilterMenuPopoverBuilder = {
5262
5265
  };
5263
5266
 
5264
5267
  const macRegex = /Mac/i;
5268
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
5265
5269
  /**
5266
5270
  * Return true if the event was triggered from
5267
5271
  * a child element.
@@ -5308,6 +5312,31 @@ function setElementScrollTop(el, scroll) {
5308
5312
  function getOpenedMenus() {
5309
5313
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5310
5314
  }
5315
+ const letterRegex = /^[a-zA-Z]$/;
5316
+ /**
5317
+ * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
5318
+ *
5319
+ * @argument ev - The keyboard event to transform
5320
+ * @argument mode - Use either ev.key of ev.code to get the string shortcut
5321
+ *
5322
+ * @example
5323
+ * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
5324
+ * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
5325
+ */
5326
+ function keyboardEventToShortcutString(ev, mode = "key") {
5327
+ let keyDownString = "";
5328
+ if (!MODIFIER_KEYS.includes(ev.key)) {
5329
+ if (isCtrlKey(ev))
5330
+ keyDownString += "Ctrl+";
5331
+ if (ev.altKey)
5332
+ keyDownString += "Alt+";
5333
+ if (ev.shiftKey)
5334
+ keyDownString += "Shift+";
5335
+ }
5336
+ const key = mode === "key" ? ev.key : ev.code;
5337
+ keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
5338
+ return keyDownString;
5339
+ }
5311
5340
  function isMacOS() {
5312
5341
  return Boolean(macRegex.test(navigator.userAgent));
5313
5342
  }
@@ -7027,7 +7056,7 @@ class BarChart extends AbstractChart {
7027
7056
  constructor(definition, sheetId, getters) {
7028
7057
  super(definition, sheetId, getters);
7029
7058
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7030
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
7059
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
7031
7060
  this.background = definition.background;
7032
7061
  this.verticalAxisPosition = definition.verticalAxisPosition;
7033
7062
  this.legendPosition = definition.legendPosition;
@@ -7271,7 +7300,7 @@ class GaugeChart extends AbstractChart {
7271
7300
  type = "gauge";
7272
7301
  constructor(definition, sheetId, getters) {
7273
7302
  super(definition, sheetId, getters);
7274
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
7303
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
7275
7304
  this.sectionRule = definition.sectionRule;
7276
7305
  this.background = definition.background;
7277
7306
  }
@@ -7607,7 +7636,7 @@ class LineChart extends AbstractChart {
7607
7636
  constructor(definition, sheetId, getters) {
7608
7637
  super(definition, sheetId, getters);
7609
7638
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7610
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
7639
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
7611
7640
  this.background = definition.background;
7612
7641
  this.verticalAxisPosition = definition.verticalAxisPosition;
7613
7642
  this.legendPosition = definition.legendPosition;
@@ -7903,7 +7932,7 @@ class PieChart extends AbstractChart {
7903
7932
  constructor(definition, sheetId, getters) {
7904
7933
  super(definition, sheetId, getters);
7905
7934
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7906
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
7935
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
7907
7936
  this.background = definition.background;
7908
7937
  this.legendPosition = definition.legendPosition;
7909
7938
  this.aggregated = definition.aggregated;
@@ -8070,8 +8099,8 @@ class ScorecardChart extends AbstractChart {
8070
8099
  type = "scorecard";
8071
8100
  constructor(definition, sheetId, getters) {
8072
8101
  super(definition, sheetId, getters);
8073
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
8074
- this.baseline = createRange(getters, sheetId, definition.baseline);
8102
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
8103
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
8075
8104
  this.baselineMode = definition.baselineMode;
8076
8105
  this.baselineDescr = definition.baselineDescr;
8077
8106
  this.background = definition.background;
@@ -18102,7 +18131,7 @@ class LineBarPieConfigPanel extends owl.Component {
18102
18131
  }
18103
18132
  const getters = this.env.model.getters;
18104
18133
  const sheetId = getters.getActiveSheetId();
18105
- const labelRange = createRange(getters, sheetId, this.labelRange);
18134
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
18106
18135
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
18107
18136
  if (dataSets.length) {
18108
18137
  return dataSets[0].dataRange.zone.top + 1;
@@ -20266,14 +20295,14 @@ class FigureComponent extends owl.Component {
20266
20295
  }
20267
20296
  onKeyDown(ev) {
20268
20297
  const figure = this.props.figure;
20269
- switch (ev.key) {
20298
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
20299
+ switch (keyDownShortcut) {
20270
20300
  case "Delete":
20271
20301
  this.env.model.dispatch("DELETE_FIGURE", {
20272
20302
  sheetId: this.env.model.getters.getActiveSheetId(),
20273
20303
  id: figure.id,
20274
20304
  });
20275
20305
  this.props.onFigureDeleted();
20276
- ev.stopPropagation();
20277
20306
  ev.preventDefault();
20278
20307
  ev.stopPropagation();
20279
20308
  break;
@@ -20294,7 +20323,22 @@ class FigureComponent extends owl.Component {
20294
20323
  x: figure.x + delta[0],
20295
20324
  y: figure.y + delta[1],
20296
20325
  });
20326
+ ev.preventDefault();
20297
20327
  ev.stopPropagation();
20328
+ break;
20329
+ case "Ctrl+A":
20330
+ // Maybe in the future we will implement a way to select all figures
20331
+ ev.preventDefault();
20332
+ ev.stopPropagation();
20333
+ break;
20334
+ case "Ctrl+Y":
20335
+ case "Ctrl+Z":
20336
+ if (keyDownShortcut === "Ctrl+Y") {
20337
+ this.env.model.dispatch("REQUEST_REDO");
20338
+ }
20339
+ else if (keyDownShortcut === "Ctrl+Z") {
20340
+ this.env.model.dispatch("REQUEST_UNDO");
20341
+ }
20298
20342
  ev.preventDefault();
20299
20343
  ev.stopPropagation();
20300
20344
  break;
@@ -32516,6 +32560,9 @@ class RangeAdapter {
32516
32560
  if (range.invalidXc) {
32517
32561
  return range.invalidXc;
32518
32562
  }
32563
+ if (!this.getters.tryGetSheet(range.sheetId)) {
32564
+ return INCORRECT_RANGE_STRING;
32565
+ }
32519
32566
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
32520
32567
  return INCORRECT_RANGE_STRING;
32521
32568
  }
@@ -42529,9 +42576,11 @@ class BottomBarSheet extends owl.Component {
42529
42576
  if (ev.key === "Enter") {
42530
42577
  ev.preventDefault();
42531
42578
  this.stopEdition();
42579
+ this.env.focusableElement.focus();
42532
42580
  }
42533
42581
  if (ev.key === "Escape") {
42534
42582
  this.cancelEdition();
42583
+ this.env.focusableElement.focus();
42535
42584
  }
42536
42585
  }
42537
42586
  onClickSheetName(ev) {
@@ -46319,6 +46368,12 @@ function addFormula(cell) {
46319
46368
  else {
46320
46369
  // Shouldn't we always output the value then ?
46321
46370
  const value = cell.value;
46371
+ // If the cell contains a non-exported formula and that is evaluates to
46372
+ // nothing* ,we don't export it.
46373
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
46374
+ // the empty string.
46375
+ if (value === "")
46376
+ return undefined;
46322
46377
  const type = getCellType(value);
46323
46378
  attrs.push(["t", type]);
46324
46379
  node = escapeXml /*xml*/ `<v>${value}</v>`;
@@ -47122,7 +47177,11 @@ function addRows(construct, data, sheet) {
47122
47177
  let cellNode = escapeXml ``;
47123
47178
  // Either formula or static value inside the cell
47124
47179
  if (cell.isFormula) {
47125
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
47180
+ const res = addFormula(cell);
47181
+ if (!res) {
47182
+ continue;
47183
+ }
47184
+ ({ attrs: additionalAttrs, node: cellNode } = res);
47126
47185
  }
47127
47186
  else if (cell.content && isMarkdownLink(cell.content)) {
47128
47187
  const { label } = parseMarkdownLink(cell.content);
@@ -48103,6 +48162,6 @@ exports.setTranslationMethod = setTranslationMethod;
48103
48162
  exports.tokenize = tokenize;
48104
48163
 
48105
48164
 
48106
- __info__.version = '16.3.36';
48107
- __info__.date = '2024-05-24T11:25:00.243Z';
48108
- __info__.hash = 'b038fad';
48165
+ __info__.version = '16.3.37';
48166
+ __info__.date = '2024-06-03T14:49:37.305Z';
48167
+ __info__.hash = 'e7bf8e1';
@@ -2484,8 +2484,11 @@ function copyRangeWithNewSheetId(sheetIdFrom, sheetIdTo, range) {
2484
2484
  /**
2485
2485
  * Create a range from a xc. If the xc is empty, this function returns undefined.
2486
2486
  */
2487
- function createRange(getters, sheetId, range) {
2488
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
2487
+ function createValidRange(getters, sheetId, xc) {
2488
+ if (!xc)
2489
+ return;
2490
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
2491
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
2489
2492
  }
2490
2493
 
2491
2494
  /** Methods from Odoo Web Utils */
@@ -5258,6 +5261,7 @@ const FilterMenuPopoverBuilder = {
5258
5261
  };
5259
5262
 
5260
5263
  const macRegex = /Mac/i;
5264
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
5261
5265
  /**
5262
5266
  * Return true if the event was triggered from
5263
5267
  * a child element.
@@ -5304,6 +5308,31 @@ function setElementScrollTop(el, scroll) {
5304
5308
  function getOpenedMenus() {
5305
5309
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5306
5310
  }
5311
+ const letterRegex = /^[a-zA-Z]$/;
5312
+ /**
5313
+ * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
5314
+ *
5315
+ * @argument ev - The keyboard event to transform
5316
+ * @argument mode - Use either ev.key of ev.code to get the string shortcut
5317
+ *
5318
+ * @example
5319
+ * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
5320
+ * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
5321
+ */
5322
+ function keyboardEventToShortcutString(ev, mode = "key") {
5323
+ let keyDownString = "";
5324
+ if (!MODIFIER_KEYS.includes(ev.key)) {
5325
+ if (isCtrlKey(ev))
5326
+ keyDownString += "Ctrl+";
5327
+ if (ev.altKey)
5328
+ keyDownString += "Alt+";
5329
+ if (ev.shiftKey)
5330
+ keyDownString += "Shift+";
5331
+ }
5332
+ const key = mode === "key" ? ev.key : ev.code;
5333
+ keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
5334
+ return keyDownString;
5335
+ }
5307
5336
  function isMacOS() {
5308
5337
  return Boolean(macRegex.test(navigator.userAgent));
5309
5338
  }
@@ -7023,7 +7052,7 @@ class BarChart extends AbstractChart {
7023
7052
  constructor(definition, sheetId, getters) {
7024
7053
  super(definition, sheetId, getters);
7025
7054
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7026
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
7055
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
7027
7056
  this.background = definition.background;
7028
7057
  this.verticalAxisPosition = definition.verticalAxisPosition;
7029
7058
  this.legendPosition = definition.legendPosition;
@@ -7267,7 +7296,7 @@ class GaugeChart extends AbstractChart {
7267
7296
  type = "gauge";
7268
7297
  constructor(definition, sheetId, getters) {
7269
7298
  super(definition, sheetId, getters);
7270
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
7299
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
7271
7300
  this.sectionRule = definition.sectionRule;
7272
7301
  this.background = definition.background;
7273
7302
  }
@@ -7603,7 +7632,7 @@ class LineChart extends AbstractChart {
7603
7632
  constructor(definition, sheetId, getters) {
7604
7633
  super(definition, sheetId, getters);
7605
7634
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7606
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
7635
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
7607
7636
  this.background = definition.background;
7608
7637
  this.verticalAxisPosition = definition.verticalAxisPosition;
7609
7638
  this.legendPosition = definition.legendPosition;
@@ -7899,7 +7928,7 @@ class PieChart extends AbstractChart {
7899
7928
  constructor(definition, sheetId, getters) {
7900
7929
  super(definition, sheetId, getters);
7901
7930
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7902
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
7931
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
7903
7932
  this.background = definition.background;
7904
7933
  this.legendPosition = definition.legendPosition;
7905
7934
  this.aggregated = definition.aggregated;
@@ -8066,8 +8095,8 @@ class ScorecardChart extends AbstractChart {
8066
8095
  type = "scorecard";
8067
8096
  constructor(definition, sheetId, getters) {
8068
8097
  super(definition, sheetId, getters);
8069
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
8070
- this.baseline = createRange(getters, sheetId, definition.baseline);
8098
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
8099
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
8071
8100
  this.baselineMode = definition.baselineMode;
8072
8101
  this.baselineDescr = definition.baselineDescr;
8073
8102
  this.background = definition.background;
@@ -18098,7 +18127,7 @@ class LineBarPieConfigPanel extends Component {
18098
18127
  }
18099
18128
  const getters = this.env.model.getters;
18100
18129
  const sheetId = getters.getActiveSheetId();
18101
- const labelRange = createRange(getters, sheetId, this.labelRange);
18130
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
18102
18131
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
18103
18132
  if (dataSets.length) {
18104
18133
  return dataSets[0].dataRange.zone.top + 1;
@@ -20262,14 +20291,14 @@ class FigureComponent extends Component {
20262
20291
  }
20263
20292
  onKeyDown(ev) {
20264
20293
  const figure = this.props.figure;
20265
- switch (ev.key) {
20294
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
20295
+ switch (keyDownShortcut) {
20266
20296
  case "Delete":
20267
20297
  this.env.model.dispatch("DELETE_FIGURE", {
20268
20298
  sheetId: this.env.model.getters.getActiveSheetId(),
20269
20299
  id: figure.id,
20270
20300
  });
20271
20301
  this.props.onFigureDeleted();
20272
- ev.stopPropagation();
20273
20302
  ev.preventDefault();
20274
20303
  ev.stopPropagation();
20275
20304
  break;
@@ -20290,7 +20319,22 @@ class FigureComponent extends Component {
20290
20319
  x: figure.x + delta[0],
20291
20320
  y: figure.y + delta[1],
20292
20321
  });
20322
+ ev.preventDefault();
20293
20323
  ev.stopPropagation();
20324
+ break;
20325
+ case "Ctrl+A":
20326
+ // Maybe in the future we will implement a way to select all figures
20327
+ ev.preventDefault();
20328
+ ev.stopPropagation();
20329
+ break;
20330
+ case "Ctrl+Y":
20331
+ case "Ctrl+Z":
20332
+ if (keyDownShortcut === "Ctrl+Y") {
20333
+ this.env.model.dispatch("REQUEST_REDO");
20334
+ }
20335
+ else if (keyDownShortcut === "Ctrl+Z") {
20336
+ this.env.model.dispatch("REQUEST_UNDO");
20337
+ }
20294
20338
  ev.preventDefault();
20295
20339
  ev.stopPropagation();
20296
20340
  break;
@@ -32512,6 +32556,9 @@ class RangeAdapter {
32512
32556
  if (range.invalidXc) {
32513
32557
  return range.invalidXc;
32514
32558
  }
32559
+ if (!this.getters.tryGetSheet(range.sheetId)) {
32560
+ return INCORRECT_RANGE_STRING;
32561
+ }
32515
32562
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
32516
32563
  return INCORRECT_RANGE_STRING;
32517
32564
  }
@@ -42525,9 +42572,11 @@ class BottomBarSheet extends Component {
42525
42572
  if (ev.key === "Enter") {
42526
42573
  ev.preventDefault();
42527
42574
  this.stopEdition();
42575
+ this.env.focusableElement.focus();
42528
42576
  }
42529
42577
  if (ev.key === "Escape") {
42530
42578
  this.cancelEdition();
42579
+ this.env.focusableElement.focus();
42531
42580
  }
42532
42581
  }
42533
42582
  onClickSheetName(ev) {
@@ -46315,6 +46364,12 @@ function addFormula(cell) {
46315
46364
  else {
46316
46365
  // Shouldn't we always output the value then ?
46317
46366
  const value = cell.value;
46367
+ // If the cell contains a non-exported formula and that is evaluates to
46368
+ // nothing* ,we don't export it.
46369
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
46370
+ // the empty string.
46371
+ if (value === "")
46372
+ return undefined;
46318
46373
  const type = getCellType(value);
46319
46374
  attrs.push(["t", type]);
46320
46375
  node = escapeXml /*xml*/ `<v>${value}</v>`;
@@ -47118,7 +47173,11 @@ function addRows(construct, data, sheet) {
47118
47173
  let cellNode = escapeXml ``;
47119
47174
  // Either formula or static value inside the cell
47120
47175
  if (cell.isFormula) {
47121
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
47176
+ const res = addFormula(cell);
47177
+ if (!res) {
47178
+ continue;
47179
+ }
47180
+ ({ attrs: additionalAttrs, node: cellNode } = res);
47122
47181
  }
47123
47182
  else if (cell.content && isMarkdownLink(cell.content)) {
47124
47183
  const { label } = parseMarkdownLink(cell.content);
@@ -48071,6 +48130,6 @@ function addFunction(functionName, functionDescription) {
48071
48130
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateEvaluationCommands, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
48072
48131
 
48073
48132
 
48074
- __info__.version = '16.3.36';
48075
- __info__.date = '2024-05-24T11:25:00.243Z';
48076
- __info__.hash = 'b038fad';
48133
+ __info__.version = '16.3.37';
48134
+ __info__.date = '2024-06-03T14:49:37.305Z';
48135
+ __info__.hash = 'e7bf8e1';
@@ -2485,8 +2485,11 @@
2485
2485
  /**
2486
2486
  * Create a range from a xc. If the xc is empty, this function returns undefined.
2487
2487
  */
2488
- function createRange(getters, sheetId, range) {
2489
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
2488
+ function createValidRange(getters, sheetId, xc) {
2489
+ if (!xc)
2490
+ return;
2491
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
2492
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
2490
2493
  }
2491
2494
 
2492
2495
  /** Methods from Odoo Web Utils */
@@ -5259,6 +5262,7 @@
5259
5262
  };
5260
5263
 
5261
5264
  const macRegex = /Mac/i;
5265
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
5262
5266
  /**
5263
5267
  * Return true if the event was triggered from
5264
5268
  * a child element.
@@ -5305,6 +5309,31 @@
5305
5309
  function getOpenedMenus() {
5306
5310
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5307
5311
  }
5312
+ const letterRegex = /^[a-zA-Z]$/;
5313
+ /**
5314
+ * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
5315
+ *
5316
+ * @argument ev - The keyboard event to transform
5317
+ * @argument mode - Use either ev.key of ev.code to get the string shortcut
5318
+ *
5319
+ * @example
5320
+ * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
5321
+ * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
5322
+ */
5323
+ function keyboardEventToShortcutString(ev, mode = "key") {
5324
+ let keyDownString = "";
5325
+ if (!MODIFIER_KEYS.includes(ev.key)) {
5326
+ if (isCtrlKey(ev))
5327
+ keyDownString += "Ctrl+";
5328
+ if (ev.altKey)
5329
+ keyDownString += "Alt+";
5330
+ if (ev.shiftKey)
5331
+ keyDownString += "Shift+";
5332
+ }
5333
+ const key = mode === "key" ? ev.key : ev.code;
5334
+ keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
5335
+ return keyDownString;
5336
+ }
5308
5337
  function isMacOS() {
5309
5338
  return Boolean(macRegex.test(navigator.userAgent));
5310
5339
  }
@@ -7024,7 +7053,7 @@
7024
7053
  constructor(definition, sheetId, getters) {
7025
7054
  super(definition, sheetId, getters);
7026
7055
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7027
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
7056
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
7028
7057
  this.background = definition.background;
7029
7058
  this.verticalAxisPosition = definition.verticalAxisPosition;
7030
7059
  this.legendPosition = definition.legendPosition;
@@ -7268,7 +7297,7 @@
7268
7297
  type = "gauge";
7269
7298
  constructor(definition, sheetId, getters) {
7270
7299
  super(definition, sheetId, getters);
7271
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
7300
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
7272
7301
  this.sectionRule = definition.sectionRule;
7273
7302
  this.background = definition.background;
7274
7303
  }
@@ -7604,7 +7633,7 @@
7604
7633
  constructor(definition, sheetId, getters) {
7605
7634
  super(definition, sheetId, getters);
7606
7635
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7607
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
7636
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
7608
7637
  this.background = definition.background;
7609
7638
  this.verticalAxisPosition = definition.verticalAxisPosition;
7610
7639
  this.legendPosition = definition.legendPosition;
@@ -7900,7 +7929,7 @@
7900
7929
  constructor(definition, sheetId, getters) {
7901
7930
  super(definition, sheetId, getters);
7902
7931
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
7903
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
7932
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
7904
7933
  this.background = definition.background;
7905
7934
  this.legendPosition = definition.legendPosition;
7906
7935
  this.aggregated = definition.aggregated;
@@ -8067,8 +8096,8 @@
8067
8096
  type = "scorecard";
8068
8097
  constructor(definition, sheetId, getters) {
8069
8098
  super(definition, sheetId, getters);
8070
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
8071
- this.baseline = createRange(getters, sheetId, definition.baseline);
8099
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
8100
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
8072
8101
  this.baselineMode = definition.baselineMode;
8073
8102
  this.baselineDescr = definition.baselineDescr;
8074
8103
  this.background = definition.background;
@@ -18099,7 +18128,7 @@
18099
18128
  }
18100
18129
  const getters = this.env.model.getters;
18101
18130
  const sheetId = getters.getActiveSheetId();
18102
- const labelRange = createRange(getters, sheetId, this.labelRange);
18131
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
18103
18132
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
18104
18133
  if (dataSets.length) {
18105
18134
  return dataSets[0].dataRange.zone.top + 1;
@@ -20263,14 +20292,14 @@
20263
20292
  }
20264
20293
  onKeyDown(ev) {
20265
20294
  const figure = this.props.figure;
20266
- switch (ev.key) {
20295
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
20296
+ switch (keyDownShortcut) {
20267
20297
  case "Delete":
20268
20298
  this.env.model.dispatch("DELETE_FIGURE", {
20269
20299
  sheetId: this.env.model.getters.getActiveSheetId(),
20270
20300
  id: figure.id,
20271
20301
  });
20272
20302
  this.props.onFigureDeleted();
20273
- ev.stopPropagation();
20274
20303
  ev.preventDefault();
20275
20304
  ev.stopPropagation();
20276
20305
  break;
@@ -20291,7 +20320,22 @@
20291
20320
  x: figure.x + delta[0],
20292
20321
  y: figure.y + delta[1],
20293
20322
  });
20323
+ ev.preventDefault();
20294
20324
  ev.stopPropagation();
20325
+ break;
20326
+ case "Ctrl+A":
20327
+ // Maybe in the future we will implement a way to select all figures
20328
+ ev.preventDefault();
20329
+ ev.stopPropagation();
20330
+ break;
20331
+ case "Ctrl+Y":
20332
+ case "Ctrl+Z":
20333
+ if (keyDownShortcut === "Ctrl+Y") {
20334
+ this.env.model.dispatch("REQUEST_REDO");
20335
+ }
20336
+ else if (keyDownShortcut === "Ctrl+Z") {
20337
+ this.env.model.dispatch("REQUEST_UNDO");
20338
+ }
20295
20339
  ev.preventDefault();
20296
20340
  ev.stopPropagation();
20297
20341
  break;
@@ -32513,6 +32557,9 @@
32513
32557
  if (range.invalidXc) {
32514
32558
  return range.invalidXc;
32515
32559
  }
32560
+ if (!this.getters.tryGetSheet(range.sheetId)) {
32561
+ return INCORRECT_RANGE_STRING;
32562
+ }
32516
32563
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
32517
32564
  return INCORRECT_RANGE_STRING;
32518
32565
  }
@@ -42526,9 +42573,11 @@
42526
42573
  if (ev.key === "Enter") {
42527
42574
  ev.preventDefault();
42528
42575
  this.stopEdition();
42576
+ this.env.focusableElement.focus();
42529
42577
  }
42530
42578
  if (ev.key === "Escape") {
42531
42579
  this.cancelEdition();
42580
+ this.env.focusableElement.focus();
42532
42581
  }
42533
42582
  }
42534
42583
  onClickSheetName(ev) {
@@ -46316,6 +46365,12 @@
46316
46365
  else {
46317
46366
  // Shouldn't we always output the value then ?
46318
46367
  const value = cell.value;
46368
+ // If the cell contains a non-exported formula and that is evaluates to
46369
+ // nothing* ,we don't export it.
46370
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
46371
+ // the empty string.
46372
+ if (value === "")
46373
+ return undefined;
46319
46374
  const type = getCellType(value);
46320
46375
  attrs.push(["t", type]);
46321
46376
  node = escapeXml /*xml*/ `<v>${value}</v>`;
@@ -47119,7 +47174,11 @@
47119
47174
  let cellNode = escapeXml ``;
47120
47175
  // Either formula or static value inside the cell
47121
47176
  if (cell.isFormula) {
47122
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
47177
+ const res = addFormula(cell);
47178
+ if (!res) {
47179
+ continue;
47180
+ }
47181
+ ({ attrs: additionalAttrs, node: cellNode } = res);
47123
47182
  }
47124
47183
  else if (cell.content && isMarkdownLink(cell.content)) {
47125
47184
  const { label } = parseMarkdownLink(cell.content);
@@ -48102,9 +48161,9 @@
48102
48161
  Object.defineProperty(exports, '__esModule', { value: true });
48103
48162
 
48104
48163
 
48105
- __info__.version = '16.3.36';
48106
- __info__.date = '2024-05-24T11:25:00.243Z';
48107
- __info__.hash = 'b038fad';
48164
+ __info__.version = '16.3.37';
48165
+ __info__.date = '2024-06-03T14:49:37.305Z';
48166
+ __info__.hash = 'e7bf8e1';
48108
48167
 
48109
48168
 
48110
48169
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);