@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.
@@ -290,15 +290,76 @@ const FONT_SIZES = [6, 7, 8, 9, 10, 11, 12, 14, 18, 24, 36];
290
290
  // -----------------------------------------------------------------------------
291
291
  // Date Type
292
292
  // -----------------------------------------------------------------------------
293
+ /**
294
+ * A DateTime object that can be used to manipulate spreadsheet dates.
295
+ * Conceptually, a spreadsheet date is simply a number with a date format,
296
+ * and it is timezone-agnostic.
297
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
298
+ */
299
+ class DateTime {
300
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
301
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
302
+ }
303
+ static fromTimestamp(timestamp) {
304
+ const date = new Date(timestamp);
305
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
306
+ }
307
+ static now() {
308
+ const now = new Date();
309
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
310
+ }
311
+ toString() {
312
+ return this.jsDate.toString();
313
+ }
314
+ getTime() {
315
+ return this.jsDate.getTime();
316
+ }
317
+ getFullYear() {
318
+ return this.jsDate.getUTCFullYear();
319
+ }
320
+ getMonth() {
321
+ return this.jsDate.getUTCMonth();
322
+ }
323
+ getDate() {
324
+ return this.jsDate.getUTCDate();
325
+ }
326
+ getDay() {
327
+ return this.jsDate.getUTCDay();
328
+ }
329
+ getHours() {
330
+ return this.jsDate.getUTCHours();
331
+ }
332
+ getMinutes() {
333
+ return this.jsDate.getUTCMinutes();
334
+ }
335
+ getSeconds() {
336
+ return this.jsDate.getUTCSeconds();
337
+ }
338
+ setFullYear(year) {
339
+ this.jsDate.setFullYear(year);
340
+ }
341
+ setDate(date) {
342
+ this.jsDate.setUTCDate(date);
343
+ }
344
+ setHours(hours) {
345
+ this.jsDate.setUTCHours(hours);
346
+ }
347
+ setMinutes(minutes) {
348
+ this.jsDate.setUTCMinutes(minutes);
349
+ }
350
+ setSeconds(seconds) {
351
+ this.jsDate.setUTCSeconds(seconds);
352
+ }
353
+ }
293
354
  // -----------------------------------------------------------------------------
294
355
  // Parsing
295
356
  // -----------------------------------------------------------------------------
296
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
357
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
297
358
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
298
359
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
299
- const CURRENT_YEAR = new Date().getFullYear();
300
- const INITIAL_JS_DAY = new Date(0);
301
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
360
+ const CURRENT_YEAR = DateTime.now().getFullYear();
361
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
362
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
302
363
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
303
364
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
304
365
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -347,7 +408,7 @@ function _parseDateTime(str) {
347
408
  return {
348
409
  value: date.value + time.value,
349
410
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
350
- 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()),
411
+ 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()),
351
412
  };
352
413
  }
353
414
  return date || time;
@@ -364,13 +425,13 @@ function parseDate(str, dateFormat) {
364
425
  const day = Number(parts[dayIndex]);
365
426
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
366
427
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
367
- const jsDate = new Date(year, month - 1, day);
428
+ const jsDate = new DateTime(year, month - 1, day);
368
429
  const sep = str.match(/\/|-|\s/)[0];
369
430
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
370
431
  // invalid date
371
432
  return null;
372
433
  }
