@odoo/o-spreadsheet 16.0.26 → 16.0.28

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.
@@ -307,15 +307,76 @@ for (let font of fontSizes) {
307
307
  // -----------------------------------------------------------------------------
308
308
  // Date Type
309
309
  // -----------------------------------------------------------------------------
310
+ /**
311
+ * A DateTime object that can be used to manipulate spreadsheet dates.
312
+ * Conceptually, a spreadsheet date is simply a number with a date format,
313
+ * and it is timezone-agnostic.
314
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
315
+ */
316
+ class DateTime {
317
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
318
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
319
+ }
320
+ static fromTimestamp(timestamp) {
321
+ const date = new Date(timestamp);
322
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
323
+ }
324
+ static now() {
325
+ const now = new Date();
326
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
327
+ }
328
+ toString() {
329
+ return this.jsDate.toString();
330
+ }
331
+ getTime() {
332
+ return this.jsDate.getTime();
333
+ }
334
+ getFullYear() {
335
+ return this.jsDate.getUTCFullYear();
336
+ }
337
+ getMonth() {
338
+ return this.jsDate.getUTCMonth();
339
+ }
340
+ getDate() {
341
+ return this.jsDate.getUTCDate();
342
+ }
343
+ getDay() {
344
+ return this.jsDate.getUTCDay();
345
+ }
346
+ getHours() {
347
+ return this.jsDate.getUTCHours();
348
+ }
349
+ getMinutes() {
350
+ return this.jsDate.getUTCMinutes();
351
+ }
352
+ getSeconds() {
353
+ return this.jsDate.getUTCSeconds();
354
+ }
355
+ setFullYear(year) {
356
+ this.jsDate.setFullYear(year);
357
+ }
358
+ setDate(date) {
359
+ this.jsDate.setUTCDate(date);
360
+ }
361
+ setHours(hours) {
362
+ this.jsDate.setUTCHours(hours);
363
+ }
364
+ setMinutes(minutes) {
365
+ this.jsDate.setUTCMinutes(minutes);
366
+ }
367
+ setSeconds(seconds) {
368
+ this.jsDate.setUTCSeconds(seconds);
369
+ }
370
+ }
310
371
  // -----------------------------------------------------------------------------
311
372
  // Parsing
312
373
  // -----------------------------------------------------------------------------
313
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
374
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
314
375
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
315
376
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
316
- const CURRENT_YEAR = new Date().getFullYear();
317
- const INITIAL_JS_DAY = new Date(0);
318
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
377
+ const CURRENT_YEAR = DateTime.now().getFullYear();
378
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
379
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
319
380
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
320
381
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
321
382
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
@@ -364,7 +425,7 @@ function _parseDateTime(str) {
364
425
  return {
365
426
  value: date.value + time.value,
366
427
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
367
- 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()),
428
+ 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()),
368
429
  };
369
430
  }
370
431
  return date || time;
