@odoo/o-spreadsheet 16.2.24 → 16.2.27

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.
@@ -287,15 +287,76 @@
287
287
  // -----------------------------------------------------------------------------
288
288
  // Date Type
289
289
  // -----------------------------------------------------------------------------
290
+ /**
291
+ * A DateTime object that can be used to manipulate spreadsheet dates.
292
+ * Conceptually, a spreadsheet date is simply a number with a date format,
293
+ * and it is timezone-agnostic.
294
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
295
+ */
296
+ class DateTime {
297
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
298
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
299
+ }
300
+ static fromTimestamp(timestamp) {
301
+ const date = new Date(timestamp);
302
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
303
+ }
304
+ static now() {
305
+ const now = new Date();
306
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
307
+ }
308
+ toString() {
309
+ return this.jsDate.toString();
310
+ }
311
+ getTime() {
312
+ return this.jsDate.getTime();
313
+ }
314
+ getFullYear() {
315
+ return this.jsDate.getUTCFullYear();
316
+ }
317
+ getMonth() {
318
+ return this.jsDate.getUTCMonth();
319
+ }
320
+ getDate() {
321
+ return this.jsDate.getUTCDate();
322
+ }
323
+ getDay() {
324
+ return this.jsDate.getUTCDay();
325
+ }
326
+ getHours() {
327
+ return this.jsDate.getUTCHours();
328
+ }
329
+ getMinutes() {
330
+ return this.jsDate.getUTCMinutes();
331
+ }
332
+ getSeconds() {
333
+ return this.jsDate.getUTCSeconds();
334
+ }
335
+ setFullYear(year) {
336
+ this.jsDate.setFullYear(year);
337
+ }
338
+ setDate(date) {
339
+ this.jsDate.setUTCDate(date);
340
+ }
341
+ setHours(hours) {
342
+ this.jsDate.setUTCHours(hours);
343
+ }
344
+ setMinutes(minutes) {
345
+ this.jsDate.setUTCMinutes(minutes);
346
+ }
347
+ setSeconds(seconds) {
348
+ this.jsDate.setUTCSeconds(seconds);
349
+ }
350
+ }
290
351
  // -----------------------------------------------------------------------------
291
352
  // Parsing
292
353
  // -----------------------------------------------------------------------------
293
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
354
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
294
355
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
295
356
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
296
- const CURRENT_YEAR = new Date().getFullYear();
297
- const INITIAL_JS_DAY = new Date(0);
298
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
357
+ const CURRENT_YEAR = DateTime.now().getFullYear();
358
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
359
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
299
360
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
300
361
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
301
362
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -344,7 +405,7 @@
344
405
  return {
345
406
  value: date.value + time.value,
346
407
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
347
- jsDate: new Date(date.jsDate.getFullYear() + time.jsDate.getFullYear() - 1899, date.jsDate.getMonth() + time.jsDate.getMonth() - 11, date.jsDate.getDate() + time.jsDate.getDate() - 30, date.jsDate.getHours() + time.jsDate.getHours(), date.jsDate.getMinutes() + time.jsDate.getMinutes(), date.jsDate.getSeconds() + time.jsDate.getSeconds()),
408
+ jsDate: new DateTime(date.jsDate.getFullYear() + time.jsDate.getFullYear() - 1899, date.jsDate.getMonth() + time.jsDate.getMonth() - 11, date.jsDate.getDate() + time.jsDate.getDate() - 30, date.jsDate.getHours() + time.jsDate.getHours(), date.jsDate.getMinutes() + time.jsDate.getMinutes(), date.jsDate.getSeconds() + time.jsDate.getSeconds()),
348
409
  };
349
410
  }
350
411
  return date || time;
@@ -361,13 +422,13 @@
361
422
  const day = Number(parts[dayIndex]);
362
423
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
363
424
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
364
- const jsDate = new Date(year, month - 1, day);
425
+ const jsDate = new DateTime(year, month - 1, day);
365
426
  const sep = str.match(/\/|-|\s/)[0];
366
427
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
367
428
  // invalid date
368
429
  return null;