373
- const delta = jsDate - INITIAL_1900_DAY;
434
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
374
435
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
375
436
  if (parts[yearIndex]) {
376
437
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -430,7 +491,7 @@ function parseTime(str) {
430
491
  if (hours >= 24) {
431
492
  format = "hhhh:mm:ss";
432
493
  }
433
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
494
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
434
495
  return {
435
496
  value: hours / 24 + minutes / 1440 + seconds / 86400,
436
497
  format: format,
@@ -444,7 +505,7 @@ function parseTime(str) {
444
505
  // -----------------------------------------------------------------------------
445
506
  function numberToJsDate(value) {
446
507
  const truncValue = Math.trunc(value);
447
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
508
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
448
509
  let time = value - truncValue;
449
510
  time = time < 0 ? 1 + time : time;
450
511
  const hours = Math.round(time * 24);
@@ -461,7 +522,7 @@ function jsDateToRoundNumber(date) {
461
522
  }
462
523
  /** Return the number of days in the current month of the given date */
463
524
  function getDaysInMonth(date) {
464
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
525
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
465
526
  }
466
527
  function isLastDayOfMonth(date) {
467
528
  return getDaysInMonth(date) === date.getDate();
@@ -479,7 +540,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
479
540
  const yStart = date.getFullYear();
480
541
  const mStart = date.getMonth();
481
542
  const dStart = date.getDate();
482
- const jsDate = new Date(yStart, mStart + months);
543
+ const jsDate = new DateTime(yStart, mStart + months, 1);
483
544
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
484
545
  jsDate.setDate(getDaysInMonth(jsDate));
485
546
  }
@@ -1900,7 +1961,7 @@ function formatJSTime(jsDate, format) {
1900
1961
  .map((p) => {
1901
1962
  switch (p) {
1902
1963
  case "hhhh":
1903
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
1964
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
1904
1965
  return helapsedHours.toString();
1905
1966
  case "hh":
1906
1967
  return hours.toString().padStart(2, "0");
@@ -5048,6 +5109,7 @@ const FilterMenuPopoverBuilder = {
5048
5109
  },
5049
5110
  };
5050
5111
 
5112
+ const macRegex = /Mac/i;
5051
5113
  /**
5052
5114
  * Return true if the event was triggered from
5053
5115
  * a child element.
@@ -5095,7 +5157,15 @@ function getOpenedMenus() {
5095
5157
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
5096
5158
  }
5097
5159
  function isMacOS() {
5098
- return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
5160
+ return Boolean(macRegex.test(navigator.userAgent));
5161
+ }
5162
+ /**
5163
+ * @param {KeyboardEvent | MouseEvent} ev
5164
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
5165
+ * On Mac, this is the "meta" or "command" key.
5166
+ */
5167
+ function isCtrlKey(ev) {
5168
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
5099
5169
  }
5100
5170
 
5101
5171
  /**
@@ -9487,6 +9557,9 @@ function arg(definition, description = "") {
9487
9557
  function makeArg(str) {
9488
9558
  let parts = str.match(ARG_REGEXP);
9489
9559
  let name = parts[1].trim();
9560
+ if (!name) {
9561
+ throw new Error(`Function argument definition is missing a name: '${str}'.`);
9562
+ }
9490
9563
  let types = [];
9491
9564
  let isOptional = false;
9492
9565
  let isRepeating = false;
@@ -11950,7 +12023,7 @@ const DATE = {
11950
12023
  if (_year < 1900) {
11951
12024
  _year += 1900;
11952
12025
  }
11953
- const jsDate = new Date(_year, _month - 1, _day);
12026
+ const jsDate = new DateTime(_year, _month - 1, _day);
11954
12027
  const result = jsDateToRoundNumber(jsDate);
11955
12028
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
11956
12029
  return result;
@@ -12058,7 +12131,7 @@ const EOMONTH = {
12058
12131
  const _months = Math.trunc(toNumber(months));
12059
12132
  const yStart = _startDate.getFullYear();
12060
12133
  const mStart = _startDate.getMonth();
12061
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
12134
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
12062
12135
  return jsDateToRoundNumber(jsDate);
12063
12136
  },
12064
12137
  isExported: true,
@@ -12095,17 +12168,17 @@ const ISOWEEKNUM = {
12095
12168
  // The first week of the year is the week that contains the first
12096
12169
  // Thursday of the year.
12097
12170
  let firstThursday = 1;
12098
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
12171
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
12099
12172
  firstThursday += 1;
12100
12173
  }
12101
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
12174
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
12102
12175
  // The last week of the year is the week that contains the last Thursday of
12103
12176
  // the year.
12104
12177
  let lastThursday = 31;
12105
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
12178
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
12106
12179
  lastThursday -= 1;
12107
12180
  }
12108
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
12181
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
12109
12182
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
12110
12183
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
12111
12184
  let offsetYear;
@@ -12131,17 +12204,17 @@ const ISOWEEKNUM = {
12131
12204
  case 1:
12132
12205
  // firstDay is the 1st day of the 1st week of the year after
12133
12206
  // firstDay = lastDayOfLastWeek + 1 Day
12134
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
12207
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
12135
12208
  break;
12136
12209
  case -1:
12137
12210
  // firstDay is the 1st day of the 1st week of the previous year.
12138
12211
  // The first week of the previous year is the week that contains the
12139
12212
  // first Thursday of the previous year.
12140
12213
  let firstThursdayPreviousYear = 1;
12141
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
12214
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
12142
12215
  firstThursdayPreviousYear += 1;
12143
12216
  }
12144
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
12217
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
12145
12218
  break;
12146
12219
  }
12147
12220
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -12276,8 +12349,8 @@ const NETWORKDAYS_INTL = {
12276
12349
  });
12277
12350
  }
12278
12351
  const invertDate = _startDate.getTime() > _endDate.getTime();
12279
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
12280
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
12352
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
12353
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
12281
12354
  const timeStopDate = stopDate.getTime();
12282
12355
  let timeStepDate = stepDate.getTime();
12283
12356
  let netWorkingDay = 0;
@@ -12301,8 +12374,7 @@ const NOW = {
12301
12374
  returns: ["DATE"],
12302
12375
  computeFormat: () => "m/d/yyyy hh:mm:ss",
12303
12376
  compute: function () {
12304
- let today = new Date();
12305
- today.setMilliseconds(0);
12377
+ let today = DateTime.now();
12306
12378
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
12307
12379
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
12308
12380
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -12372,8 +12444,8 @@ const TODAY = {
12372
12444
  returns: ["DATE"],
12373
12445
  computeFormat: () => "m/d/yyyy",
12374
12446
  compute: function () {
12375
- const today = new Date();
12376
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
12447
+ const today = DateTime.now();
12448
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
12377
12449
  return jsDateToRoundNumber(jsDate);
12378
12450
  },
12379
12451
  isExported: true,
@@ -12428,10 +12500,10 @@ const WEEKNUM = {
12428
12500
  }
12429
12501
  const y = _date.getFullYear();
12430
12502
  let dayStart = 1;
12431
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
12503
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
12432
12504
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
12433
12505
  dayStart += 1;
12434
- startDayOfFirstWeek = new Date(y, 0, dayStart);
12506
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
12435
12507
  }
12436
12508
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
12437
12509
  if (dif < 0) {
@@ -12485,7 +12557,7 @@ const WORKDAY_INTL = {
12485
12557
  timesHoliday.add(holiday.getTime());
12486
12558
  });
12487
12559
  }
12488
- let stepDate = new Date(_startDate.getTime());
12560
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
12489
12561
  let timeStepDate = stepDate.getTime();
12490
12562
  const unitDay = Math.sign(_numDays);
12491
12563
  let stepDay = Math.abs(_numDays);
@@ -12547,7 +12619,7 @@ const MONTH_START = {
12547
12619
  const _startDate = toJsDate(date);
12548
12620
  const yStart = _startDate.getFullYear();
12549
12621
  const mStart = _startDate.getMonth();
12550
- const jsDate = new Date(yStart, mStart, 1);
12622
+ const jsDate = new DateTime(yStart, mStart, 1);
12551
12623
  return jsDateToRoundNumber(jsDate);
12552
12624
  },
12553
12625
  };
@@ -12585,7 +12657,7 @@ const QUARTER_START = {
12585
12657
  compute: function (date) {
12586
12658
  const quarter = QUARTER.compute(date);
12587
12659
  const year = YEAR.compute(date);
12588
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
12660
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
12589
12661
  return jsDateToRoundNumber(jsDate);
12590
12662
  },
12591
12663
  };
@@ -12600,7 +12672,7 @@ const QUARTER_END = {
12600
12672
  compute: function (date) {
12601
12673
  const quarter = QUARTER.compute(date);
12602
12674
  const year = YEAR.compute(date);
12603
- const jsDate = new Date(year, quarter * 3, 0);
12675
+ const jsDate = new DateTime(year, quarter * 3, 0);
12604
12676
  return jsDateToRoundNumber(jsDate);
12605
12677
  },
12606
12678
  };
@@ -12614,7 +12686,7 @@ const YEAR_START = {
12614
12686
  computeFormat: () => "m/d/yyyy",
12615
12687
  compute: function (date) {
12616
12688
  const year = YEAR.compute(date);
12617
- const jsDate = new Date(year, 0, 1);
12689
+ const jsDate = new DateTime(year, 0, 1);
12618
12690
  return jsDateToRoundNumber(jsDate);
12619
12691
  },
12620
12692
  };
@@ -12628,7 +12700,7 @@ const YEAR_END = {
12628
12700
  computeFormat: () => "m/d/yyyy",
12629
12701
  compute: function (date) {
12630
12702
  const year = YEAR.compute(date);
12631
- const jsDate = new Date(year + 1, 0, 0);
12703
+ const jsDate = new DateTime(year + 1, 0, 0);
12632
12704
  return jsDateToRoundNumber(jsDate);
12633
12705
  },
12634
12706
  };
@@ -12675,8 +12747,8 @@ const DEFAULT_DELTA_ARG = 0;
12675
12747
  const DELTA = {
12676
12748
  description: _lt("Compare two numeric values, returning 1 if they're equal."),
12677
12749
  args: [
12678
- arg(" (number)", _lt("The first number to compare.")),
12679
- arg(` (number, default=${DEFAULT_DELTA_ARG})`, _lt("The second number to compare.")),
12750
+ arg("number1 (number)", _lt("The first number to compare.")),
12751
+ arg(`number2 (number, default=${DEFAULT_DELTA_ARG})`, _lt("The second number to compare.")),
12680
12752
  ],
12681
12753
  returns: ["NUMBER"],
12682
12754
  compute: function (number1, number2 = DEFAULT_DELTA_ARG) {
@@ -12762,7 +12834,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
12762
12834
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
12763
12835
  const startDate = toJsDate(settlement);
12764
12836
  const endDate = toJsDate(maturity);
12765
- const startDatePlusOneYear = new Date(startDate);
12837
+ const startDatePlusOneYear = toJsDate(settlement);
12766
12838
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
12767
12839
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _lt("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
12768
12840
  }
@@ -12897,7 +12969,7 @@ const AMORLINC = {
12897
12969
  arg("salvage (number)", _lt("The value of the asset at the end of depreciation.")),
12898
12970
  arg("period (number)", _lt("The single period within life for which to calculate depreciation.")),
12899
12971
  arg("rate (number)", _lt("The deprecation rate.")),
12900
- arg(" (number, optional)", _lt("An indicator of what day count method to use.")),
12972
+ arg("day_count_convention (number, optional)", _lt("An indicator of what day count method to use.")),
12901
12973
  ],
12902
12974
  returns: ["NUMBER"],
12903
12975
  compute: function (cost, purchaseDate, firstPeriodEnd, salvage, period, rate, dayCountConvention = DEFAULT_DAY_COUNT_CONVENTION) {
@@ -15841,8 +15913,8 @@ const MID = {
15841
15913
  description: _lt("A segment of a string."),
15842
15914
  args: [
15843
15915
  arg("text (string)", _lt("The string to extract a segment from.")),
15844
- arg(" (number)", _lt("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
15845
- arg(" (number)", _lt("The length of the segment to extract.")),
15916
+ 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.")),
15917
+ arg("extract_length (number)", _lt("The length of the segment to extract.")),
15846
15918
  ],
15847
15919
  returns: ["STRING"],
15848
15920
  compute: function (text, starting_at, extract_length) {
@@ -16713,10 +16785,10 @@ const arrowMap = {
16713
16785
  function updateSelectionWithArrowKeys(ev, selection) {
16714
16786
  const direction = arrowMap[ev.key];
16715
16787
  if (ev.shiftKey) {
16716
- selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
16788
+ selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
16717
16789
  }
16718
16790
  else {
16719
- selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
16791
+ selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
16720
16792
  }
16721
16793
  }
16722
16794
 
@@ -21051,7 +21123,10 @@ class GridOverlay extends owl.Component {
21051
21123
  return;
21052
21124
  }
21053
21125
  const [col, row] = this.getCartesianCoordinates(ev);
21054
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
21126
+ this.props.onCellClicked(col, row, {
21127
+ expandZone: ev.shiftKey,
21128
+ addZone: isCtrlKey(ev),
21129
+ });
21055
21130
  }
21056
21131
  onDoubleClick(ev) {
21057
21132
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -21295,7 +21370,7 @@ class AbstractResizer extends owl.Component {
21295
21370
  this._increaseSelection(index);
21296
21371
  }
21297
21372
  else {
21298
- this._selectElement(index, ev.ctrlKey);
21373
+ this._selectElement(index, isCtrlKey(ev));
21299
21374
  }
21300
21375
  this.lastSelectedElementIndex = index;
21301
21376
  const mouseMoveSelect = (col, row) => {
@@ -21308,7 +21383,7 @@ class AbstractResizer extends owl.Component {
21308
21383
  const mouseUpSelect = () => {
21309
21384
  this.state.isSelecting = false;
21310
21385
  this.lastSelectedElementIndex = null;
21311
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21386
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21312
21387
  this._computeGrabDisplay(ev);
21313
21388
  };
21314
21389
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -21458,8 +21533,8 @@ class ColResizer extends AbstractResizer {
21458
21533
  this.env.raiseError(MergeErrorMessage);
21459
21534
  }
21460
21535
  }
21461
- _selectElement(index, ctrlKey) {
21462
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
21536
+ _selectElement(index, addDistinctHeader) {
21537
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21463
21538
  }
21464
21539
  _increaseSelection(index) {
21465
21540
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -21631,8 +21706,8 @@ class RowResizer extends AbstractResizer {
21631
21706
  this.env.raiseError(MergeErrorMessage);
21632
21707
  }
21633
21708
  }
21634
- _selectElement(index, ctrlKey) {
21635
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
21709
+ _selectElement(index, addDistinctHeader) {
21710
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21636
21711
  }
21637
21712
  _increaseSelection(index) {
21638
21713
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -22379,8 +22454,8 @@ class Grid extends owl.Component {
22379
22454
  // ---------------------------------------------------------------------------
22380
22455
  // Zone selection with mouse
22381
22456
  // ---------------------------------------------------------------------------
22382
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
22383
- if (ctrlKey) {
22457
+ onCellClicked(col, row, { addZone, expandZone }) {
22458
+ if (addZone) {
22384
22459
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
22385
22460
  }
22386
22461
  if (this.env.model.getters.hasOpenedPopover()) {
@@ -22389,10 +22464,10 @@ class Grid extends owl.Component {
22389
22464
  if (this.env.model.getters.getEditionMode() === "editing") {
22390
22465
  this.env.model.dispatch("STOP_EDITION");
22391
22466
  }
22392
- if (shiftKey) {
22467
+ if (expandZone) {
22393
22468
  this.env.model.selection.setAnchorCorner(col, row);
22394
22469
  }
22395
- else if (ctrlKey) {
22470
+ else if (addZone) {
22396
22471
  this.env.model.selection.addCellToSelection(col, row);
22397
22472
  }
22398
22473
  else {
@@ -22454,9 +22529,7 @@ class Grid extends owl.Component {
22454
22529
  return;
22455
22530
  }
22456
22531
  let keyDownString = "";
22457
- if (ev.ctrlKey)
22458
- keyDownString += "CTRL+";
22459
- if (ev.metaKey)
22532
+ if (isCtrlKey(ev))
22460
22533
  keyDownString += "CTRL+";
22461
22534
  if (ev.altKey)
22462
22535
  keyDownString += "ALT+";
@@ -31974,13 +32047,18 @@ class EvaluationPlugin extends UIPlugin {
31974
32047
  // Getters
31975
32048
  // ---------------------------------------------------------------------------
31976
32049
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
31977
- const compiledFormula = compile(formulaString);
31978
- const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
31979
- const ranges = [];
31980
- for (let xc of compiledFormula.dependencies) {
31981
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32050
+ try {
32051
+ const compiledFormula = compile(formulaString);
32052
+ const params = this.getCompilationParameters((cell) => this.getEvaluatedCell(this.getters.getCellPosition(cell.id)));
32053
+ const ranges = [];
32054
+ for (let xc of compiledFormula.dependencies) {
32055
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32056
+ }
32057
+ return compiledFormula.execute(ranges, ...params).value;
32058
+ }
32059
+ catch (error) {
32060
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
31982
32061
  }
31983
- return compiledFormula.execute(ranges, ...params).value;
31984
32062
  }
31985
32063
  /**
31986
32064
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32482,39 +32560,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32482
32560
  this.computedIcons[sheetId] = {};
32483
32561
  const computedStyle = this.computedStyles[sheetId];
32484
32562
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
32485
- try {
32486
- switch (cf.rule.type) {
32487
- case "ColorScaleRule":
32488
- for (let range of cf.ranges) {
32489
- this.applyColorScale(range, cf.rule);
32490
- }
32491
- break;
32492
- case "IconSetRule":
32493
- for (let range of cf.ranges) {
32494
- this.applyIcon(range, cf.rule);
32495
- }
32496
- break;
32497
- default:
32498
- for (let ref of cf.ranges) {
32499
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32500
- for (let row = zone.top; row <= zone.bottom; row++) {
32501
- for (let col = zone.left; col <= zone.right; col++) {
32502
- const pr = this.rulePredicate[cf.rule.type];
32503
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32504
- if (pr && pr(cell, cf.rule)) {
32505
- if (!computedStyle[col])
32506
- computedStyle[col] = [];
32507
- // we must combine all the properties of all the CF rules applied to the given cell
32508
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32509
- }
32563
+ switch (cf.rule.type) {
32564
+ case "ColorScaleRule":
32565
+ for (let range of cf.ranges) {
32566
+ this.applyColorScale(range, cf.rule);
32567
+ }
32568
+ break;
32569
+ case "IconSetRule":
32570
+ for (let range of cf.ranges) {
32571
+ this.applyIcon(range, cf.rule);
32572
+ }
32573
+ break;
32574
+ default:
32575
+ for (let ref of cf.ranges) {
32576
+ const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
32577
+ for (let row = zone.top; row <= zone.bottom; row++) {
32578
+ for (let col = zone.left; col <= zone.right; col++) {
32579
+ const pr = this.rulePredicate[cf.rule.type];
32580
+ let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
32581
+ if (pr && pr(cell, cf.rule)) {
32582
+ if (!computedStyle[col])
32583
+ computedStyle[col] = [];
32584
+ // we must combine all the properties of all the CF rules applied to the given cell
32585
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
32510
32586
  }
32511
32587
  }
32512
32588
  }
32513
- break;
32514
- }
32515
- }
32516
- catch (_) {
32517
- // we don't care about the errors within the evaluation of a rule
32589
+ }
32590
+ break;
32518
32591
  }
32519
32592
  }
32520
32593
  }
@@ -32539,7 +32612,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32539
32612
  return percentile(rangeValues, Number(threshold.value) / 100, true);
32540
32613
  case "formula":
32541
32614
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
32542
- return !(value instanceof Promise) ? value : null;
32615
+ return typeof value === "number" ? value : null;
32543
32616
  default:
32544
32617
  return null;
32545
32618
  }
@@ -41887,7 +41960,7 @@ class Spreadsheet extends owl.Component {
41887
41960
  }
41888
41961
  onKeydown(ev) {
41889
41962
  let keyDownString = "";
41890
- if (ev.ctrlKey || ev.metaKey) {
41963
+ if (isCtrlKey(ev)) {
41891
41964
  keyDownString += "CTRL+";
41892
41965
  }
41893
41966
  keyDownString += ev.key.toUpperCase();
@@ -45591,6 +45664,6 @@ exports.setTranslationMethod = setTranslationMethod;
45591
45664
  exports.tokenize = tokenize;
45592
45665
 
45593
45666
 
45594
- __info__.version = '16.2.24';
45595
- __info__.date = '2023-12-05T09:37:14.992Z';
45596
- __info__.hash = '7b1a35c';
45667
+ __info__.version = '16.2.27';
45668
+ __info__.date = '2024-01-12T13:42:07.889Z';
45669
+ __info__.hash = 'd8b1d67';