@@ -381,13 +442,13 @@ function parseDate(str, dateFormat) {
381
442
  const day = Number(parts[dayIndex]);
382
443
  const leadingZero = (parts[monthIndex].length === 2 && month < 10) || (parts[dayIndex].length === 2 && day < 10);
383
444
  const year = parts[yearIndex] ? inferYear(parts[yearIndex]) : CURRENT_YEAR;
384
- const jsDate = new Date(year, month - 1, day);
445
+ const jsDate = new DateTime(year, month - 1, day);
385
446
  const sep = str.match(/\/|-|\s/)[0];
386
447
  if (jsDate.getMonth() !== month - 1 || jsDate.getDate() !== day) {
387
448
  // invalid date
388
449
  return null;
389
450
  }
390
- const delta = jsDate - INITIAL_1900_DAY;
451
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
391
452
  let format = leadingZero ? `mm${sep}dd` : `m${sep}d`;
392
453
  if (parts[yearIndex]) {
393
454
  format = isMDY ? format + sep + "yyyy" : "yyyy" + sep + format;
@@ -447,7 +508,7 @@ function parseTime(str) {
447
508
  if (hours >= 24) {
448
509
  format = "hhhh:mm:ss";
449
510
  }
450
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
511
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
451
512
  return {
452
513
  value: hours / 24 + minutes / 1440 + seconds / 86400,
453
514
  format: format,
@@ -461,7 +522,7 @@ function parseTime(str) {
461
522
  // -----------------------------------------------------------------------------
462
523
  function numberToJsDate(value) {
463
524
  const truncValue = Math.trunc(value);
464
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
525
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
465
526
  let time = value - truncValue;
466
527
  time = time < 0 ? 1 + time : time;
467
528
  const hours = Math.round(time * 24);
@@ -478,7 +539,7 @@ function jsDateToRoundNumber(date) {
478
539
  }
479
540
  /** Return the number of days in the current month of the given date */
480
541
  function getDaysInMonth(date) {
481
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
542
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
482
543
  }
483
544
  function isLastDayOfMonth(date) {
484
545
  return getDaysInMonth(date) === date.getDate();
@@ -496,7 +557,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
496
557
  const yStart = date.getFullYear();
497
558
  const mStart = date.getMonth();
498
559
  const dStart = date.getDate();
499
- const jsDate = new Date(yStart, mStart + months);
560
+ const jsDate = new DateTime(yStart, mStart + months, 1);
500
561
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
501
562
  jsDate.setDate(getDaysInMonth(jsDate));
502
563
  }
@@ -849,7 +910,7 @@ function isBoolean(str) {
849
910
  function isDateTime(str) {
850
911
  return parseDateTime(str) !== null;
851
912
  }
852
- const MARKDOWN_LINK_REGEX = /^\[([^\[]+)\]\((.+)\)$/;
913
+ const MARKDOWN_LINK_REGEX = /^\[(.+)\]\((.+)\)$/;
853
914
  //link must start with http or https
854
915
  //https://stackoverflow.com/a/3809435/4760614
855
916
  const WEB_LINK_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/;
@@ -1772,7 +1833,7 @@ function formatJSTime(jsDate, format) {
1772
1833
  .map((p) => {
1773
1834
  switch (p) {
1774
1835
  case "hhhh":
1775
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
1836
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
1776
1837
  return helapsedHours.toString();
1777
1838
  case "hh":
1778
1839
  return hours.toString().padStart(2, "0");
@@ -3887,6 +3948,7 @@ function getMenuDescription(node) {
3887
3948
  return node.description ? node.description : "";
3888
3949
  }
3889
3950
 
3951
+ const macRegex = /Mac/i;
3890
3952
  /**
3891
3953
  * Return true if the event was triggered from
3892
3954
  * a child element.
@@ -3906,7 +3968,15 @@ function getOpenedMenus() {
3906
3968
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
3907
3969
  }
3908
3970
  function isMacOS() {
3909
- return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
3971
+ return Boolean(macRegex.test(navigator.userAgent));
3972
+ }
3973
+ /**
3974
+ * @param {KeyboardEvent | MouseEvent} ev
3975
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
3976
+ * On Mac, this is the "meta" or "command" key.
3977
+ */
3978
+ function isCtrlKey(ev) {
3979
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
3910
3980
  }
3911
3981
 
3912
3982
  /**
@@ -13839,7 +13909,7 @@ const DATE = {
13839
13909
  if (_year < 1900) {
13840
13910
  _year += 1900;
13841
13911
  }
13842
- const jsDate = new Date(_year, _month - 1, _day);
13912
+ const jsDate = new DateTime(_year, _month - 1, _day);
13843
13913
  const result = jsDateToRoundNumber(jsDate);
13844
13914
  assert(() => result >= 0, _lt(`The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900.`));
13845
13915
  return result;
@@ -13951,7 +14021,7 @@ const EOMONTH = {
13951
14021
  const _months = Math.trunc(toNumber(months));
13952
14022
  const yStart = _startDate.getFullYear();
13953
14023
  const mStart = _startDate.getMonth();
13954
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
14024
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
13955
14025
  return jsDateToRoundNumber(jsDate);
13956
14026
  },
13957
14027
  isExported: true,
@@ -13990,17 +14060,17 @@ const ISOWEEKNUM = {
13990
14060
  // The first week of the year is the week that contains the first
13991
14061
  // Thursday of the year.
13992
14062
  let firstThursday = 1;
13993
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
14063
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
13994
14064
  firstThursday += 1;
13995
14065
  }
13996
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
14066
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
13997
14067
  // The last week of the year is the week that contains the last Thursday of
13998
14068
  // the year.
13999
14069
  let lastThursday = 31;
14000
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
14070
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
14001
14071
  lastThursday -= 1;
14002
14072
  }
14003
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
14073
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
14004
14074
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
14005
14075
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
14006
14076
  let offsetYear;
@@ -14026,17 +14096,17 @@ const ISOWEEKNUM = {
14026
14096
  case 1:
14027
14097
  // firstDay is the 1st day of the 1st week of the year after
14028
14098
  // firstDay = lastDayOfLastWeek + 1 Day
14029
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
14099
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
14030
14100
  break;
14031
14101
  case -1:
14032
14102
  // firstDay is the 1st day of the 1st week of the previous year.
14033
14103
  // The first week of the previous year is the week that contains the
14034
14104
  // first Thursday of the previous year.
14035
14105
  let firstThursdayPreviousYear = 1;
14036
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
14106
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
14037
14107
  firstThursdayPreviousYear += 1;
14038
14108
  }
14039
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
14109
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
14040
14110
  break;
14041
14111
  }
14042
14112
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -14175,8 +14245,8 @@ const NETWORKDAYS_INTL = {
14175
14245
  });
14176
14246
  }
14177
14247
  const invertDate = _startDate.getTime() > _endDate.getTime();
14178
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
14179
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
14248
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
14249
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
14180
14250
  const timeStopDate = stopDate.getTime();
14181
14251
  let timeStepDate = stepDate.getTime();
14182
14252
  let netWorkingDay = 0;
@@ -14200,8 +14270,7 @@ const NOW = {
14200
14270
  returns: ["DATE"],
14201
14271
  computeFormat: () => "m/d/yyyy hh:mm:ss",
14202
14272
  compute: function () {
14203
- let today = new Date();
14204
- today.setMilliseconds(0);
14273
+ let today = DateTime.now();
14205
14274
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
14206
14275
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
14207
14276
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -14275,8 +14344,8 @@ const TODAY = {
14275
14344
  returns: ["DATE"],
14276
14345
  computeFormat: () => "m/d/yyyy",
14277
14346
  compute: function () {
14278
- const today = new Date();
14279
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
14347
+ const today = DateTime.now();
14348
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
14280
14349
  return jsDateToRoundNumber(jsDate);
14281
14350
  },
14282
14351
  isExported: true,
@@ -14331,10 +14400,10 @@ const WEEKNUM = {
14331
14400
  }
14332
14401
  const y = _date.getFullYear();
14333
14402
  let dayStart = 1;
14334
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
14403
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
14335
14404
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
14336
14405
  dayStart += 1;
14337
- startDayOfFirstWeek = new Date(y, 0, dayStart);
14406
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
14338
14407
  }
14339
14408
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
14340
14409
  if (dif < 0) {
@@ -14388,7 +14457,7 @@ const WORKDAY_INTL = {
14388
14457
  timesHoliday.add(holiday.getTime());
14389
14458
  });
14390
14459
  }
14391
- let stepDate = new Date(_startDate.getTime());
14460
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
14392
14461
  let timeStepDate = stepDate.getTime();
14393
14462
  const unitDay = Math.sign(_numDays);
14394
14463
  let stepDay = Math.abs(_numDays);
@@ -14454,7 +14523,7 @@ const MONTH_START = {
14454
14523
  const _startDate = toJsDate(date);
14455
14524
  const yStart = _startDate.getFullYear();
14456
14525
  const mStart = _startDate.getMonth();
14457
- const jsDate = new Date(yStart, mStart, 1);
14526
+ const jsDate = new DateTime(yStart, mStart, 1);
14458
14527
  return jsDateToRoundNumber(jsDate);
14459
14528
  },
14460
14529
  };
@@ -14498,7 +14567,7 @@ const QUARTER_START = {
14498
14567
  compute: function (date) {
14499
14568
  const quarter = QUARTER.compute(date);
14500
14569
  const year = YEAR.compute(date);
14501
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
14570
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
14502
14571
  return jsDateToRoundNumber(jsDate);
14503
14572
  },
14504
14573
  };
@@ -14515,7 +14584,7 @@ const QUARTER_END = {
14515
14584
  compute: function (date) {
14516
14585
  const quarter = QUARTER.compute(date);
14517
14586
  const year = YEAR.compute(date);
14518
- const jsDate = new Date(year, quarter * 3, 0);
14587
+ const jsDate = new DateTime(year, quarter * 3, 0);
14519
14588
  return jsDateToRoundNumber(jsDate);
14520
14589
  },
14521
14590
  };
@@ -14531,7 +14600,7 @@ const YEAR_START = {
14531
14600
  computeFormat: () => "m/d/yyyy",
14532
14601
  compute: function (date) {
14533
14602
  const year = YEAR.compute(date);
14534
- const jsDate = new Date(year, 0, 1);
14603
+ const jsDate = new DateTime(year, 0, 1);
14535
14604
  return jsDateToRoundNumber(jsDate);
14536
14605
  },
14537
14606
  };
@@ -14547,7 +14616,7 @@ const YEAR_END = {
14547
14616
  computeFormat: () => "m/d/yyyy",
14548
14617
  compute: function (date) {
14549
14618
  const year = YEAR.compute(date);
14550
- const jsDate = new Date(year + 1, 0, 0);
14619
+ const jsDate = new DateTime(year + 1, 0, 0);
14551
14620
  return jsDateToRoundNumber(jsDate);
14552
14621
  },
14553
14622
  };
@@ -14681,7 +14750,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
14681
14750
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity) {
14682
14751
  const startDate = toJsDate(settlement);
14683
14752
  const endDate = toJsDate(maturity);
14684
- const startDatePlusOneYear = new Date(startDate);
14753
+ const startDatePlusOneYear = toJsDate(settlement);
14685
14754
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
14686
14755
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _lt("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
14687
14756
  }
@@ -21476,7 +21545,10 @@ class GridOverlay extends owl.Component {
21476
21545
  return;
21477
21546
  }
21478
21547
  const [col, row] = this.getCartesianCoordinates(ev);
21479
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
21548
+ this.props.onCellClicked(col, row, {
21549
+ expandZone: ev.shiftKey,
21550
+ addZone: isCtrlKey(ev),
21551
+ });
21480
21552
  }
21481
21553
  onDoubleClick(ev) {
21482
21554
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -21704,7 +21776,7 @@ class AbstractResizer extends owl.Component {
21704
21776
  this._increaseSelection(index);
21705
21777
  }
21706
21778
  else {
21707
- this._selectElement(index, ev.ctrlKey);
21779
+ this._selectElement(index, isCtrlKey(ev));
21708
21780
  }
21709
21781
  this.lastSelectedElementIndex = index;
21710
21782
  const mouseMoveSelect = (col, row) => {
@@ -21717,7 +21789,7 @@ class AbstractResizer extends owl.Component {
21717
21789
  const mouseUpSelect = () => {
21718
21790
  this.state.isSelecting = false;
21719
21791
  this.lastSelectedElementIndex = null;
21720
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21792
+ this.env.model.dispatch(isCtrlKey(ev) ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
21721
21793
  this._computeGrabDisplay(ev);
21722
21794
  };
21723
21795
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -21864,8 +21936,8 @@ class ColResizer extends AbstractResizer {
21864
21936
  this.env.raiseError(MergeErrorMessage);
21865
21937
  }
21866
21938
  }
21867
- _selectElement(index, ctrlKey) {
21868
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
21939
+ _selectElement(index, addDistinctHeader) {
21940
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
21869
21941
  }
21870
21942
  _increaseSelection(index) {
21871
21943
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -22034,8 +22106,8 @@ class RowResizer extends AbstractResizer {
22034
22106
  this.env.raiseError(MergeErrorMessage);
22035
22107
  }
22036
22108
  }
22037
- _selectElement(index, ctrlKey) {
22038
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
22109
+ _selectElement(index, addDistinctHeader) {
22110
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
22039
22111
  }
22040
22112
  _increaseSelection(index) {
22041
22113
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -22726,18 +22798,18 @@ class Grid extends owl.Component {
22726
22798
  // ---------------------------------------------------------------------------
22727
22799
  // Zone selection with mouse
22728
22800
  // ---------------------------------------------------------------------------
22729
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
22730
- if (ctrlKey) {
22801
+ onCellClicked(col, row, { addZone, expandZone }) {
22802
+ if (addZone) {
22731
22803
  this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
22732
22804
  }
22733
22805
  this.closeOpenedPopover();
22734
22806
  if (this.env.model.getters.getEditionMode() === "editing") {
22735
22807
  this.env.model.dispatch("STOP_EDITION");
22736
22808
  }
22737
- if (shiftKey) {
22809
+ if (expandZone) {
22738
22810
  this.env.model.selection.setAnchorCorner(col, row);
22739
22811
  }
22740
- else if (ctrlKey) {
22812
+ else if (addZone) {
22741
22813
  this.env.model.selection.addCellToSelection(col, row);
22742
22814
  }
22743
22815
  else {
@@ -22791,10 +22863,10 @@ class Grid extends owl.Component {
22791
22863
  };
22792
22864
  const direction = arrowMap[ev.key];
22793
22865
  if (ev.shiftKey) {
22794
- this.env.model.selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
22866
+ this.env.model.selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
22795
22867
  }
22796
22868
  else {
22797
- this.env.model.selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
22869
+ this.env.model.selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
22798
22870
  }
22799
22871
  if (this.env.model.getters.isPaintingFormat()) {
22800
22872
  this.env.model.dispatch("PASTE", {
@@ -22808,9 +22880,7 @@ class Grid extends owl.Component {
22808
22880
  return;
22809
22881
  }
22810
22882
  let keyDownString = "";
22811
- if (ev.ctrlKey)
22812
- keyDownString += "CTRL+";
22813
- if (ev.metaKey)
22883
+ if (isCtrlKey(ev))
22814
22884
  keyDownString += "CTRL+";
22815
22885
  if (ev.altKey)
22816
22886
  keyDownString += "ALT+";
@@ -32571,13 +32641,18 @@ class EvaluationPlugin extends UIPlugin {
32571
32641
  // Getters
32572
32642
  // ---------------------------------------------------------------------------
32573
32643
  evaluateFormula(formulaString, sheetId = this.getters.getActiveSheetId()) {
32574
- const compiledFormula = compile(formulaString);
32575
- const params = this.getCompilationParameters(() => { });
32576
- const ranges = [];
32577
- for (let xc of compiledFormula.dependencies) {
32578
- ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32644
+ try {
32645
+ const compiledFormula = compile(formulaString);
32646
+ const params = this.getCompilationParameters(() => { });
32647
+ const ranges = [];
32648
+ for (let xc of compiledFormula.dependencies) {
32649
+ ranges.push(this.getters.getRangeFromSheetXC(sheetId, xc));
32650
+ }
32651
+ return compiledFormula.execute(ranges, ...params).value;
32652
+ }
32653
+ catch (error) {
32654
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
32579
32655
  }
32580
- return compiledFormula.execute(ranges, ...params).value;
32581
32656
  }
32582
32657
  /**
32583
32658
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -32979,39 +33054,34 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
32979
33054
  this.computedIcons[activeSheetId] = {};
32980
33055
  const computedStyle = this.computedStyles[activeSheetId];
32981
33056
  for (let cf of this.getters.getConditionalFormats(activeSheetId).reverse()) {
32982
- try {
32983
- switch (cf.rule.type) {
32984
- case "ColorScaleRule":
32985
- for (let range of cf.ranges) {
32986
- this.applyColorScale(range, cf.rule);
32987
- }
32988
- break;
32989
- case "IconSetRule":
32990
- for (let range of cf.ranges) {
32991
- this.applyIcon(range, cf.rule);
32992
- }
32993
- break;
32994
- default:
32995
- for (let ref of cf.ranges) {
32996
- const zone = this.getters.getRangeFromSheetXC(activeSheetId, ref).zone;
32997
- for (let row = zone.top; row <= zone.bottom; row++) {
32998
- for (let col = zone.left; col <= zone.right; col++) {
32999
- const pr = this.rulePredicate[cf.rule.type];
33000
- let cell = this.getters.getCell(activeSheetId, col, row);
33001
- if (pr && pr(cell, cf.rule)) {
33002
- if (!computedStyle[col])
33003
- computedStyle[col] = [];
33004
- // we must combine all the properties of all the CF rules applied to the given cell
33005
- computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
33006
- }
33057
+ switch (cf.rule.type) {
33058
+ case "ColorScaleRule":
33059
+ for (let range of cf.ranges) {
33060
+ this.applyColorScale(range, cf.rule);
33061
+ }
33062
+ break;
33063
+ case "IconSetRule":
33064
+ for (let range of cf.ranges) {
33065
+ this.applyIcon(range, cf.rule);
33066
+ }
33067
+ break;
33068
+ default:
33069
+ for (let ref of cf.ranges) {
33070
+ const zone = this.getters.getRangeFromSheetXC(activeSheetId, ref).zone;
33071
+ for (let row = zone.top; row <= zone.bottom; row++) {
33072
+ for (let col = zone.left; col <= zone.right; col++) {
33073
+ const pr = this.rulePredicate[cf.rule.type];
33074
+ let cell = this.getters.getCell(activeSheetId, col, row);
33075
+ if (pr && pr(cell, cf.rule)) {
33076
+ if (!computedStyle[col])
33077
+ computedStyle[col] = [];
33078
+ // we must combine all the properties of all the CF rules applied to the given cell
33079
+ computedStyle[col][row] = Object.assign(((_a = computedStyle[col]) === null || _a === void 0 ? void 0 : _a[row]) || {}, cf.rule.style);
33007
33080
  }
33008
33081
  }
33009
33082
  }
33010
- break;
33011
- }
33012
- }
33013
- catch (_) {
33014
- // we don't care about the errors within the evaluation of a rule
33083
+ }
33084
+ break;
33015
33085
  }
33016
33086
  }
33017
33087
  }
@@ -33035,7 +33105,7 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
33035
33105
  return percentile(rangeValues, Number(threshold.value) / 100, true);
33036
33106
  case "formula":
33037
33107
  const value = threshold.value && this.getters.evaluateFormula(threshold.value);
33038
- return !(value instanceof Promise) ? value : null;
33108
+ return typeof value === "number" ? value : null;
33039
33109
  default:
33040
33110
  return null;
33041
33111
  }
@@ -39114,7 +39184,7 @@ class Spreadsheet extends owl.Component {
39114
39184
  }
39115
39185
  onKeydown(ev) {
39116
39186
  let keyDownString = "";
39117
- if (ev.ctrlKey || ev.metaKey) {
39187
+ if (isCtrlKey(ev)) {
39118
39188
  keyDownString += "CTRL+";
39119
39189
  }
39120
39190
  keyDownString += ev.key.toUpperCase();
@@ -43061,6 +43131,6 @@ exports.setTranslationMethod = setTranslationMethod;
43061
43131
  exports.tokenize = tokenize;
43062
43132
 
43063
43133
 
43064
- __info__.version = '16.0.26';
43065
- __info__.date = '2023-12-05T09:37:20.450Z';
43066
- __info__.hash = '10de093';
43134
+ __info__.version = '16.0.28';
43135
+ __info__.date = '2024-01-12T13:42:04.756Z';
43136
+ __info__.hash = '7251a47';
@@ -243,6 +243,33 @@ declare function toCartesian(xc: string): Position$1;
243
243
  */
244
244
  declare function toXC(col: HeaderIndex, row: HeaderIndex, rangePart?: RangePart): string;
245
245
 
246
+ /**
247
+ * A DateTime object that can be used to manipulate spreadsheet dates.
248
+ * Conceptually, a spreadsheet date is simply a number with a date format,
249
+ * and it is timezone-agnostic.
250
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
251
+ */
252
+ declare class DateTime {
253
+ private jsDate;
254
+ constructor(year: number, month: number, day: number, hours?: number, minutes?: number, seconds?: number);
255
+ static fromTimestamp(timestamp: number): DateTime;
256
+ static now(): DateTime;
257
+ toString(): string;
258
+ getTime(): number;
259
+ getFullYear(): number;
260
+ getMonth(): number;
261
+ getDate(): number;
262
+ getDay(): number;
263
+ getHours(): number;
264
+ getMinutes(): number;
265
+ getSeconds(): number;
266
+ setFullYear(year: number): void;
267
+ setDate(date: number): void;
268
+ setHours(hours: number): void;
269
+ setMinutes(minutes: number): void;
270
+ setSeconds(seconds: number): void;
271
+ }
272
+
246
273
  /**
247
274
  * Formats a cell value with its format.
248
275
  */
@@ -3872,7 +3899,7 @@ declare class EvaluationPlugin extends UIPlugin {
3872
3899
  constructor(getters: Getters, state: StateObserver, dispatch: CommandDispatcher["dispatch"], config: ModelConfig, selection: SelectionStreamProcessor);
3873
3900
  handle(cmd: Command): void;
3874
3901
  finalize(): void;
3875
- evaluateFormula(formulaString: string, sheetId?: UID): any;
3902
+ evaluateFormula(formulaString: string, sheetId?: UID): FunctionReturnValue | null;
3876
3903
  /**
3877
3904
  * Return the value of each cell in the range as they are displayed in the grid.
3878
3905
  */
@@ -5759,8 +5786,8 @@ interface Props$d {
5759
5786
  onCellHovered: (position: Partial<Position$1>) => void;
5760
5787
  onCellDoubleClicked: (col: HeaderIndex, row: HeaderIndex) => void;
5761
5788
  onCellClicked: (col: HeaderIndex, row: HeaderIndex, modifiers: {
5762
- ctrlKey: boolean;
5763
- shiftKey: boolean;
5789
+ addZone: boolean;
5790
+ expandZone: boolean;
5764
5791
  }) => void;
5765
5792
  onCellRightClicked: (col: HeaderIndex, row: HeaderIndex, coordinates: DOMCoordinates) => void;
5766
5793
  onGridResized: (dimension: DOMDimension) => void;
@@ -5842,7 +5869,7 @@ declare abstract class AbstractResizer extends Component<ResizerProps, Spreadshe
5842
5869
  abstract _getMaxSize(): Pixel;
5843
5870
  abstract _updateSize(): void;
5844
5871
  abstract _moveElements(): void;
5845
- abstract _selectElement(index: HeaderIndex, ctrlKey: boolean): void;
5872
+ abstract _selectElement(index: HeaderIndex, addDistinctHeader: boolean): void;
5846
5873
  abstract _increaseSelection(index: HeaderIndex): void;
5847
5874
  abstract _fitElementSize(index: HeaderIndex): void;
5848
5875
  abstract _getType(): ContextMenuType;
@@ -5876,7 +5903,7 @@ declare class ColResizer extends AbstractResizer {
5876
5903
  _getMaxSize(): Pixel;
5877
5904
  _updateSize(): void;
5878
5905
  _moveElements(): void;
5879
- _selectElement(index: HeaderIndex, ctrlKey: boolean): void;
5906
+ _selectElement(index: HeaderIndex, addDistinctHeader: boolean): void;
5880
5907
  _increaseSelection(index: HeaderIndex): void;
5881
5908
  _fitElementSize(index: HeaderIndex): void;
5882
5909
  _getType(): ContextMenuType;
@@ -5901,7 +5928,7 @@ declare class RowResizer extends AbstractResizer {
5901
5928
  _getMaxSize(): Pixel;
5902
5929
  _updateSize(): void;
5903
5930
  _moveElements(): void;
5904
- _selectElement(index: HeaderIndex, ctrlKey: boolean): void;
5931
+ _selectElement(index: HeaderIndex, addDistinctHeader: boolean): void;
5905
5932
  _increaseSelection(index: HeaderIndex): void;
5906
5933
  _fitElementSize(index: HeaderIndex): void;
5907
5934
  _getType(): ContextMenuType;
@@ -6105,9 +6132,9 @@ declare class Grid extends Component<Props$5, SpreadsheetChildEnv> {
6105
6132
  private moveCanvas;
6106
6133
  getClientPositionKey(client: Client): string;
6107
6134
  isCellHovered(col: HeaderIndex, row: HeaderIndex): boolean;
6108
- onCellClicked(col: HeaderIndex, row: HeaderIndex, { ctrlKey, shiftKey }: {
6109
- ctrlKey: boolean;
6110
- shiftKey: boolean;
6135
+ onCellClicked(col: HeaderIndex, row: HeaderIndex, { addZone, expandZone }: {
6136
+ addZone: boolean;
6137
+ expandZone: boolean;
6111
6138
  }): void;
6112
6139
  onCellDoubleClicked(col: HeaderIndex, row: HeaderIndex): void;
6113
6140
  closeOpenedPopover(): void;
@@ -6141,7 +6168,7 @@ declare class ChartPanel extends Component<Props$4, SpreadsheetChildEnv> {
6141
6168
  declare function toNumber(value: string | number | boolean | null | undefined): number;
6142
6169
  declare function toString(value: string | number | boolean | null | undefined): string;
6143
6170
  declare function toBoolean(value: string | number | boolean | null | undefined): boolean;
6144
- declare function toJsDate(value: string | number | boolean | null | undefined): Date;
6171
+ declare function toJsDate(value: string | number | boolean | null | undefined): DateTime;
6145
6172
 
6146
6173
  /**
6147
6174
  * This function is meant to be used as a tag for a template strings.