369
430
  }
370
- const delta = jsDate - INITIAL_1900_DAY;
431
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
371
432
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
372
433
  if (parts[yearIndex]) {
373
434
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -427,7 +488,7 @@
427
488
  if (hours >= 24) {
428
489
  format = "hhhh:mm:ss";
429
490
  }
430
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
491
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
431
492
  return {
432
493
  value: hours / 24 + minutes / 1440 + seconds / 86400,
433
494
  format: format,
@@ -441,7 +502,7 @@
441
502
  // -----------------------------------------------------------------------------
442
503
  function numberToJsDate(value) {
443
504
  const truncValue = Math.trunc(value);
444
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
505
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
445
506
  let time = value - truncValue;
446
507
  time = time < 0 ? 1 + time : time;
447
508
  const hours = Math.round(time * 24);
@@ -458,7 +519,7 @@
458
519
  }
459
520
  /** Return the number of days in the current month of the given date */
460
521
  function getDaysInMonth(date) {
461
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
522
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
462
523
  }
463
524
  function isLastDayOfMonth(date) {
464
525
  return getDaysInMonth(date) === date.getDate();
@@ -476,7 +537,7 @@
476
537
  const yStart = date.getFullYear();
477
538
  const mStart = date.getMonth();
478
539
  const dStart = date.getDate();
479
- const jsDate = new Date(yStart, mStart + months);
540
+ const jsDate = new DateTime(yStart, mStart + months, 1);
480
541
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
481
542
  jsDate.setDate(getDaysInMonth(jsDate));
482
543
  }
@@ -1897,7 +1958,7 @@
1897
1958
  .map((p) => {
1898
1959
  switch (p) {
1899
1960
  case "hhhh":
1900
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
1961
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
1901
1962
  return helapsedHours.toString();
1902
1963
  case "hh":
1903
1964
  return hours.toString().padStart(2, "0");
@@ -5045,6 +5106,7 @@
5045
5106
  },
5046
5107
  };
5047
5108
 
5109
+ const macRegex = /Mac/i;
5048
5110
  /**
5049
5111
  * Return true if the event was triggered from
5050
5112
  * a child element.
@@ -5092,7 +5154,15 @@
5092
5154
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5093
5155
  }
5094
5156
  function isMacOS() {
5095
- return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
5157
+ return Boolean(macRegex.test(navigator.userAgent));
5158
+ }
5159
+ /**
5160
+ * @param {KeyboardEvent | MouseEvent} ev
5161
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
5162
+ * On Mac, this is the "meta" or "command" key.
5163
+ */
5164
+ function isCtrlKey(ev) {
5165
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
5096
5166
  }
5097
5167
 
5098
5168
  /**
@@ -9484,6 +9554,9 @@
9484
9554
  function makeArg(str) {
9485
9555
  let parts = str.match(ARG_REGEXP);
9486
9556
  let name = parts[1].trim();
9557
+ if (!name) {
9558
+ throw new Error(`Function argument definition is missing a name: '${str}'.`);
9559
+ }
9487
9560
  let types = [];
9488
9561
  let isOptional = false;
9489
9562
  let isRepeating = false;
@@ -11947,7 +12020,7 @@
11947
12020
  if (_year < 1900) {
11948
12021
  _year += 1900;
11949
12022
  }
11950
- const jsDate = new Date(_year, _month - 1, _day);
12023
+ const jsDate = new DateTime(_year, _month - 1, _day);
11951
12024
  const result = jsDateToRoundNumber(jsDate);
11952
12025
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
11953
12026
  return result;
@@ -12055,7 +12128,7 @@
12055
12128
  const _months = Math.trunc(toNumber(months));
12056
12129
  const yStart = _startDate.getFullYear();
12057
12130
  const mStart = _startDate.getMonth();
12058
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
12131
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
12059
12132
  return jsDateToRoundNumber(jsDate);
12060
12133
  },
12061
12134
  isExported: true,
@@ -12092,17 +12165,17 @@
12092
12165
  // The first week of the year is the week that contains the first
12093
12166
  // Thursday of the year.
12094
12167
  let firstThursday = 1;
12095
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
12168
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
12096
12169
  firstThursday += 1;
12097
12170
  }
12098
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
12171
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
12099
12172
  // The last week of the year is the week that contains the last Thursday of
12100
12173
  // the year.
12101
12174
  let lastThursday = 31;
12102
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
12175
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
12103
12176
  lastThursday -= 1;
12104
12177
  }
12105
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
12178
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
12106
12179
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
12107
12180
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
12108
12181
  let offsetYear;
@@ -12128,17 +12201,17 @@
12128
12201
  case 1:
12129
12202
  // firstDay is the 1st day of the 1st week of the year after
12130
12203
  // firstDay = lastDayOfLastWeek + 1 Day
12131
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
12204
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
12132
12205
  break;
12133
12206
  case -1:
12134
12207
  // firstDay is the 1st day of the 1st week of the previous year.
12135
12208
  // The first week of the previous year is the week that contains the
12136
12209
  // first Thursday of the previous year.
12137
12210
  let firstThursdayPreviousYear = 1;
12138
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
12211
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
12139
12212
  firstThursdayPreviousYear += 1;
12140
12213
  }
12141
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
12214
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
12142
12215
  break;
12143
12216
  }
12144
12217
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -12273,8 +12346,8 @@
12273
12346
  });
12274
12347
  }
12275
12348
  const invertDate = _startDate.getTime() > _endDate.getTime();
12276
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
12277
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
12349
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
12350
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
12278
12351
  const timeStopDate = stopDate.getTime();
12279
12352
  let timeStepDate = stepDate.getTime();
12280
12353
  let netWorkingDay = 0;
@@ -12298,8 +12371,7 @@
12298
12371
  returns: ["DATE"],
12299
12372
  computeFormat: () => "m/d/yyyy hh:mm:ss",
12300
12373
  compute: function () {
12301
- let today = new Date();
12302
- today.setMilliseconds(0);
12374
+ let today = DateTime.now();
12303
12375
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
12304
12376
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
12305
12377
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -12369,8 +12441,8 @@
12369
12441
  returns: ["DATE"],
12370
12442
  computeFormat: () => "m/d/yyyy",
12371
12443
  compute: function () {
12372
- const today = new Date();
12373
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
12444
+ const today = DateTime.now();
12445
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
12374
12446
  return jsDateToRoundNumber(jsDate);
12375
12447
  },
12376
12448
  isExported: true,
@@ -12425,10 +12497,10 @@
12425
12497
  }
12426
12498
  const y = _date.getFullYear();
12427
12499
  let dayStart = 1;
12428
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
12500
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
12429
12501
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
12430
12502
  dayStart += 1;
12431
- startDayOfFirstWeek = new Date(y, 0, dayStart);
12503
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
12432
12504
  }
12433
12505
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
12434
12506
  if (dif < 0) {
@@ -12482,7 +12554,7 @@
12482
12554
  timesHoliday.add(holiday.getTime());
12483
12555
  });
12484
12556
  }
12485
- let stepDate = new Date(_startDate.getTime());
12557
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
12486
12558
  let timeStepDate = stepDate.getTime();
12487
12559
  const unitDay = Math.sign(_numDays);
12488
12560
  let stepDay = Math.abs(_numDays);
@@ -12544,7 +12616,7 @@
12544
12616
  const _startDate = toJsDate(date);
12545
12617
  const yStart = _startDate.getFullYear();
12546
12618
  const mStart = _startDate.getMonth();
12547
- const jsDate = new Date(yStart, mStart, 1);
12619
+ const jsDate = new DateTime(yStart, mStart, 1);
12548
12620
  return jsDateToRoundNumber(jsDate);
12549
12621
  },
12550
12622
  };
@@ -12582,7 +12654,7 @@
12582
12654
  compute: function (date) {
12583
12655
  const quarter = QUARTER.compute(date);
12584
12656
  const year = YEAR.compute(date);
12585
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
12657
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
12586
12658
  return jsDateToRoundNumber(jsDate);
12587
12659
  },
12588
12660
  };
@@ -12597,7 +12669,7 @@
12597
12669
  compute: function (date) {
12598
12670
  const quarter = QUARTER.compute(date);
12599
12671
  const year = YEAR.compute(date);
12600
- const jsDate = new Date(year, quarter * 3, 0);
12672
+ const jsDate = new DateTime(year, quarter * 3, 0);
12601
12673
  return jsDateToRoundNumber(jsDate);
12602
12674
  },
12603
12675
  };
@@ -12611,7 +12683,7 @@
12611
12683
  computeFormat: () => "m/d/yyyy",
12612
12684
  compute: function (date) {
12613
12685
  const year = YEAR.compute(date);
12614
- const jsDate = new Date(year, 0, 1);
12686
+ const jsDate = new DateTime(year, 0, 1);
12615
12687
  return jsDateToRoundNumber(jsDate);
12616
12688
  },
12617
12689
  };
@@ -12625,7 +12697,7 @@
12625
12697
  computeFormat: () => "m/d/yyyy",
12626
12698
  compute: function (date) {
12627
12699
  const year = YEAR.compute(date);
12628
- const jsDate = new Date(year + 1, 0, 0);
12700
+ const jsDate = new DateTime(year + 1, 0, 0);
12629
12701
  return jsDateToRoundNumber(jsDate);
12630
12702
  },
12631
12703
  };
@@ -12672,8 +12744,8 @@
12672
12744
  const DELTA = {
12673
12745
  description: _lt("Compare two numeric values, returning 1 if they're equal."),
12674
12746
  args: [
12675
- arg(" (number)", _lt("The first number to compare.")),
12676
- arg(` (number, default=${DEFAULT_DELTA_ARG})`, _lt("The second number to compare.")),
12747
+ arg("number1 (number)", _lt("The first number to compare.")),
12748
+ arg(`number2 (number, default=${DEFAULT_DELTA_ARG})`, _lt("The second number to compare.")),
12677
12749
  ],
12678
12750
  returns: ["NUMBER"],
12679
12751
  compute: function (number1, number2 = DEFAULT_DELTA_ARG) {
@@ -12759,7 +12831,7 @@
12759
12831
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
12760
12832
  const startDate = toJsDate(settlement);
12761
12833
  const endDate = toJsDate(maturity);
12762
- const startDatePlusOneYear = new Date(startDate);
12834
+ const startDatePlusOneYear = toJsDate(settlement);
12763
12835
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
12764
12836
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _lt("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
12765
12837
  }
@@ -12894,7 +12966,7 @@
12894
12966
  arg("salvage (number)", _lt("The value of the asset at the end of depreciation.")),
12895
12967
  arg("period (number)", _lt("The single period within life for which to calculate depreciation.")),
12896
12968
  arg("rate (number)", _lt("The deprecation rate.")),
12897
- arg(" (number, optional)", _lt("An indicator of what day count method to use.")),
12969
+ arg("day_count_convention (number, optional)", _lt("An indicator of what day count method to use.")),
12898
12970
  ],
12899
12971
  returns: ["NUMBER"],
12900
12972
  compute: function (cost, purchaseDate, firstPeriodEnd, salvage, period, rate, dayCountConvention = DEFAULT_DAY_COUNT_CONVENTION) {
@@ -15838,8 +15910,8 @@
15838
15910
  description: _lt("A segment of a string."),
15839
15911
  args: [
15840
15912
  arg("text (string)", _lt("The string to extract a segment from.")),
15841
- arg(" (number)", _lt("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
15842
- arg(" (number)", _lt("The length of the segment to extract.")),
15913
+ arg("starting_at (number)", _lt("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
15914
+ arg("extract_length (number)", _lt("The length of the segment to extract.")),
15843
15915
  ],
15844
15916
  returns: ["STRING"],
15845
15917
  compute: function (text, starting_at, extract_length) {
@@ -16710,10 +16782,10 @@
16710
16782
  function updateSelectionWithArrowKeys(ev, selection) {
16711
16783
  const direction = arrowMap[ev.key];
16712
16784
  if (ev.shiftKey) {
16713
- selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
16785
+ selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
16714
16786
  }
16715
16787
  else {
16716
- selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
16788
+ selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
16717
16789
  }
16718
16790
  }
16719
16791
 
@@ -21048,7 +21120,10 @@
21048
21120
  return;
21049
21121
  }
21050
21122
  const [col, row] = this.getCartesianCoordinates(ev);
21051
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
21123
+ this.props.onCellClicked(col, row, {
21124
+ expandZone: ev.shiftKey,
21125
+ addZone: isCtrlKey(ev),
21126
+ });
21052
21127
  }
21053
21128
  onDoubleClick(ev) {
21054
21129
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -21292,7 +21367,7 @@
21292
21367
  this._increaseSelection(index);
21293
21368
  }
21294
21369
  else {
21295
- this._selectElement(index, ev.ctrlKey);
21370
+ this._selectElement(index, isCtrlKey(ev));
21296
21371
  }
21297
21372
  this.lastSelectedElementIndex = index;
21298
21373
  const mouseMoveSelect = (col, row) => {
@@ -21305,7 +21380,7 @@
21305
21380
  const mouseUpSelect = () => {
21306
21381
  this.state.isSelecting = false;
21307
21382
  this.lastSelectedElementIndex = null;
21308
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21383
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21309
21384
  this._computeGrabDisplay(ev);
21310
21385
  };
21311
21386
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -21455,8 +21530,8 @@
21455
21530
  this.env.raiseError(MergeErrorMessage);
21456
21531
  }
21457
21532
  }
21458
- _selectElement(index, ctrlKey) {
21459
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
21533
+ _selectElement(index, addDistinctHeader) {
21534
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21460
21535
  }
21461
21536
  _increaseSelection(index) {
21462
21537
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -21628,8 +21703,8 @@
21628
21703
  this.env.raiseError(MergeErrorMessage);
21629
21704
  }
21630
21705
  }
21631
- _selectElement(index, ctrlKey) {
21632
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
21706
+ _selectElement(index, addDistinctHeader) {
21707
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21633
21708
  }
21634
21709
  _increaseSelection(index) {
21635
21710
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -22376,8 +22451,8 @@
22376
22451
  // ---------------------------------------------------------------------------
22377
22452
  // Zone selection with mouse
22378
22453
  // ---------------------------------------------------------------------------
22379
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
22380
- if (ctrlKey) {
22454
+ onCellClicked(col, row, { addZone, expandZone }) {
22455
+ if (addZone) {
22381
22456
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
22382
22457
  }
22383
22458
  if (this.env.model.getters.hasOpenedPopover()) {
@@ -22386,10 +22461,10 @@
22386
22461
  if (this.env.model.getters.getEditionMode() === "editing") {
22387
22462
  this.env.model.dispatch("STOP_EDITION");
22388
22463
  }
22389
- if (shiftKey) {
22464
+ if (expandZone) {
22390
22465
  this.env.model.selection.setAnchorCorner(col, row);
22391
22466
  }
22392
- else if (ctrlKey) {
22467
+ else if (addZone) {
22393
22468
  this.env.model.selection.addCellToSelection(col, row);
22394
22469
  }
22395
22470
  else {
@@ -22451,9 +22526,7 @@
22451
22526
  return;
22452
22527
  }
22453
22528
  let keyDownString = "";
22454
- if (ev.ctrlKey)
22455
- keyDownString += "CTRL+";
22456
- if (ev.metaKey)
22529
+ if (isCtrlKey(ev))
22457
22530
  keyDownString += "CTRL+";
22458
22531
  if (ev.altKey)
22459
22532
  keyDownString += "ALT+";
@@ -31971,13 +32044,18 @@
31971
32044
  // Getters
31972
32045
  // ---------------------------------------------------------------------------
31973
32046
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
31974
- const compiledFormula = compile(formulaString);
31975
- const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
31976
- const ranges = [];
31977
- for (let xc of compiledFormula.dependencies) {
31978
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32047
+ try {
32048
+ const compiledFormula = compile(formulaString);
32049
+ const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32050
+ const ranges = [];
32051
+ for (let xc of compiledFormula.dependencies) {
32052
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32053
+ }
32054
+ return compiledFormula.execute(ranges, ...params).value;
32055
+ }
32056
+ catch (error) {
32057
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
31979
32058
  }
31980
- return compiledFormula.execute(ranges, ...params).value;
31981
32059
  }
31982
32060
  /**
31983
32061
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32479,39 +32557,34 @@
32479
32557
  this.computedIcons[sheetId] = {};
32480
32558
  const computedStyle = this.computedStyles[sheetId];
32481
32559
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
32482
- try {
32483
- switch (cf.rule.type) {
32484
- case "ColorScaleRule":
32485
- for (let range of cf.ranges) {
32486
- this.applyColorScale(range, cf.rule);
32487
- }
32488
- break;
32489
- case "IconSetRule":
32490
- for (let range of cf.ranges) {
32491
- this.applyIcon(range, cf.rule);
32492
- }
32493
- break;
32494
- default:
32495
- for (let ref of cf.ranges) {
32496
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32497
- for (let row = zone.top; row <= zone.bottom; row++) {
32498
- for (let col = zone.left; col <= zone.right; col++) {
32499
- const pr = this.rulePredicate[cf.rule.type];
32500
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32501
- if (pr && pr(cell, cf.rule)) {
32502
- if (!computedStyle[col])
32503
- computedStyle[col] = [];
32504
- // we must combine all the properties of all the CF rules applied to the given cell
32505
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32506
- }
32560
+ switch (cf.rule.type) {
32561
+ case "ColorScaleRule":
32562
+ for (let range of cf.ranges) {
32563
+ this.applyColorScale(range, cf.rule);
32564
+ }
32565
+ break;
32566
+ case "IconSetRule":
32567
+ for (let range of cf.ranges) {
32568
+ this.applyIcon(range, cf.rule);
32569
+ }
32570
+ break;
32571
+ default:
32572
+ for (let ref of cf.ranges) {
32573
+ const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32574
+ for (let row = zone.top; row <= zone.bottom; row++) {
32575
+ for (let col = zone.left; col <= zone.right; col++) {
32576
+ const pr = this.rulePredicate[cf.rule.type];
32577
+ let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32578
+ if (pr && pr(cell, cf.rule)) {
32579
+ if (!computedStyle[col])
32580
+ computedStyle[col] = [];
32581
+ // we must combine all the properties of all the CF rules applied to the given cell
32582
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32507
32583
  }
32508
32584
  }
32509
32585
  }
32510
- break;
32511
- }
32512
- }
32513
- catch (_) {
32514
- // we don't care about the errors within the evaluation of a rule
32586
+ }
32587
+ break;
32515
32588
  }
32516
32589
  }
32517
32590
  }
@@ -32536,7 +32609,7 @@
32536
32609
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32537
32610
  case "formula":
32538
32611
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32539
- return !(value instanceof Promise) ? value : null;
32612
+ return typeof value === "number" ? value : null;
32540
32613
  default:
32541
32614
  return null;
32542
32615
  }
@@ -41884,7 +41957,7 @@
41884
41957
  }
41885
41958
  onKeydown(ev) {
41886
41959
  let keyDownString = "";
41887
- if (ev.ctrlKey || ev.metaKey) {
41960
+ if (isCtrlKey(ev)) {
41888
41961
  keyDownString += "CTRL+";
41889
41962
  }
41890
41963
  keyDownString += ev.key.toUpperCase();
@@ -45590,9 +45663,9 @@
45590
45663
  Object.defineProperty(exports, '__esModule', { value: true });
45591
45664
 
45592
45665
 
45593
- __info__.version = '16.2.24';
45594
- __info__.date = '2023-12-05T09:37:14.992Z';
45595
- __info__.hash = '7b1a35c';
45666
+ __info__.version = '16.2.27';
45667
+ __info__.date = '2024-01-12T13:42:07.889Z';
45668
+ __info__.hash = 'd8b1d67';
45596
45669
 
45597
45670
 
45598
45671
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);