@odoo/o-spreadsheet 17.1.16 → 17.1.18

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.16
6
- * @date 2024-05-24T11:27:23.083Z
7
- * @hash 3a41c3a
5
+ * @version 17.1.18
6
+ * @date 2024-06-10T09:34:55.045Z
7
+ * @hash 0e756ce
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -294,7 +294,10 @@
294
294
  * Check if the object is a plain old javascript object.
295
295
  */
296
296
  function isPlainObject(obj) {
297
- return typeof obj === "object" && obj?.constructor === Object;
297
+ return (typeof obj === "object" &&
298
+ obj !== null &&
299
+ // obj.constructor can be undefined when there's no prototype (`Object.create(null, {})`)
300
+ (obj?.constructor === Object || obj?.constructor === undefined));
298
301
  }
299
302
  /**
300
303
  * Sanitize the name of a sheet, by eventually removing quotes
@@ -2606,7 +2609,7 @@
2606
2609
  return false;
2607
2610
  }
2608
2611
  if (typeof operand === "number" && operator === "=") {
2609
- return toString(value) === toString(operand);
2612
+ return value.toString() === operand.toString();
2610
2613
  }
2611
2614
  if (operator === "<>" || operator === "=") {
2612
2615
  let result;
@@ -2666,14 +2669,13 @@
2666
2669
  if (countArg % 2 === 1) {
2667
2670
  throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects criteria_range and criterion to be in pairs."));
2668
2671
  }
2669
- const dimRow = args[0].length;
2670
- const dimCol = args[0][0].length;
2672
+ const firstArg = toMatrix(args[0]);
2673
+ const dimRow = firstArg.length;
2674
+ const dimCol = firstArg[0].length;
2671
2675
  let predicates = [];
2672
2676
  for (let i = 0; i < countArg - 1; i += 2) {
2673
- const criteriaRange = args[i];
2674
- if (!isMatrix(criteriaRange) ||
2675
- criteriaRange.length !== dimRow ||
2676
- criteriaRange[0].length !== dimCol) {
2677
+ const criteriaRange = toMatrix(args[i]);
2678
+ if (criteriaRange.length !== dimRow || criteriaRange[0].length !== dimCol) {
2677
2679
  throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects criteria_range to have the same dimension"));
2678
2680
  }
2679
2681
  const description = toString(args[i + 1]);
@@ -2683,7 +2685,7 @@
2683
2685
  for (let j = 0; j < dimCol; j++) {
2684
2686
  let validatedPredicates = true;
2685
2687
  for (let k = 0; k < countArg - 1; k += 2) {
2686
- const criteriaValue = args[k][i][j].value;
2688
+ const criteriaValue = toMatrix(args[k])[i][j].value;
2687
2689
  const criterion = predicates[k / 2];
2688
2690
  validatedPredicates = evaluatePredicate(criteriaValue ?? undefined, criterion);
2689
2691
  if (!validatedPredicates) {
@@ -4371,8 +4373,11 @@
4371
4373
  /**
4372
4374
  * Create a range from a xc. If the xc is empty, this function returns undefined.
4373
4375
  */
