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