4374
- function createRange(getters, sheetId, range) {
4375
- return range ? getters.getRangeFromSheetXC(sheetId, range) : undefined;
4376
+ function createValidRange(getters, sheetId, xc) {
4377
+ if (!xc)
4378
+ return;
4379
+ const range = getters.getRangeFromSheetXC(sheetId, xc);
4380
+ return !(range.invalidSheetName || range.invalidXc) ? range : undefined;
4376
4381
  }
4377
4382
  /**
4378
4383
  * Spread multiple colrows zone to one row/col zone and add a many new input range as needed.
@@ -5303,8 +5308,8 @@
5303
5308
  type = "scorecard";
5304
5309
  constructor(definition, sheetId, getters) {
5305
5310
  super(definition, sheetId, getters);
5306
- this.keyValue = createRange(getters, sheetId, definition.keyValue);
5307
- this.baseline = createRange(getters, sheetId, definition.baseline);
5311
+ this.keyValue = createValidRange(getters, sheetId, definition.keyValue);
5312
+ this.baseline = createValidRange(getters, sheetId, definition.baseline);
5308
5313
  this.baselineMode = definition.baselineMode;
5309
5314
  this.baselineDescr = definition.baselineDescr;
5310
5315
  this.background = definition.background;
@@ -6838,8 +6843,7 @@
6838
6843
  */
6839
6844
  function useSpreadsheetRect() {
6840
6845
  const position = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
6841
- let spreadsheetElement = document.querySelector(".o-spreadsheet");
6842
- updatePosition();
6846
+ let spreadsheetElement = null;
6843
6847
  function updatePosition() {
6844
6848
  if (!spreadsheetElement) {
6845
6849
  spreadsheetElement = document.querySelector(".o-spreadsheet");
@@ -8603,7 +8607,7 @@
8603
8607
  if ("chartJsConfig" in runtime) {
8604
8608
  runtime.chartJsConfig.plugins = [backgroundColorChartJSPlugin];
8605
8609
  // @ts-ignore
8606
- const chart = new window.Chart(canvas, runtime.chartJsConfig);
8610
+ const chart = new window.Chart(canvas, deepCopy(runtime.chartJsConfig));
8607
8611
  const imgContent = chart.toBase64Image();
8608
8612
  chart.destroy();
8609
8613
  div.remove();
@@ -8647,7 +8651,7 @@
8647
8651
  constructor(definition, sheetId, getters) {
8648
8652
  super(definition, sheetId, getters);
8649
8653
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
8650
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
8654
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
8651
8655
  this.background = definition.background;
8652
8656
  this.verticalAxisPosition = definition.verticalAxisPosition;
8653
8657
  this.legendPosition = definition.legendPosition;
@@ -8890,7 +8894,7 @@
8890
8894
  type = "gauge";
8891
8895
  constructor(definition, sheetId, getters) {
8892
8896
  super(definition, sheetId, getters);
8893
- this.dataRange = createRange(this.getters, this.sheetId, definition.dataRange);
8897
+ this.dataRange = createValidRange(this.getters, this.sheetId, definition.dataRange);
8894
8898
  this.sectionRule = definition.sectionRule;
8895
8899
  this.background = definition.background;
8896
8900
  }
@@ -9224,7 +9228,7 @@
9224
9228
  constructor(definition, sheetId, getters) {
9225
9229
  super(definition, sheetId, getters);
9226
9230
  this.dataSets = createDataSets(this.getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
9227
- this.labelRange = createRange(this.getters, sheetId, definition.labelRange);
9231
+ this.labelRange = createValidRange(this.getters, sheetId, definition.labelRange);
9228
9232
  this.background = definition.background;
9229
9233
  this.verticalAxisPosition = definition.verticalAxisPosition;
9230
9234
  this.legendPosition = definition.legendPosition;
@@ -9544,7 +9548,7 @@
9544
9548
  constructor(definition, sheetId, getters) {
9545
9549
  super(definition, sheetId, getters);
9546
9550
  this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
9547
- this.labelRange = createRange(getters, sheetId, definition.labelRange);
9551
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
9548
9552
  this.background = definition.background;
9549
9553
  this.legendPosition = definition.legendPosition;
9550
9554
  this.aggregated = definition.aggregated;
@@ -11188,6 +11192,9 @@
11188
11192
  if (types.some((t) => t.startsWith("RANGE"))) {
11189
11193
  result.acceptMatrix = true;
11190
11194
  }
11195
+ if (types.every((t) => t.startsWith("RANGE"))) {
11196
+ result.acceptMatrixOnly = true;
11197
+ }
11191
11198
  return result;
11192
11199
  }
11193
11200
  /**
@@ -11469,11 +11476,16 @@
11469
11476
  compute: function (array, ...columns) {
11470
11477
  const _array = toMatrix(array);
11471
11478
  const _columns = flattenRowFirst(columns, (item) => toInteger(item?.value, this.locale));
11472
- assert(() => _columns.every((col) => col > 0 && col <= _array.length), _t("The columns arguments must be between 1 and %s (got %s).", _array.length.toString(), (_columns.find((col) => col <= 0 || col > _array.length) || 0).toString()));
11479
+ const argOutOfRange = _columns.filter((col) => col === 0 || _array.length < Math.abs(col));
11480
+ assert(() => argOutOfRange.length === 0, _t("The columns arguments must be between -%s and %s (got %s), excluding 0.", _array.length.toString(), _array.length.toString(), argOutOfRange.join(",")));
11473
11481
  const result = Array(_columns.length);
11474
11482
  for (let col = 0; col < _columns.length; col++) {
11475
- const colIndex = _columns[col] - 1; // -1 because columns arguments are 1-indexed
11476
- result[col] = _array[colIndex];
11483
+ if (_columns[col] > 0) {
11484
+ result[col] = _array[_columns[col] - 1]; // -1 because columns arguments are 1-indexed
11485
+ }
11486
+ else {
11487
+ result[col] = _array[_array.length + _columns[col]];
11488
+ }
11477
11489
  }
11478
11490
  return result;
11479
11491
  },
@@ -11494,8 +11506,14 @@
11494
11506
  const _array = toMatrix(array);
11495
11507
  const _rows = flattenRowFirst(rows, (item) => toInteger(item?.value, this.locale));
11496
11508
  const _nbColumns = _array.length;
11497
- assert(() => _rows.every((row) => row > 0 && row <= _array[0].length), _t("The rows arguments must be between 1 and %s (got %s).", _array[0].length.toString(), (_rows.find((row) => row <= 0 || row > _array[0].length) || 0).toString()));
11498
- return generateMatrix(_nbColumns, _rows.length, (col, row) => _array[col][_rows[row] - 1]); // -1 because rows arguments are 1-indexed
11509
+ const argOutOfRange = _rows.filter((row) => row === 0 || _array[0].length < Math.abs(row));
11510
+ assert(() => argOutOfRange.length === 0, _t("The rows arguments must be between -%s and %s (got %s), excluding 0.", _array[0].length.toString(), _array[0].length.toString(), argOutOfRange.join(",")));
11511
+ return generateMatrix(_nbColumns, _rows.length, (col, row) => {
11512
+ if (_rows[row] > 0) {
11513
+ return _array[col][_rows[row] - 1]; // -1 because columns arguments are 1-indexed
11514
+ }
11515
+ return _array[col][_array[col].length + _rows[row]];
11516
+ });
11499
11517
  },
11500
11518
  isExported: true,
11501
11519
  };
@@ -12403,7 +12421,7 @@
12403
12421
  compute: function (range, ...args) {
12404
12422
  let uniqueValues = new Set();
12405
12423
  visitMatchingRanges(args, (i, j) => {
12406
- const data = range[i][j];
12424
+ const data = range[i]?.[j];
12407
12425
  if (isDefined(data)) {
12408
12426
  uniqueValues.add(data.value);
12409
12427
  }
@@ -13033,7 +13051,7 @@
13033
13051
  }
13034
13052
  let sum = 0;
13035
13053
  visitMatchingRanges([criteriaRange, criterion], (i, j) => {
13036
- const value = sumRange[i][j].value;
13054
+ const value = sumRange[i]?.[j]?.value;
13037
13055
  if (typeof value === "number") {
13038
13056
  sum += value;
13039
13057
  }
@@ -13058,7 +13076,7 @@
13058
13076
  compute: function (sumRange, ...criters) {
13059
13077
  let sum = 0;
13060
13078
  visitMatchingRanges(criters, (i, j) => {
13061
- const value = sumRange[i][j].value;
13079
+ const value = sumRange[i]?.[j]?.value;
13062
13080
  if (typeof value === "number") {
13063
13081
  sum += value;
13064
13082
  }
@@ -13605,7 +13623,7 @@
13605
13623
  let count = 0;
13606
13624
  let sum = 0;
13607
13625
  visitMatchingRanges([criteriaRange, criterion], (i, j) => {
13608
- const value = _averageRange[i][j].value;
13626
+ const value = _averageRange[i]?.[j]?.value;
13609
13627
  if (typeof value === "number") {
13610
13628
  count += 1;
13611
13629
  sum += value;
@@ -13634,7 +13652,7 @@
13634
13652
  let count = 0;
13635
13653
  let sum = 0;
13636
13654
  visitMatchingRanges(args, (i, j) => {
13637
- const value = _averageRange[i][j].value;
13655
+ const value = _averageRange[i]?.[j]?.value;
13638
13656
  if (typeof value === "number") {
13639
13657
  count += 1;
13640
13658
  sum += value;
@@ -13939,7 +13957,7 @@
13939
13957
  compute: function (range, ...args) {
13940
13958
  let result = -Infinity;
13941
13959
  visitMatchingRanges(args, (i, j) => {
13942
- const value = range[i][j].value;
13960
+ const value = range[i]?.[j]?.value;
13943
13961
  if (typeof value === "number") {
13944
13962
  result = result < value ? value : result;
13945
13963
  }
@@ -14022,7 +14040,7 @@
14022
14040
  compute: function (range, ...args) {
14023
14041
  let result = Infinity;
14024
14042
  visitMatchingRanges(args, (i, j) => {
14025
- const value = range[i][j].value;
14043
+ const value = range[i]?.[j]?.value;
14026
14044
  if (typeof value === "number") {
14027
14045
  result = result > value ? value : result;
14028
14046
  }
@@ -19799,6 +19817,9 @@
19799
19817
  }
19800
19818
  args[i] = arg[0][0];
19801
19819
  }
19820
+ if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
19821
+ throw new BadExpressionError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be reference to a cell or range.", (i + 1).toString()));
19822
+ }
19802
19823
  }
19803
19824
  return descr.compute.apply(this, args);
19804
19825
  }
@@ -22151,7 +22172,7 @@
22151
22172
  }
22152
22173
  const getters = this.env.model.getters;
22153
22174
  const sheetId = getters.getActiveSheetId();
22154
- const labelRange = createRange(getters, sheetId, this.labelRange);
22175
+ const labelRange = createValidRange(getters, sheetId, this.labelRange);
22155
22176
  const dataSets = createDataSets(getters, this.dataSeriesRanges, sheetId, this.props.definition.dataSetsHaveTitle);
22156
22177
  if (dataSets.length) {
22157
22178
  return dataSets[0].dataRange.zone.top + 1;
@@ -26274,14 +26295,14 @@
26274
26295
  }
26275
26296
  onKeyDown(ev) {
26276
26297
  const figure = this.props.figure;
26277
- switch (ev.key) {
26298
+ const keyDownShortcut = keyboardEventToShortcutString(ev);
26299
+ switch (keyDownShortcut) {
26278
26300
  case "Delete":
26279
26301
  this.env.model.dispatch("DELETE_FIGURE", {
26280
26302
  sheetId: this.env.model.getters.getActiveSheetId(),
26281
26303
  id: figure.id,
26282
26304
  });
26283
26305
  this.props.onFigureDeleted();
26284
- ev.stopPropagation();
26285
26306
  ev.preventDefault();
26286
26307
  ev.stopPropagation();
26287
26308
  break;
@@ -26302,7 +26323,22 @@
26302
26323
  x: figure.x + delta[0],
26303
26324
  y: figure.y + delta[1],
26304
26325
  });
26326
+ ev.preventDefault();
26305
26327
  ev.stopPropagation();
26328
+ break;
26329
+ case "Ctrl+A":
26330
+ // Maybe in the future we will implement a way to select all figures
26331
+ ev.preventDefault();
26332
+ ev.stopPropagation();
26333
+ break;
26334
+ case "Ctrl+Y":
26335
+ case "Ctrl+Z":
26336
+ if (keyDownShortcut === "Ctrl+Y") {
26337
+ this.env.model.dispatch("REQUEST_REDO");
26338
+ }
26339
+ else if (keyDownShortcut === "Ctrl+Z") {
26340
+ this.env.model.dispatch("REQUEST_UNDO");
26341
+ }
26306
26342
  ev.preventDefault();
26307
26343
  ev.stopPropagation();
26308
26344
  break;
@@ -27340,8 +27376,15 @@
27340
27376
  }
27341
27377
  onPaste(ev) {
27342
27378
  if (this.env.model.getters.getEditionMode() !== "inactive") {
27379
+ // let the browser clipboard work
27343
27380
  ev.stopPropagation();
27344
27381
  }
27382
+ else {
27383
+ // the user meant to paste in the sheet, not open the composer with the pasted content
27384
+ // While we're not editing, we still have the focus and should therefore prevent
27385
+ // the native "paste" to occur.
27386
+ ev.preventDefault();
27387
+ }
27345
27388
  }
27346
27389
  /*
27347
27390
  * Triggered automatically by the content-editable between the keydown and key up
@@ -27350,10 +27393,6 @@
27350
27393
  if (!this.shouldProcessInputEvents) {
27351
27394
  return;
27352
27395
  }
27353
- if (ev.inputType === "insertFromPaste" &&
27354
- this.env.model.getters.getEditionMode() === "inactive") {
27355
- return;
27356
- }
27357
27396
  ev.stopPropagation();
27358
27397
  let content;
27359
27398
  if (this.env.model.getters.getEditionMode() === "inactive") {
@@ -27794,10 +27833,7 @@
27794
27833
  }
27795
27834
  get containerStyle() {
27796
27835
  if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27797
- return `
27798
- position: absolute;
27799
- z-index: -1000;
27800
- `;
27836
+ return `z-index: -1000;`;
27801
27837
  }
27802
27838
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27803
27839
  const cell = this.env.model.getters.getActiveCell();
@@ -35763,12 +35799,6 @@
35763
35799
  // detect when an argument need to be evaluated as a meta argument
35764
35800
  const isMeta = argTypes.includes("META");
35765
35801
  const hasRange = argTypes.some((t) => isRangeType(t));
35766
- const isRangeOnly = argTypes.every((t) => isRangeType(t));
35767
- if (isRangeOnly) {
35768
- if (!isRangeInput(currentArg)) {
35769
- throw new BadExpressionError(_t("Function %s expects the parameter %s to be reference to a cell or range, not a %s.", functionName, (i + 1).toString(), currentArg.type.toLowerCase()));
35770
- }
35771
- }
35772
35802
  compiledArgs.push(compileAST(currentArg, isMeta, hasRange, {
35773
35803
  functionName,
35774
35804
  paramIndex: i + 1,
@@ -35939,16 +35969,6 @@
35939
35969
  function isRangeType(type) {
35940
35970
  return type.startsWith("RANGE");
35941
35971
  }
35942
- function isRangeInput(arg) {
35943
- if (arg.type === "REFERENCE") {
35944
- return true;
35945
- }
35946
- if (arg.type === "FUNCALL") {
35947
- const fnDef = functions$1[arg.value.toUpperCase()];
35948
- return fnDef && isRangeType(fnDef.returns[0]);
35949
- }
35950
- return false;
35951
- }
35952
35972
 
35953
35973
  /**
35954
35974
  * Add the following information on tokens:
@@ -39061,6 +39081,9 @@
39061
39081
  if (range.invalidXc) {
39062
39082
  return range.invalidXc;
39063
39083
  }
39084
+ if (!this.getters.tryGetSheet(range.sheetId)) {
39085
+ return CellErrorType.InvalidReference;
39086
+ }
39064
39087
  if (range.zone.bottom - range.zone.top < 0 || range.zone.right - range.zone.left < 0) {
39065
39088
  return CellErrorType.InvalidReference;
39066
39089
  }
@@ -42508,16 +42531,22 @@
42508
42531
  let newContent = undefined;
42509
42532
  let newFormat = undefined;
42510
42533
  let isExported = true;
42534
+ const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
42511
42535
  const formulaCell = this.getCorrespondingFormulaCell(position);
42512
42536
  if (formulaCell) {
42513
42537
  isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
42514
42538
  isFormula = isExported;
42515
42539
  if (!isExported) {
42516
- newContent = value.toString();
42517
- newFormat = evaluatedCell.format;
42540
+ // If the cell contains a non-exported formula and that is evaluates to
42541
+ // nothing* ,we don't export it.
42542
+ // * non-falsy value are relevant and so are 0 and FALSE, which only leaves
42543
+ // the empty string.
42544
+ if (value !== "") {
42545
+ newContent = value.toString();
42546
+ newFormat = evaluatedCell.format;
42547
+ }
42518
42548
  }
42519
42549
  }
42520
- const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
42521
42550
  const exportedCellData = exportedSheetData.cells[xc] || {};
42522
42551
  const format = newFormat
42523
42552
  ? getItemId(newFormat, data.formats)
@@ -52014,9 +52043,11 @@
52014
52043
  if (ev.key === "Enter") {
52015
52044
  ev.preventDefault();
52016
52045
  this.stopEdition();
52046
+ this.env.focusableElement.focus();
52017
52047
  }
52018
52048
  if (ev.key === "Escape") {
52019
52049
  this.cancelEdition();
52050
+ this.env.focusableElement.focus();
52020
52051
  }
52021
52052
  }
52022
52053
  onClickSheetName(ev) {
@@ -56711,7 +56742,11 @@
56711
56742
  let cellNode = escapeXml ``;
56712
56743
  // Either formula or static value inside the cell
56713
56744
  if (cell.isFormula) {
56714
- ({ attrs: additionalAttrs, node: cellNode } = addFormula(cell));
56745
+ const res = addFormula(cell);
56746
+ if (!res) {
56747
+ continue;
56748
+ }
56749
+ ({ attrs: additionalAttrs, node: cellNode } = res);
56715
56750
  }
56716
56751
  else if (cell.content && isMarkdownLink(cell.content)) {
56717
56752
  const { label } = parseMarkdownLink(cell.content);
@@ -57741,9 +57776,9 @@
57741
57776
  exports.tokenize = tokenize;
57742
57777
 
57743
57778
 
57744
- __info__.version = "17.1.16";
57745
- __info__.date = "2024-05-24T11:27:23.083Z";
57746
- __info__.hash = "3a41c3a";
57779
+ __info__.version = "17.1.18";
57780
+ __info__.date = "2024-06-10T09:34:55.045Z";
57781
+ __info__.hash = "0e756ce";
57747
57782
 
57748
57783
 
57749
57784
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);