@odoo/o-spreadsheet 17.1.0-alpha.5 → 17.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.1.0-alpha.5
6
- * @date 2023-12-05T09:51:40.034Z
7
- * @hash c2823eb
5
+ * @version 17.1.0-alpha.7
6
+ * @date 2024-01-12T13:45:00.505Z
7
+ * @hash cbce1ed
8
8
  */
9
9
 
10
10
  'use strict';
@@ -330,13 +330,6 @@ const FONT_SIZES = [6, 7, 8, 9, 10, 11, 12, 14, 18, 24, 36];
330
330
  //------------------------------------------------------------------------------
331
331
  // Miscellaneous
332
332
  //------------------------------------------------------------------------------
333
- /**
334
- * Stringify an object, like JSON.stringify, except that the first level of keys
335
- * is ordered.
336
- */
337
- function stringify(obj) {
338
- return JSON.stringify(obj, Object.keys(obj).sort());
339
- }
340
333
  /**
341
334
  * Remove quotes from a quoted string
342
335
  * ```js
@@ -559,7 +552,7 @@ function isObjectEmptyRecursive(argument) {
559
552
  */
560
553
  function getItemId(item, itemsDic) {
561
554
  for (let [key, value] of Object.entries(itemsDic)) {
562
- if (stringify(value) === stringify(item)) {
555
+ if (deepEquals(value, item)) {
563
556
  return parseInt(key, 10);
564
557
  }
565
558
  }
@@ -1209,16 +1202,84 @@ function toXC(col, row, rangePart = { colFixed: false, rowFixed: false }) {
1209
1202
  // -----------------------------------------------------------------------------
1210
1203
  // Date Type
1211
1204
  // -----------------------------------------------------------------------------
1205
+ /**
1206
+ * A DateTime object that can be used to manipulate spreadsheet dates.
1207
+ * Conceptually, a spreadsheet date is simply a number with a date format,
1208
+ * and it is timezone-agnostic.
1209
+ * This DateTime object consistently uses UTC time to represent a naive date and time.
1210
+ */
1211
+ class DateTime {
1212
+ jsDate;
1213
+ constructor(year, month, day, hours = 0, minutes = 0, seconds = 0) {
1214
+ this.jsDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds, 0));
1215
+ }
1216
+ static fromTimestamp(timestamp) {
1217
+ const date = new Date(timestamp);
1218
+ return new DateTime(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
1219
+ }
1220
+ static now() {
1221
+ const now = new Date();
1222
+ return new DateTime(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
1223
+ }
1224
+ toString() {
1225
+ return this.jsDate.toString();
1226
+ }
1227
+ toLocaleDateString() {
1228
+ return this.jsDate.toLocaleDateString();
1229
+ }
1230
+ getTime() {
1231
+ return this.jsDate.getTime();
1232
+ }
1233
+ getFullYear() {
1234
+ return this.jsDate.getUTCFullYear();
1235
+ }
1236
+ getMonth() {
1237
+ return this.jsDate.getUTCMonth();
1238
+ }
1239
+ getDate() {
1240
+ return this.jsDate.getUTCDate();
1241
+ }
1242
+ getDay() {
1243
+ return this.jsDate.getUTCDay();
1244
+ }
1245
+ getHours() {
1246
+ return this.jsDate.getUTCHours();
1247
+ }
1248
+ getMinutes() {
1249
+ return this.jsDate.getUTCMinutes();
1250
+ }
1251
+ getSeconds() {
1252
+ return this.jsDate.getUTCSeconds();
1253
+ }
1254
+ setFullYear(year) {
1255
+ return this.jsDate.setUTCFullYear(year);
1256
+ }
1257
+ setMonth(month) {
1258
+ return this.jsDate.setUTCMonth(month);
1259
+ }
1260
+ setDate(date) {
1261
+ return this.jsDate.setUTCDate(date);
1262
+ }
1263
+ setHours(hours) {
1264
+ return this.jsDate.setUTCHours(hours);
1265
+ }
1266
+ setMinutes(minutes) {
1267
+ return this.jsDate.setUTCMinutes(minutes);
1268
+ }
1269
+ setSeconds(seconds) {
1270
+ return this.jsDate.setUTCSeconds(seconds);
1271
+ }
1272
+ }
1212
1273
  // -----------------------------------------------------------------------------
1213
1274
  // Parsing
1214
1275
  // -----------------------------------------------------------------------------
1215
- const INITIAL_1900_DAY = new Date(1899, 11, 30);
1276
+ const INITIAL_1900_DAY = new DateTime(1899, 11, 30);
1216
1277
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
1217
1278
  const CURRENT_MILLENIAL = 2000; // note: don't forget to update this in 2999
1218
- const CURRENT_YEAR = new Date().getFullYear();
1219
- const CURRENT_MONTH = new Date().getMonth();
1220
- const INITIAL_JS_DAY = new Date(0);
1221
- const DATE_JS_1900_OFFSET = INITIAL_JS_DAY - INITIAL_1900_DAY;
1279
+ const CURRENT_YEAR = DateTime.now().getFullYear();
1280
+ const CURRENT_MONTH = DateTime.now().getMonth();
1281
+ const INITIAL_JS_DAY = DateTime.fromTimestamp(0);
1282
+ const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
1222
1283
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
1223
1284
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
1224
1285
  const dateSeparatorsRegex = /\/|-|\s/;
@@ -1279,7 +1340,7 @@ function _parseDateTime(str, locale) {
1279
1340
  return {
1280
1341
  value: date.value + time.value,
1281
1342
  format: date.format + " " + (time.format === "hhhh:mm:ss" ? "hh:mm:ss" : time.format),
1282
- 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()),
1343
+ 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()),
1283
1344
  };
1284
1345
  }
1285
1346
  return date || time;
@@ -1355,12 +1416,12 @@ function parseDate(parts, separator) {
1355
1416
  // month + 1: months are 0-indexed in JS
1356
1417
  const leadingZero = (monthStr?.length === 2 && month + 1 < 10) || (dayStr?.length === 2 && day < 10);
1357
1418
  const fullYear = yearStr?.length !== 2;
1358
- const jsDate = new Date(year, month, day);
1419
+ const jsDate = new DateTime(year, month, day);
1359
1420
  if (jsDate.getMonth() !== month || jsDate.getDate() !== day) {
1360
1421
  // invalid date
1361
1422
  return null;
1362
1423
  }
1363
- const delta = jsDate - INITIAL_1900_DAY;
1424
+ const delta = jsDate.getTime() - INITIAL_1900_DAY.getTime();
1364
1425
  const format = getFormatFromDateParts(parts, separator, leadingZero, fullYear);
1365
1426
  return {
1366
1427
  value: Math.round(delta / MS_PER_DAY),
@@ -1451,7 +1512,7 @@ function parseTime(str) {
1451
1512
  if (hours >= 24) {
1452
1513
  format = "hhhh:mm:ss";
1453
1514
  }
1454
- const jsDate = new Date(1899, 11, 30, hours, minutes, seconds);
1515
+ const jsDate = new DateTime(1899, 11, 30, hours, minutes, seconds);
1455
1516
  return {
1456
1517
  value: hours / 24 + minutes / 1440 + seconds / 86400,
1457
1518
  format: format,
@@ -1465,7 +1526,7 @@ function parseTime(str) {
1465
1526
  // -----------------------------------------------------------------------------
1466
1527
  function numberToJsDate(value) {
1467
1528
  const truncValue = Math.trunc(value);
1468
- let date = new Date(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
1529
+ let date = DateTime.fromTimestamp(truncValue * MS_PER_DAY - DATE_JS_1900_OFFSET);
1469
1530
  let time = value - truncValue;
1470
1531
  time = time < 0 ? 1 + time : time;
1471
1532
  const hours = Math.round(time * 24);
@@ -1485,7 +1546,7 @@ function jsDateToNumber(date) {
1485
1546
  }
1486
1547
  /** Return the number of days in the current month of the given date */
1487
1548
  function getDaysInMonth(date) {
1488
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
1549
+ return new DateTime(date.getFullYear(), date.getMonth() + 1, 0).getDate();
1489
1550
  }
1490
1551
  function isLastDayOfMonth(date) {
1491
1552
  return getDaysInMonth(date) === date.getDate();
@@ -1503,7 +1564,7 @@ function addMonthsToDate(date, months, keepEndOfMonth) {
1503
1564
  const yStart = date.getFullYear();
1504
1565
  const mStart = date.getMonth();
1505
1566
  const dStart = date.getDate();
1506
- const jsDate = new Date(yStart, mStart + months);
1567
+ const jsDate = new DateTime(yStart, mStart + months, 1);
1507
1568
  if (keepEndOfMonth && dStart === getDaysInMonth(date)) {
1508
1569
  jsDate.setDate(getDaysInMonth(jsDate));
1509
1570
  }
@@ -2062,6 +2123,8 @@ exports.CommandResult = void 0;
2062
2123
  CommandResult["NoChanges"] = "NoChanges";
2063
2124
  })(exports.CommandResult || (exports.CommandResult = {}));
2064
2125
 
2126
+ const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
2127
+
2065
2128
  const DEFAULT_LOCALES = [
2066
2129
  {
2067
2130
  name: "English (US)",
@@ -2726,20 +2789,20 @@ function flattenRowFirst(items, callback) {
2726
2789
  }
2727
2790
 
2728
2791
  function toCriterionDateNumber(dateValue) {
2729
- const today = new Date();
2792
+ const today = DateTime.now();
2730
2793
  switch (dateValue) {
2731
2794
  case "today":
2732
2795
  return jsDateToNumber(today);
2733
2796
  case "yesterday":
2734
- return jsDateToNumber(new Date(today.setDate(today.getDate() - 1)));
2797
+ return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
2735
2798
  case "tomorrow":
2736
- return jsDateToNumber(new Date(today.setDate(today.getDate() + 1)));
2799
+ return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
2737
2800
  case "lastWeek":
2738
- return jsDateToNumber(new Date(today.setDate(today.getDate() - 7)));
2801
+ return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
2739
2802
  case "lastMonth":
2740
- return jsDateToNumber(new Date(today.setMonth(today.getMonth() - 1)));
2803
+ return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
2741
2804
  case "lastYear":
2742
- return jsDateToNumber(new Date(today.setFullYear(today.getFullYear() - 1)));
2805
+ return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
2743
2806
  }
2744
2807
  }
2745
2808
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -2825,6 +2888,9 @@ function parseFormat(formatString) {
2825
2888
  * Formats a cell value with its format.
2826
2889
  */
2827
2890
  function formatValue(value, { format, locale }) {
2891
+ if (format === PLAIN_TEXT_FORMAT) {
2892
+ return toString(value) || "";
2893
+ }
2828
2894
  switch (typeof value) {
2829
2895
  case "string":
2830
2896
  return value;
@@ -3097,7 +3163,7 @@ function formatJSTime(jsDate, format) {
3097
3163
  .map((p) => {
3098
3164
  switch (p) {
3099
3165
  case "hhhh":
3100
- const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY) / (60 * 60 * 1000));
3166
+ const helapsedHours = Math.floor((jsDate.getTime() - INITIAL_1900_DAY.getTime()) / (60 * 60 * 1000));
3101
3167
  return helapsedHours.toString();
3102
3168
  case "hh":
3103
3169
  return hours.toString().padStart(2, "0");
@@ -4665,6 +4731,9 @@ function transformRangeData(range, executed) {
4665
4731
 
4666
4732
  class ChartJsComponent extends owl.Component {
4667
4733
  static template = "o-spreadsheet-ChartJsComponent";
4734
+ static props = {
4735
+ figure: Object,
4736
+ };
4668
4737
  canvas = owl.useRef("graphContainer");
4669
4738
  chart;
4670
4739
  get background() {
@@ -4717,9 +4786,6 @@ class ChartJsComponent extends owl.Component {
4717
4786
  this.chart.update("active");
4718
4787
  }
4719
4788
  }
4720
- ChartJsComponent.props = {
4721
- figure: Object,
4722
- };
4723
4789
 
4724
4790
  /**
4725
4791
  * AbstractChart is the class from which every Chart should inherit.
@@ -5296,7 +5362,7 @@ function createScorecardChartRuntime(chart, getters) {
5296
5362
  };
5297
5363
  baselineCell = getters.getEvaluatedCell(baselinePosition);
5298
5364
  }
5299
- const background = getters.getBackgroundOfSingleCellChart(chart.background, chart.keyValue);
5365
+ const { background, fontColor } = getters.getStyleOfSingleCellChart(chart.background, chart.keyValue);
5300
5366
  const locale = getters.getLocale();
5301
5367
  return {
5302
5368
  title: _t(chart.title),
@@ -5305,7 +5371,7 @@ function createScorecardChartRuntime(chart, getters) {
5305
5371
  baselineArrow: getBaselineArrowDirection(baselineCell, keyValueCell, chart.baselineMode),
5306
5372
  baselineColor: getBaselineColor(baselineCell, chart.baselineMode, keyValueCell, chart.baselineColorUp, chart.baselineColorDown),
5307
5373
  baselineDescr: chart.baselineDescr ? _t(chart.baselineDescr) : "",
5308
- fontColor: chartFontColor(background),
5374
+ fontColor,
5309
5375
  background,
5310
5376
  baselineStyle: chart.baselineMode !== "percentage" && baseline
5311
5377
  ? getters.getCellStyle({
@@ -5570,6 +5636,9 @@ class KeyValueElement extends ScorecardScalableElement {
5570
5636
 
5571
5637
  class ScorecardChart extends owl.Component {
5572
5638
  static template = "o-spreadsheet-ScorecardChart";
5639
+ static props = {
5640
+ figure: Object,
5641
+ };
5573
5642
  canvas = owl.useRef("chartContainer");
5574
5643
  get runtime() {
5575
5644
  return this.env.model.getters.getChartRuntime(this.props.figure.id);
@@ -5587,9 +5656,6 @@ class ScorecardChart extends owl.Component {
5587
5656
  drawScoreChart(config, canvas);
5588
5657
  }
5589
5658
  }
5590
- ScorecardChart.props = {
5591
- figure: Object,
5592
- };
5593
5659
 
5594
5660
  /**
5595
5661
  * Registry
@@ -5731,6 +5797,9 @@ function detectLink(value) {
5731
5797
  }
5732
5798
 
5733
5799
  function evaluateLiteral(content, localeFormat) {
5800
+ if (localeFormat.format === PLAIN_TEXT_FORMAT) {
5801
+ return textCell(content || "", localeFormat);
5802
+ }
5734
5803
  return createEvaluatedCell(parseLiteral(content || "", localeFormat.locale), localeFormat);
5735
5804
  }
5736
5805
  function parseLiteral(content, locale) {
@@ -5765,6 +5834,9 @@ function createEvaluatedCell(value, localeFormat) {
5765
5834
  }
5766
5835
  function _createEvaluatedCell(value, localeFormat) {
5767
5836
  try {
5837
+ if (localeFormat.format === PLAIN_TEXT_FORMAT) {
5838
+ return textCell(toString(value), localeFormat);
5839
+ }
5768
5840
  for (const builder of builders) {
5769
5841
  const evaluateCell = builder(value, localeFormat);
5770
5842
  if (evaluateCell) {
@@ -6262,11 +6334,11 @@ css /* scss */ `
6262
6334
  class ErrorToolTip extends owl.Component {
6263
6335
  static maxSize = { maxHeight: ERROR_TOOLTIP_MAX_HEIGHT };
6264
6336
  static template = "o-spreadsheet-ErrorToolTip";
6337
+ static props = {
6338
+ errors: Array,
6339
+ onClosed: { type: Function, optional: true },
6340
+ };
6265
6341
  }
6266
- ErrorToolTip.props = {
6267
- errors: Array,
6268
- onClosed: { type: Function, optional: true },
6269
- };
6270
6342
  const ErrorToolTipPopoverBuilder = {
6271
6343
  onHover: (position, getters) => {
6272
6344
  const cell = getters.getEvaluatedCell(position);
@@ -6308,6 +6380,14 @@ css /*SCSS*/ `
6308
6380
  `;
6309
6381
  class FilterMenuValueItem extends owl.Component {
6310
6382
  static template = "o-spreadsheet-FilterMenuValueItem";
6383
+ static props = {
6384
+ value: String,
6385
+ isChecked: Boolean,
6386
+ isSelected: Boolean,
6387
+ onMouseMove: Function,
6388
+ onClick: Function,
6389
+ scrolledTo: { type: String, optional: true },
6390
+ };
6311
6391
  itemRef = owl.useRef("menuValueItem");
6312
6392
  setup() {
6313
6393
  owl.onWillPatch(() => {
@@ -6325,17 +6405,9 @@ class FilterMenuValueItem extends owl.Component {
6325
6405
  });
6326
6406
  }
6327
6407
  }
6328
- FilterMenuValueItem.props = {
6329
- value: String,
6330
- isChecked: Boolean,
6331
- isSelected: Boolean,
6332
- onMouseMove: Function,
6333
- onClick: Function,
6334
- scrolledTo: { type: String, optional: true },
6335
- };
6336
6408
 
6337
6409
  const FILTER_MENU_HEIGHT = 295;
6338
- const CSS$2 = css /* scss */ `
6410
+ const CSS = css /* scss */ `
6339
6411
  .o-filter-menu {
6340
6412
  box-sizing: border-box;
6341
6413
  padding: 8px 16px;
@@ -6431,9 +6503,12 @@ const CSS$2 = css /* scss */ `
6431
6503
  }
6432
6504
  `;
6433
6505
  class FilterMenu extends owl.Component {
6434
- static size = { width: MENU_WIDTH, height: FILTER_MENU_HEIGHT };
6435
6506
  static template = "o-spreadsheet-FilterMenu";
6436
- static style = CSS$2;
6507
+ static props = {
6508
+ filterPosition: Object,
6509
+ onClosed: { type: Function, optional: true },
6510
+ };
6511
+ static style = CSS;
6437
6512
  static components = { FilterMenuValueItem };
6438
6513
  state = owl.useState({
6439
6514
  values: [],
@@ -6585,10 +6660,6 @@ class FilterMenu extends owl.Component {
6585
6660
  this.props.onClosed?.();
6586
6661
  }
6587
6662
  }
6588
- FilterMenu.props = {
6589
- filterPosition: Object,
6590
- onClosed: { type: Function, optional: true },
6591
- };
6592
6663
  const FilterMenuPopoverBuilder = {
6593
6664
  onOpen: (position, getters) => {
6594
6665
  return {
@@ -6600,6 +6671,7 @@ const FilterMenuPopoverBuilder = {
6600
6671
  },
6601
6672
  };
6602
6673
 
6674
+ const macRegex = /Mac/i;
6603
6675
  /**
6604
6676
  * Return true if the event was triggered from
6605
6677
  * a child element.
@@ -6651,7 +6723,7 @@ const letterRegex = /^[a-zA-Z]$/;
6651
6723
  */
6652
6724
  function keyboardEventToShortcutString(ev, mode = "key") {
6653
6725
  let keyDownString = "";
6654
- if (ev.ctrlKey && ev.key !== "Ctrl")
6726
+ if (isCtrlKey(ev) && ev.key !== "Ctrl")
6655
6727
  keyDownString += "Ctrl+";
6656
6728
  if (ev.metaKey)
6657
6729
  keyDownString += "Ctrl+";
@@ -6664,7 +6736,15 @@ function keyboardEventToShortcutString(ev, mode = "key") {
6664
6736
  return keyDownString;
6665
6737
  }
6666
6738
  function isMacOS() {
6667
- return navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
6739
+ return Boolean(macRegex.test(navigator.userAgent));
6740
+ }
6741
+ /**
6742
+ * @param {KeyboardEvent | MouseEvent} ev
6743
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
6744
+ * On Mac, this is the "meta" or "command" key.
6745
+ */
6746
+ function isCtrlKey(ev) {
6747
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
6668
6748
  }
6669
6749
 
6670
6750
  /**
@@ -6781,6 +6861,19 @@ css /* scss */ `
6781
6861
  `;
6782
6862
  class Popover extends owl.Component {
6783
6863
  static template = "o-spreadsheet-Popover";
6864
+ static props = {
6865
+ anchorRect: Object,
6866
+ containerRect: { type: Object, optional: true },
6867
+ positioning: { type: String, optional: true },
6868
+ maxWidth: { type: Number, optional: true },
6869
+ maxHeight: { type: Number, optional: true },
6870
+ verticalOffset: { type: Number, optional: true },
6871
+ onMouseWheel: { type: Function, optional: true },
6872
+ onPopoverHidden: { type: Function, optional: true },
6873
+ onPopoverMoved: { type: Function, optional: true },
6874
+ zIndex: { type: Number, optional: true },
6875
+ slots: Object,
6876
+ };
6784
6877
  static defaultProps = {
6785
6878
  positioning: "BottomLeft",
6786
6879
  verticalOffset: 0,
@@ -6837,19 +6930,6 @@ class Popover extends owl.Component {
6837
6930
  });
6838
6931
  }
6839
6932
  }
6840
- Popover.props = {
6841
- anchorRect: Object,
6842
- containerRect: { type: Object, optional: true },
6843
- positioning: { type: String, optional: true },
6844
- maxWidth: { type: Number, optional: true },
6845
- maxHeight: { type: Number, optional: true },
6846
- verticalOffset: { type: Number, optional: true },
6847
- onMouseWheel: { type: Function, optional: true },
6848
- onPopoverHidden: { type: Function, optional: true },
6849
- onPopoverMoved: { type: Function, optional: true },
6850
- zIndex: { type: Number, optional: true },
6851
- slots: Object,
6852
- };
6853
6933
  class PopoverPositionContext {
6854
6934
  anchorRect;
6855
6935
  containerRect;
@@ -7031,6 +7111,15 @@ css /* scss */ `
7031
7111
  `;
7032
7112
  class Menu extends owl.Component {
7033
7113
  static template = "o-spreadsheet-Menu";
7114
+ static props = {
7115
+ position: Object,
7116
+ menuItems: Array,
7117
+ depth: { type: Number, optional: true },
7118
+ maxHeight: { type: Number, optional: true },
7119
+ onClose: Function,
7120
+ onMenuClicked: { type: Function, optional: true },
7121
+ menuId: { type: String, optional: true },
7122
+ };
7034
7123
  static components = { Menu, Popover };
7035
7124
  static defaultProps = {
7036
7125
  depth: 1,
@@ -7187,15 +7276,6 @@ class Menu extends owl.Component {
7187
7276
  }
7188
7277
  }
7189
7278
  }
7190
- Menu.props = {
7191
- position: Object,
7192
- menuItems: Array,
7193
- depth: { type: Number, optional: true },
7194
- maxHeight: { type: Number, optional: true },
7195
- onClose: Function,
7196
- onMenuClicked: { type: Function, optional: true },
7197
- menuId: { type: String, optional: true },
7198
- };
7199
7279
 
7200
7280
  const LINK_TOOLTIP_HEIGHT = 32;
7201
7281
  const LINK_TOOLTIP_WIDTH = 220;
@@ -7248,8 +7328,12 @@ css /* scss */ `
7248
7328
  }
7249
7329
  `;
7250
7330
  class LinkDisplay extends owl.Component {
7251
- static components = { Menu };
7252
7331
  static template = "o-spreadsheet-LinkDisplay";
7332
+ static props = {
7333
+ cellPosition: Object,
7334
+ onClosed: { type: Function, optional: true },
7335
+ };
7336
+ static components = { Menu };
7253
7337
  get cell() {
7254
7338
  const { col, row } = this.props.cellPosition;
7255
7339
  const sheetId = this.env.model.getters.getActiveSheetId();
@@ -7304,10 +7388,6 @@ const LinkCellPopoverBuilder = {
7304
7388
  };
7305
7389
  },
7306
7390
  };
7307
- LinkDisplay.props = {
7308
- cellPosition: Object,
7309
- onClosed: { type: Function, optional: true },
7310
- };
7311
7391
 
7312
7392
  /**
7313
7393
  * Tokenizer
@@ -7941,6 +8021,10 @@ css /* scss */ `
7941
8021
  `;
7942
8022
  class LinkEditor extends owl.Component {
7943
8023
  static template = "o-spreadsheet-LinkEditor";
8024
+ static props = {
8025
+ cellPosition: Object,
8026
+ onClosed: { type: Function, optional: true },
8027
+ };
7944
8028
  static components = { Menu };
7945
8029
  menuItems = linkMenuRegistry.getMenuItems();
7946
8030
  link = owl.useState(this.defaultState);
@@ -8038,10 +8122,6 @@ const LinkEditorPopoverBuilder = {
8038
8122
  };
8039
8123
  },
8040
8124
  };
8041
- LinkEditor.props = {
8042
- cellPosition: Object,
8043
- onClosed: { type: Function, optional: true },
8044
- };
8045
8125
 
8046
8126
  const cellPopoverRegistry = new Registry();
8047
8127
  cellPopoverRegistry
@@ -8324,6 +8404,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale })
8324
8404
  labels: labels.map(truncateLabel),
8325
8405
  datasets: [],
8326
8406
  },
8407
+ platform: undefined,
8327
8408
  plugins: [],
8328
8409
  };
8329
8410
  }
@@ -8903,7 +8984,7 @@ function createGaugeChartRuntime(chart, getters) {
8903
8984
  });
8904
8985
  return {
8905
8986
  chartJsConfig: config,
8906
- background: getters.getBackgroundOfSingleCellChart(chart.background, dataRange),
8987
+ background: getters.getStyleOfSingleCellChart(chart.background, dataRange).background,
8907
8988
  };
8908
8989
  }
8909
8990
 
@@ -8984,7 +9065,7 @@ function getFormatMinDisplayUnit(format) {
8984
9065
  else if (format.includes("h") || format.includes("H")) {
8985
9066
  return "hour";
8986
9067
  }
8987
- else if (format.includes("D")) {
9068
+ else if (format.includes("d")) {
8988
9069
  return "day";
8989
9070
  }
8990
9071
  else if (format.includes("M")) {
@@ -9496,6 +9577,27 @@ function calculatePercentage(dataset, dataIndex) {
9496
9577
  const percentage = (dataset[dataIndex] / total) * 100;
9497
9578
  return percentage.toFixed(2);
9498
9579
  }
9580
+ function filterNegativeValues(labels, datasets) {
9581
+ const dataPointsIndexes = labels.reduce((indexes, label, i) => {
9582
+ const shouldKeep = datasets.some((dataset) => {
9583
+ const dataPoint = dataset.data[i];
9584
+ return typeof dataPoint !== "number" || dataPoint >= 0;
9585
+ });
9586
+ if (shouldKeep) {
9587
+ indexes.push(i);
9588
+ }
9589
+ return indexes;
9590
+ }, []);
9591
+ const filteredLabels = dataPointsIndexes.map((i) => labels[i] || "");
9592
+ const filteredDatasets = datasets.map((dataset) => ({
9593
+ ...dataset,
9594
+ data: dataPointsIndexes.map((i) => {
9595
+ const dataPoint = dataset.data[i];
9596
+ return typeof dataPoint !== "number" || dataPoint >= 0 ? dataPoint : 0;
9597
+ }),
9598
+ }));
9599
+ return { labels: filteredLabels, dataSetsValues: filteredDatasets };
9600
+ }
9499
9601
  function createPieChartRuntime(chart, getters) {
9500
9602
  const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
9501
9603
  let labels = labelValues.formattedValues;
@@ -9509,6 +9611,7 @@ function createPieChartRuntime(chart, getters) {
9509
9611
  if (chart.aggregated) {
9510
9612
  ({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
9511
9613
  }
9614
+ ({ dataSetsValues, labels } = filterNegativeValues(labels, dataSetsValues));
9512
9615
  const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
9513
9616
  const locale = getters.getLocale();
9514
9617
  const config = getPieConfiguration(chart, labels, { format: dataSetFormat, locale });
@@ -9606,6 +9709,10 @@ css /* scss */ `
9606
9709
  `;
9607
9710
  class ChartFigure extends owl.Component {
9608
9711
  static template = "o-spreadsheet-ChartFigure";
9712
+ static props = {
9713
+ figure: Object,
9714
+ onFigureDeleted: Function,
9715
+ };
9609
9716
  static components = {};
9610
9717
  onDoubleClick() {
9611
9718
  this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
@@ -9623,13 +9730,13 @@ class ChartFigure extends owl.Component {
9623
9730
  return component;
9624
9731
  }
9625
9732
  }
9626
- ChartFigure.props = {
9627
- figure: Object,
9628
- onFigureDeleted: Function,
9629
- };
9630
9733
 
9631
9734
  class ImageFigure extends owl.Component {
9632
9735
  static template = "o-spreadsheet-ImageFigure";
9736
+ static props = {
9737
+ figure: Object,
9738
+ onFigureDeleted: Function,
9739
+ };
9633
9740
  static components = {};
9634
9741
  // ---------------------------------------------------------------------------
9635
9742
  // Getters
@@ -9641,10 +9748,6 @@ class ImageFigure extends owl.Component {
9641
9748
  return this.env.model.getters.getImagePath(this.figureId);
9642
9749
  }
9643
9750
  }
9644
- ImageFigure.props = {
9645
- figure: Object,
9646
- onFigureDeleted: Function,
9647
- };
9648
9751
 
9649
9752
  function centerFigurePosition(getters, size) {
9650
9753
  const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
@@ -10346,7 +10449,7 @@ function setStyle(env, style) {
10346
10449
  // Simple actions
10347
10450
  //------------------------------------------------------------------------------
10348
10451
  const PASTE_ACTION = async (env) => paste$1(env);
10349
- const PASTE_VALUE_ACTION = async (env) => paste$1(env, "onlyValue");
10452
+ const PASTE_AS_VALUE_ACTION = async (env) => paste$1(env, "asValue");
10350
10453
  async function paste$1(env, pasteOption) {
10351
10454
  const spreadsheetClipboard = env.model.getters.getClipboardTextContent();
10352
10455
  const osClipboard = await env.clipboard.readText();
@@ -10359,7 +10462,7 @@ async function paste$1(env, pasteOption) {
10359
10462
  else {
10360
10463
  interactivePaste(env, target, pasteOption);
10361
10464
  }
10362
- if (env.model.getters.isCutOperation() && pasteOption !== "onlyValue") {
10465
+ if (env.model.getters.isCutOperation() && pasteOption !== "asValue") {
10363
10466
  await env.clipboard.write({ [ClipboardMIMEType.PlainText]: "" });
10364
10467
  }
10365
10468
  break;
@@ -10774,9 +10877,9 @@ const pasteSpecial = {
10774
10877
  icon: "o-spreadsheet-Icon.PASTE",
10775
10878
  };
10776
10879
  const pasteSpecialValue = {
10777
- name: _t("Paste value only"),
10880
+ name: _t("Paste as value"),
10778
10881
  description: "Ctrl+Shift+V",
10779
- execute: PASTE_VALUE_ACTION,
10882
+ execute: PASTE_AS_VALUE_ACTION,
10780
10883
  };
10781
10884
  const pasteSpecialFormat = {
10782
10885
  name: _t("Paste format only"),
@@ -10932,6 +11035,9 @@ function arg(definition, description = "") {
10932
11035
  function makeArg(str, description) {
10933
11036
  let parts = str.match(ARG_REGEXP);
10934
11037
  let name = parts[1].trim();
11038
+ if (!name) {
11039
+ throw new Error(`Function argument definition is missing a name: '${str}'.`);
11040
+ }
10935
11041
  let types = [];
10936
11042
  let isOptional = false;
10937
11043
  let isRepeating = false;
@@ -14654,7 +14760,7 @@ const DATE = {
14654
14760
  if (_year < 1900) {
14655
14761
  _year += 1900;
14656
14762
  }
14657
- const jsDate = new Date(_year, _month - 1, _day);
14763
+ const jsDate = new DateTime(_year, _month - 1, _day);
14658
14764
  const result = jsDateToRoundNumber(jsDate);
14659
14765
  assert(() => result >= 0, _t("The function [[FUNCTION_NAME]] result must be greater than or equal 01/01/1900."));
14660
14766
  return result;
@@ -14697,7 +14803,7 @@ const DATEDIF = {
14697
14803
  // See: https://support.microsoft.com/en-us/office/datedif-function-25dba1a4-2812-480b-84dd-8b32a451b35c
14698
14804
  let days = jsEndDate.getDate() - jsStartDate.getDate();
14699
14805
  if (days < 0) {
14700
- const monthBeforeEndMonth = new Date(jsEndDate.getFullYear(), jsEndDate.getMonth() - 1, 1);
14806
+ const monthBeforeEndMonth = new DateTime(jsEndDate.getFullYear(), jsEndDate.getMonth() - 1, 1);
14701
14807
  const daysInMonthBeforeEndMonth = getDaysInMonth(monthBeforeEndMonth);
14702
14808
  days = daysInMonthBeforeEndMonth - Math.abs(days);
14703
14809
  }
@@ -14706,7 +14812,7 @@ const DATEDIF = {
14706
14812
  if (areTwoDatesWithinOneYear(_startDate, _endDate)) {
14707
14813
  return getTimeDifferenceInWholeDays(jsStartDate, jsEndDate);
14708
14814
  }
14709
- const endDateWithinOneYear = new Date(jsStartDate.getFullYear(), jsEndDate.getMonth(), jsEndDate.getDate());
14815
+ const endDateWithinOneYear = new DateTime(jsStartDate.getFullYear(), jsEndDate.getMonth(), jsEndDate.getDate());
14710
14816
  let days = getTimeDifferenceInWholeDays(jsStartDate, endDateWithinOneYear);
14711
14817
  if (days < 0) {
14712
14818
  endDateWithinOneYear.setFullYear(jsStartDate.getFullYear() + 1);
@@ -14823,7 +14929,7 @@ const EOMONTH = {
14823
14929
  const _months = Math.trunc(toNumber(months, this.locale));
14824
14930
  const yStart = _startDate.getFullYear();
14825
14931
  const mStart = _startDate.getMonth();
14826
- const jsDate = new Date(yStart, mStart + _months + 1, 0);
14932
+ const jsDate = new DateTime(yStart, mStart + _months + 1, 0);
14827
14933
  return jsDateToRoundNumber(jsDate);
14828
14934
  },
14829
14935
  isExported: true,
@@ -14860,17 +14966,17 @@ const ISOWEEKNUM = {
14860
14966
  // The first week of the year is the week that contains the first
14861
14967
  // Thursday of the year.
14862
14968
  let firstThursday = 1;
14863
- while (new Date(y, 0, firstThursday).getDay() !== 4) {
14969
+ while (new DateTime(y, 0, firstThursday).getDay() !== 4) {
14864
14970
  firstThursday += 1;
14865
14971
  }
14866
- const firstDayOfFirstWeek = new Date(y, 0, firstThursday - 3);
14972
+ const firstDayOfFirstWeek = new DateTime(y, 0, firstThursday - 3);
14867
14973
  // The last week of the year is the week that contains the last Thursday of
14868
14974
  // the year.
14869
14975
  let lastThursday = 31;
14870
- while (new Date(y, 11, lastThursday).getDay() !== 4) {
14976
+ while (new DateTime(y, 11, lastThursday).getDay() !== 4) {
14871
14977
  lastThursday -= 1;
14872
14978
  }
14873
- const lastDayOfLastWeek = new Date(y, 11, lastThursday + 3);
14979
+ const lastDayOfLastWeek = new DateTime(y, 11, lastThursday + 3);
14874
14980
  // B - If our date > lastDayOfLastWeek then it's in the weeks of the year after
14875
14981
  // If our date < firstDayOfFirstWeek then it's in the weeks of the year before
14876
14982
  let offsetYear;
@@ -14896,17 +15002,17 @@ const ISOWEEKNUM = {
14896
15002
  case 1:
14897
15003
  // firstDay is the 1st day of the 1st week of the year after
14898
15004
  // firstDay = lastDayOfLastWeek + 1 Day
14899
- firstDay = new Date(y, 11, lastThursday + 3 + 1);
15005
+ firstDay = new DateTime(y, 11, lastThursday + 3 + 1);
14900
15006
  break;
14901
15007
  case -1:
14902
15008
  // firstDay is the 1st day of the 1st week of the previous year.
14903
15009
  // The first week of the previous year is the week that contains the
14904
15010
  // first Thursday of the previous year.
14905
15011
  let firstThursdayPreviousYear = 1;
14906
- while (new Date(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
15012
+ while (new DateTime(y - 1, 0, firstThursdayPreviousYear).getDay() !== 4) {
14907
15013
  firstThursdayPreviousYear += 1;
14908
15014
  }
14909
- firstDay = new Date(y - 1, 0, firstThursdayPreviousYear - 3);
15015
+ firstDay = new DateTime(y - 1, 0, firstThursdayPreviousYear - 3);
14910
15016
  break;
14911
15017
  }
14912
15018
  const diff = (_date.getTime() - firstDay.getTime()) / MS_PER_DAY;
@@ -15041,8 +15147,8 @@ const NETWORKDAYS_INTL = {
15041
15147
  });
15042
15148
  }
15043
15149
  const invertDate = _startDate.getTime() > _endDate.getTime();
15044
- const stopDate = new Date((invertDate ? _startDate : _endDate).getTime());
15045
- let stepDate = new Date((invertDate ? _endDate : _startDate).getTime());
15150
+ const stopDate = DateTime.fromTimestamp((invertDate ? _startDate : _endDate).getTime());
15151
+ let stepDate = DateTime.fromTimestamp((invertDate ? _endDate : _startDate).getTime());
15046
15152
  const timeStopDate = stopDate.getTime();
15047
15153
  let timeStepDate = stepDate.getTime();
15048
15154
  let netWorkingDay = 0;
@@ -15068,8 +15174,7 @@ const NOW = {
15068
15174
  return getDateTimeFormat(this.locale);
15069
15175
  },
15070
15176
  compute: function () {
15071
- let today = new Date();
15072
- today.setMilliseconds(0);
15177
+ let today = DateTime.now();
15073
15178
  const delta = today.getTime() - INITIAL_1900_DAY.getTime();
15074
15179
  const time = today.getHours() / 24 + today.getMinutes() / 1440 + today.getSeconds() / 86400;
15075
15180
  return Math.floor(delta / MS_PER_DAY) + time;
@@ -15143,8 +15248,8 @@ const TODAY = {
15143
15248
  return this.locale.dateFormat;
15144
15249
  },
15145
15250
  compute: function () {
15146
- const today = new Date();
15147
- const jsDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
15251
+ const today = DateTime.now();
15252
+ const jsDate = new DateTime(today.getFullYear(), today.getMonth(), today.getDate());
15148
15253
  return jsDateToRoundNumber(jsDate);
15149
15254
  },
15150
15255
  isExported: true,
@@ -15199,10 +15304,10 @@ const WEEKNUM = {
15199
15304
  }
15200
15305
  const y = _date.getFullYear();
15201
15306
  let dayStart = 1;
15202
- let startDayOfFirstWeek = new Date(y, 0, dayStart);
15307
+ let startDayOfFirstWeek = new DateTime(y, 0, dayStart);
15203
15308
  while (startDayOfFirstWeek.getDay() !== startDayOfWeek) {
15204
15309
  dayStart += 1;
15205
- startDayOfFirstWeek = new Date(y, 0, dayStart);
15310
+ startDayOfFirstWeek = new DateTime(y, 0, dayStart);
15206
15311
  }
15207
15312
  const dif = (_date.getTime() - startDayOfFirstWeek.getTime()) / MS_PER_DAY;
15208
15313
  if (dif < 0) {
@@ -15260,7 +15365,7 @@ const WORKDAY_INTL = {
15260
15365
  timesHoliday.add(holiday.getTime());
15261
15366
  });
15262
15367
  }
15263
- let stepDate = new Date(_startDate.getTime());
15368
+ let stepDate = DateTime.fromTimestamp(_startDate.getTime());
15264
15369
  let timeStepDate = stepDate.getTime();
15265
15370
  const unitDay = Math.sign(_numDays);
15266
15371
  let stepDay = Math.abs(_numDays);
@@ -15324,7 +15429,7 @@ const MONTH_START = {
15324
15429
  const _startDate = toJsDate(date, this.locale);
15325
15430
  const yStart = _startDate.getFullYear();
15326
15431
  const mStart = _startDate.getMonth();
15327
- const jsDate = new Date(yStart, mStart, 1);
15432
+ const jsDate = new DateTime(yStart, mStart, 1);
15328
15433
  return jsDateToRoundNumber(jsDate);
15329
15434
  },
15330
15435
  };
@@ -15366,7 +15471,7 @@ const QUARTER_START = {
15366
15471
  compute: function (date) {
15367
15472
  const quarter = QUARTER.compute.bind(this)(date);
15368
15473
  const year = YEAR.compute.bind(this)(date);
15369
- const jsDate = new Date(year, (quarter - 1) * 3, 1);
15474
+ const jsDate = new DateTime(year, (quarter - 1) * 3, 1);
15370
15475
  return jsDateToRoundNumber(jsDate);
15371
15476
  },
15372
15477
  };
@@ -15383,7 +15488,7 @@ const QUARTER_END = {
15383
15488
  compute: function (date) {
15384
15489
  const quarter = QUARTER.compute.bind(this)(date);
15385
15490
  const year = YEAR.compute.bind(this)(date);
15386
- const jsDate = new Date(year, quarter * 3, 0);
15491
+ const jsDate = new DateTime(year, quarter * 3, 0);
15387
15492
  return jsDateToRoundNumber(jsDate);
15388
15493
  },
15389
15494
  };
@@ -15399,7 +15504,7 @@ const YEAR_START = {
15399
15504
  },
15400
15505
  compute: function (date) {
15401
15506
  const year = YEAR.compute.bind(this)(date);
15402
- const jsDate = new Date(year, 0, 1);
15507
+ const jsDate = new DateTime(year, 0, 1);
15403
15508
  return jsDateToRoundNumber(jsDate);
15404
15509
  },
15405
15510
  };
@@ -15415,7 +15520,7 @@ const YEAR_END = {
15415
15520
  },
15416
15521
  compute: function (date) {
15417
15522
  const year = YEAR.compute.bind(this)(date);
15418
- const jsDate = new Date(year + 1, 0, 0);
15523
+ const jsDate = new DateTime(year + 1, 0, 0);
15419
15524
  return jsDateToRoundNumber(jsDate);
15420
15525
  },
15421
15526
  };
@@ -15463,8 +15568,8 @@ const DEFAULT_DELTA_ARG = 0;
15463
15568
  const DELTA = {
15464
15569
  description: _t("Compare two numeric values, returning 1 if they're equal."),
15465
15570
  args: [
15466
- arg(" (number)", _t("The first number to compare.")),
15467
- arg(` (number, default=${DEFAULT_DELTA_ARG})`, _t("The second number to compare.")),
15571
+ arg("number1 (number)", _t("The first number to compare.")),
15572
+ arg(`number2 (number, default=${DEFAULT_DELTA_ARG})`, _t("The second number to compare.")),
15468
15573
  ],
15469
15574
  returns: ["NUMBER"],
15470
15575
  compute: function (number1, number2 = DEFAULT_DELTA_ARG) {
@@ -15891,7 +15996,7 @@ function assertDeprecationFactorStrictlyPositive(factor) {
15891
15996
  function assertSettlementLessThanOneYearBeforeMaturity(settlement, maturity, locale) {
15892
15997
  const startDate = toJsDate(settlement, locale);
15893
15998
  const endDate = toJsDate(maturity, locale);
15894
- const startDatePlusOneYear = new Date(startDate);
15999
+ const startDatePlusOneYear = toJsDate(settlement, locale);
15895
16000
  startDatePlusOneYear.setFullYear(startDate.getFullYear() + 1);
15896
16001
  assert(() => endDate.getTime() <= startDatePlusOneYear.getTime(), _t("The settlement date (%s) must at most one year after the maturity date (%s).", settlement.toString(), maturity.toString()));
15897
16002
  }
@@ -16026,7 +16131,7 @@ const AMORLINC = {
16026
16131
  arg("salvage (number)", _t("The value of the asset at the end of depreciation.")),
16027
16132
  arg("period (number)", _t("The single period within life for which to calculate depreciation.")),
16028
16133
  arg("rate (number)", _t("The deprecation rate.")),
16029
- arg(" (number, optional)", _t("An indicator of what day count method to use.")),
16134
+ arg("day_count_convention (number, optional)", _t("An indicator of what day count method to use.")),
16030
16135
  ],
16031
16136
  returns: ["NUMBER"],
16032
16137
  compute: function (cost, purchaseDate, firstPeriodEnd, salvage, period, rate, dayCountConvention = DEFAULT_DAY_COUNT_CONVENTION) {
@@ -19198,8 +19303,8 @@ const MID = {
19198
19303
  description: _t("A segment of a string."),
19199
19304
  args: [
19200
19305
  arg("text (string)", _t("The string to extract a segment from.")),
19201
- arg(" (number)", _t("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
19202
- arg(" (number)", _t("The length of the segment to extract.")),
19306
+ arg("starting_at (number)", _t("The index from the left of string from which to begin extracting. The first character in string has the index 1.")),
19307
+ arg("extract_length (number)", _t("The length of the segment to extract.")),
19203
19308
  ],
19204
19309
  returns: ["STRING"],
19205
19310
  compute: function (text, starting_at, extract_length) {
@@ -20015,6 +20120,11 @@ const formatNumberAutomatic = {
20015
20120
  execute: (env) => setFormatter(env, ""),
20016
20121
  isActive: (env) => isAutomaticFormatSelected(env),
20017
20122
  };
20123
+ const formatNumberPlainText = {
20124
+ name: _t("Plain text"),
20125
+ execute: (env) => setFormatter(env, PLAIN_TEXT_FORMAT),
20126
+ isActive: (env) => isFormatSelected(env, PLAIN_TEXT_FORMAT),
20127
+ };
20018
20128
  const formatNumberNumber = createFormatActionSpec({
20019
20129
  name: _t("Number"),
20020
20130
  descriptionValue: 1000.12,
@@ -20374,6 +20484,7 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
20374
20484
  formatNumberFullWeekDayAndMonth: formatNumberFullWeekDayAndMonth,
20375
20485
  formatNumberNumber: formatNumberNumber,
20376
20486
  formatNumberPercent: formatNumberPercent,
20487
+ formatNumberPlainText: formatNumberPlainText,
20377
20488
  formatNumberShortMonth: formatNumberShortMonth,
20378
20489
  formatNumberShortWeekDay: formatNumberShortWeekDay,
20379
20490
  formatNumberTime: formatNumberTime,
@@ -20804,6 +20915,10 @@ numberFormatMenuRegistry
20804
20915
  .add("format_number_automatic", {
20805
20916
  ...formatNumberAutomatic,
20806
20917
  sequence: 10,
20918
+ })
20919
+ .add("format_number_plain_text", {
20920
+ ...formatNumberPlainText,
20921
+ sequence: 15,
20807
20922
  separator: true,
20808
20923
  })
20809
20924
  .add("format_number_number", {
@@ -21402,10 +21517,10 @@ const arrowMap = {
21402
21517
  function updateSelectionWithArrowKeys(ev, selection) {
21403
21518
  const direction = arrowMap[ev.key];
21404
21519
  if (ev.shiftKey) {
21405
- selection.resizeAnchorZone(direction, ev.ctrlKey ? "end" : 1);
21520
+ selection.resizeAnchorZone(direction, isCtrlKey(ev) ? "end" : 1);
21406
21521
  }
21407
21522
  else {
21408
- selection.moveAnchorCell(direction, ev.ctrlKey ? "end" : 1);
21523
+ selection.moveAnchorCell(direction, isCtrlKey(ev) ? "end" : 1);
21409
21524
  }
21410
21525
  }
21411
21526
 
@@ -21472,6 +21587,15 @@ css /* scss */ `
21472
21587
  */
21473
21588
  class SelectionInput extends owl.Component {
21474
21589
  static template = "o-spreadsheet-SelectionInput";
21590
+ static props = {
21591
+ ranges: Function,
21592
+ hasSingleRange: { type: Boolean, optional: true },
21593
+ required: { type: Boolean, optional: true },
21594
+ isInvalid: { type: Boolean, optional: true },
21595
+ class: { type: String, optional: true },
21596
+ onSelectionChanged: { type: Function, optional: true },
21597
+ onSelectionConfirmed: { type: Function, optional: true },
21598
+ };
21475
21599
  id = uuidGenerator$1.uuidv4();
21476
21600
  previousRanges = this.props.ranges() || [];
21477
21601
  originSheet = this.env.model.getters.getActiveSheetId();
@@ -21634,15 +21758,6 @@ class SelectionInput extends owl.Component {
21634
21758
  this.env.model.dispatch("UNFOCUS_SELECTION_INPUT");
21635
21759
  }
21636
21760
  }
21637
- SelectionInput.props = {
21638
- ranges: Function,
21639
- hasSingleRange: { type: Boolean, optional: true },
21640
- required: { type: Boolean, optional: true },
21641
- isInvalid: { type: Boolean, optional: true },
21642
- class: { type: String, optional: true },
21643
- onSelectionChanged: { type: Function, optional: true },
21644
- onSelectionConfirmed: { type: Function, optional: true },
21645
- };
21646
21761
 
21647
21762
  css /* scss */ `
21648
21763
  .o-validation-error,
@@ -21658,6 +21773,10 @@ css /* scss */ `
21658
21773
  `;
21659
21774
  class ValidationMessages extends owl.Component {
21660
21775
  static template = "o-spreadsheet-ValidationMessages";
21776
+ static props = {
21777
+ messages: Array,
21778
+ msgType: String,
21779
+ };
21661
21780
  get divClasses() {
21662
21781
  if (this.props.msgType === "warning") {
21663
21782
  return "o-validation-warning text-warning";
@@ -21665,14 +21784,96 @@ class ValidationMessages extends owl.Component {
21665
21784
  return "o-validation-error text-danger";
21666
21785
  }
21667
21786
  }
21668
- ValidationMessages.props = {
21669
- messages: Array,
21670
- msgType: String,
21671
- };
21787
+
21788
+ css /* scss */ `
21789
+ .o-checkbox {
21790
+ display: flex;
21791
+ justify-items: center;
21792
+ input {
21793
+ margin-right: 5px;
21794
+ }
21795
+ }
21796
+ `;
21797
+ class Checkbox extends owl.Component {
21798
+ static template = "o-spreadsheet.Checkbox";
21799
+ static props = {
21800
+ label: { type: String, optional: true },
21801
+ value: { type: Boolean, optional: true },
21802
+ className: { type: String, optional: true },
21803
+ name: { type: String, optional: true },
21804
+ onChange: Function,
21805
+ };
21806
+ static defaultProps = { value: false };
21807
+ onChange(ev) {
21808
+ const value = ev.target.checked;
21809
+ this.props.onChange(value);
21810
+ }
21811
+ }
21812
+
21813
+ class Section extends owl.Component {
21814
+ static template = "o_spreadsheet.Section";
21815
+ static props = {
21816
+ class: { type: String, optional: true },
21817
+ slots: Object,
21818
+ };
21819
+ }
21820
+
21821
+ class ChartDataSeries extends owl.Component {
21822
+ static template = "o-spreadsheet.ChartDataSeries";
21823
+ static components = { SelectionInput, Section };
21824
+ static props = {
21825
+ ranges: Function,
21826
+ hasSingleRange: { type: Boolean, optional: true },
21827
+ onSelectionChanged: Function,
21828
+ onSelectionConfirmed: Function,
21829
+ };
21830
+ get title() {
21831
+ return this.props.hasSingleRange ? _t("Data range") : _t("Data series");
21832
+ }
21833
+ }
21834
+
21835
+ class ChartErrorSection extends owl.Component {
21836
+ static template = "o-spreadsheet.ChartErrorSection";
21837
+ static components = { Section, ValidationMessages };
21838
+ static props = { messages: { type: Array, element: String } };
21839
+ }
21840
+
21841
+ class ChartLabelRange extends owl.Component {
21842
+ static template = "o-spreadsheet.ChartLabelRange";
21843
+ static components = { SelectionInput, Checkbox, Section };
21844
+ static props = {
21845
+ title: { type: String, optional: true },
21846
+ range: Function,
21847
+ isInvalid: Boolean,
21848
+ required: { type: Boolean, optional: true },
21849
+ onSelectionChanged: Function,
21850
+ onSelectionConfirmed: Function,
21851
+ options: { type: Array, optional: true },
21852
+ };
21853
+ static defaultProps = {
21854
+ title: _t("Categories / Labels"),
21855
+ options: [],
21856
+ required: false,
21857
+ };
21858
+ }
21672
21859
 
21673
21860
  class LineBarPieConfigPanel extends owl.Component {
21674
21861
  static template = "o-spreadsheet-LineBarPieConfigPanel";
21675
- static components = { SelectionInput, ValidationMessages };
21862
+ static components = {
21863
+ SelectionInput,
21864
+ ValidationMessages,
21865
+ ChartDataSeries,
21866
+ ChartLabelRange,
21867
+ Section,
21868
+ Checkbox,
21869
+ ChartErrorSection,
21870
+ };
21871
+ static props = {
21872
+ figureId: String,
21873
+ definition: Object,
21874
+ updateChart: Function,
21875
+ canUpdateChart: Function,
21876
+ };
21676
21877
  state = owl.useState({
21677
21878
  datasetDispatchResult: undefined,
21678
21879
  labelsDispatchResult: undefined,
@@ -21696,9 +21897,22 @@ class LineBarPieConfigPanel extends owl.Component {
21696
21897
  get isLabelInvalid() {
21697
21898
  return !!this.state.labelsDispatchResult?.isCancelledBecause("InvalidLabelRange" /* CommandResult.InvalidLabelRange */);
21698
21899
  }
21699
- onUpdateDataSetsHaveTitle(ev) {
21900
+ get dataSetsHaveTitleLabel() {
21901
+ return _t("Use row %s as headers", this.calculateHeaderPosition() || "");
21902
+ }
21903
+ getLabelRangeOptions() {
21904
+ return [
21905
+ {
21906
+ name: "aggregated",
21907
+ label: _t("Aggregate"),
21908
+ value: this.props.definition.aggregated,
21909
+ onChange: this.onUpdateAggregated.bind(this),
21910
+ },
21911
+ ];
21912
+ }
21913
+ onUpdateDataSetsHaveTitle(dataSetsHaveTitle) {
21700
21914
  this.props.updateChart(this.props.figureId, {
21701
- dataSetsHaveTitle: ev.target.checked,
21915
+ dataSetsHaveTitle,
21702
21916
  });
21703
21917
  }
21704
21918
  /**
@@ -21738,9 +21952,9 @@ class LineBarPieConfigPanel extends owl.Component {
21738
21952
  getLabelRange() {
21739
21953
  return this.labelRange || "";
21740
21954
  }
21741
- onUpdateAggregated(ev) {
21955
+ onUpdateAggregated(aggregated) {
21742
21956
  this.props.updateChart(this.props.figureId, {
21743
- aggregated: ev.target.checked,
21957
+ aggregated,
21744
21958
  });
21745
21959
  }
21746
21960
  calculateHeaderPosition() {
@@ -21760,23 +21974,20 @@ class LineBarPieConfigPanel extends owl.Component {
21760
21974
  return undefined;
21761
21975
  }
21762
21976
  }
21763
- LineBarPieConfigPanel.props = {
21764
- figureId: String,
21765
- definition: Object,
21766
- updateChart: Function,
21767
- canUpdateChart: Function,
21768
- };
21769
21977
 
21770
21978
  class BarConfigPanel extends LineBarPieConfigPanel {
21771
21979
  static template = "o-spreadsheet-BarConfigPanel";
21772
- onUpdateStacked(ev) {
21980
+ get stackedLabel() {
21981
+ return _t("Stacked barchart");
21982
+ }
21983
+ onUpdateStacked(stacked) {
21773
21984
  this.props.updateChart(this.props.figureId, {
21774
- stacked: ev.target.checked,
21985
+ stacked,
21775
21986
  });
21776
21987
  }
21777
- onUpdateAggregated(ev) {
21988
+ onUpdateAggregated(aggregated) {
21778
21989
  this.props.updateChart(this.props.figureId, {
21779
- aggregated: ev.target.checked,
21990
+ aggregated,
21780
21991
  });
21781
21992
  }
21782
21993
  }
@@ -22104,6 +22315,12 @@ css /* scss */ `
22104
22315
  `;
22105
22316
  class ColorPicker extends owl.Component {
22106
22317
  static template = "o-spreadsheet-ColorPicker";
22318
+ static props = {
22319
+ onColorPicked: Function,
22320
+ currentColor: { type: String, optional: true },
22321
+ maxHeight: { type: Number, optional: true },
22322
+ anchorRect: Object,
22323
+ };
22107
22324
  static defaultProps = { currentColor: "" };
22108
22325
  static components = { Popover };
22109
22326
  COLORS = COLOR_PICKER_DEFAULTS;
@@ -22239,12 +22456,6 @@ class ColorPicker extends owl.Component {
22239
22456
  return isSameColor(color1, color2);
22240
22457
  }
22241
22458
  }
22242
- ColorPicker.props = {
22243
- onColorPicked: Function,
22244
- currentColor: { type: String, optional: true },
22245
- maxHeight: { type: Number, optional: true },
22246
- anchorRect: Object,
22247
- };
22248
22459
 
22249
22460
  css /* scss */ `
22250
22461
  .o-color-picker-widget {
@@ -22282,6 +22493,17 @@ css /* scss */ `
22282
22493
  `;
22283
22494
  class ColorPickerWidget extends owl.Component {
22284
22495
  static template = "o-spreadsheet-ColorPickerWidget";
22496
+ static props = {
22497
+ currentColor: { type: String, optional: true },
22498
+ toggleColorPicker: Function,
22499
+ showColorPicker: Boolean,
22500
+ onColorPicked: Function,
22501
+ icon: String,
22502
+ title: { type: String, optional: true },
22503
+ disabled: { type: Boolean, optional: true },
22504
+ dropdownMaxHeight: { type: Number, optional: true },
22505
+ class: { type: String, optional: true },
22506
+ };
22285
22507
  static components = { ColorPicker };
22286
22508
  colorPickerButtonRef = owl.useRef("colorPickerButton");
22287
22509
  get iconStyle() {
@@ -22300,44 +22522,55 @@ class ColorPickerWidget extends owl.Component {
22300
22522
  };
22301
22523
  }
22302
22524
  }
22303
- ColorPickerWidget.props = {
22304
- currentColor: { type: String, optional: true },
22305
- toggleColorPicker: Function,
22306
- showColorPicker: Boolean,
22307
- onColorPicked: Function,
22308
- icon: String,
22309
- title: { type: String, optional: true },
22310
- disabled: { type: Boolean, optional: true },
22311
- dropdownMaxHeight: { type: Number, optional: true },
22312
- class: { type: String, optional: true },
22313
- };
22314
22525
 
22315
- class LineBarPieDesignPanel extends owl.Component {
22316
- static template = "o-spreadsheet-LineBarPieDesignPanel";
22317
- static components = { ColorPickerWidget };
22318
- state = owl.useState({
22319
- title: "",
22320
- fillColorTool: false,
22321
- });
22322
- onClick(ev) {
22323
- this.state.fillColorTool = false;
22324
- }
22526
+ class ChartColor extends owl.Component {
22527
+ static template = "o-spreadsheet.ChartColor";
22528
+ static components = { ColorPickerWidget, Section };
22529
+ static props = {
22530
+ currentColor: { type: String, optional: true },
22531
+ onColorPicked: Function,
22532
+ };
22533
+ state;
22325
22534
  setup() {
22326
- this.state.title = _t(this.props.definition.title);
22327
- owl.useExternalListener(window, "click", this.onClick);
22535
+ this.state = owl.useState({ pickerOpened: false });
22536
+ owl.useExternalListener(window, "click", this.closePicker);
22328
22537
  }
22329
- toggleColorPicker() {
22330
- this.state.fillColorTool = !this.state.fillColorTool;
22538
+ closePicker() {
22539
+ this.state.pickerOpened = false;
22540
+ }
22541
+ togglePicker() {
22542
+ this.state.pickerOpened = !this.state.pickerOpened;
22543
+ }
22544
+ }
22545
+
22546
+ class ChartTitle extends owl.Component {
22547
+ static template = "o-spreadsheet.ChartTitle";
22548
+ static components = { ColorPickerWidget, Section };
22549
+ static props = { title: String, update: Function };
22550
+ updateTitle(ev) {
22551
+ this.props.update(ev.target.value);
22552
+ }
22553
+ }
22554
+
22555
+ class LineBarPieDesignPanel extends owl.Component {
22556
+ static template = "o-spreadsheet-LineBarPieDesignPanel";
22557
+ static components = { ChartColor, ColorPickerWidget, ChartTitle, Section };
22558
+ static props = {
22559
+ figureId: String,
22560
+ definition: Object,
22561
+ updateChart: Function,
22562
+ canUpdateChart: Function,
22563
+ };
22564
+ get title() {
22565
+ return _t(this.props.definition.title);
22331
22566
  }
22332
22567
  updateBackgroundColor(color) {
22333
22568
  this.props.updateChart(this.props.figureId, {
22334
22569
  background: color,
22335
22570
  });
22336
22571
  }
22337
- updateTitle() {
22338
- this.props.updateChart(this.props.figureId, {
22339
- title: this.state.title,
22340
- });
22572
+ updateTitle(title) {
22573
+ this.props.updateChart(this.props.figureId, { title });
22341
22574
  }
22342
22575
  updateSelect(attr, ev) {
22343
22576
  this.props.updateChart(this.props.figureId, {
@@ -22345,12 +22578,6 @@ class LineBarPieDesignPanel extends owl.Component {
22345
22578
  });
22346
22579
  }
22347
22580
  }
22348
- LineBarPieDesignPanel.props = {
22349
- figureId: String,
22350
- definition: Object,
22351
- updateChart: Function,
22352
- canUpdateChart: Function,
22353
- };
22354
22581
 
22355
22582
  class BarChartDesignPanel extends LineBarPieDesignPanel {
22356
22583
  static template = "o-spreadsheet-BarChartDesignPanel";
@@ -22358,7 +22585,13 @@ class BarChartDesignPanel extends LineBarPieDesignPanel {
22358
22585
 
22359
22586
  class GaugeChartConfigPanel extends owl.Component {
22360
22587
  static template = "o-spreadsheet-GaugeChartConfigPanel";
22361
- static components = { SelectionInput, ValidationMessages };
22588
+ static components = { SelectionInput, ChartErrorSection, ChartDataSeries };
22589
+ static props = {
22590
+ figureId: String,
22591
+ definition: Object,
22592
+ updateChart: Function,
22593
+ canUpdateChart: Function,
22594
+ };
22362
22595
  state = owl.useState({
22363
22596
  dataRangeDispatchResult: undefined,
22364
22597
  });
@@ -22385,12 +22618,6 @@ class GaugeChartConfigPanel extends owl.Component {
22385
22618
  return this.dataRange || "";
22386
22619
  }
22387
22620
  }
22388
- GaugeChartConfigPanel.props = {
22389
- figureId: String,
22390
- definition: Object,
22391
- updateChart: Function,
22392
- canUpdateChart: Function,
22393
- };
22394
22621
 
22395
22622
  css /* scss */ `
22396
22623
  .o-gauge-color-set {
@@ -22425,31 +22652,35 @@ css /* scss */ `
22425
22652
  `;
22426
22653
  class GaugeChartDesignPanel extends owl.Component {
22427
22654
  static template = "o-spreadsheet-GaugeChartDesignPanel";
22428
- static components = { ColorPickerWidget, ValidationMessages };
22655
+ static components = { ColorPickerWidget, ChartErrorSection, ChartColor, ChartTitle, Section };
22656
+ static props = {
22657
+ figureId: String,
22658
+ definition: Object,
22659
+ updateChart: Function,
22660
+ canUpdateChart: Function,
22661
+ };
22429
22662
  state = owl.useState({
22430
- title: "",
22431
22663
  openedMenu: undefined,
22432
22664
  sectionRuleDispatchResult: undefined,
22433
22665
  sectionRule: deepCopy(this.props.definition.sectionRule),
22434
22666
  });
22435
22667
  setup() {
22436
- this.state.title = _t(this.props.definition.title);
22437
22668
  owl.useExternalListener(window, "click", this.closeMenus);
22438
22669
  }
22670
+ get title() {
22671
+ return _t(this.props.definition.title);
22672
+ }
22439
22673
  get designErrorMessages() {
22440
22674
  const cancelledReasons = [...(this.state.sectionRuleDispatchResult?.reasons || [])];
22441
22675
  return cancelledReasons.map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
22442
22676
  }
22443
22677
  updateBackgroundColor(color) {
22444
- this.state.openedMenu = undefined;
22445
22678
  this.props.updateChart(this.props.figureId, {
22446
22679
  background: color,
22447
22680
  });
22448
22681
  }
22449
- updateTitle() {
22450
- this.props.updateChart(this.props.figureId, {
22451
- title: this.state.title,
22452
- });
22682
+ updateTitle(title) {
22683
+ this.props.updateChart(this.props.figureId, { title });
22453
22684
  }
22454
22685
  isRangeMinInvalid() {
22455
22686
  return !!(this.state.sectionRuleDispatchResult?.isCancelledBecause("EmptyGaugeRangeMin" /* CommandResult.EmptyGaugeRangeMin */) ||
@@ -22499,12 +22730,6 @@ class GaugeChartDesignPanel extends owl.Component {
22499
22730
  this.state.openedMenu = undefined;
22500
22731
  }
22501
22732
  }
22502
- GaugeChartDesignPanel.props = {
22503
- figureId: String,
22504
- definition: Object,
22505
- updateChart: Function,
22506
- canUpdateChart: Function,
22507
- };
22508
22733
 
22509
22734
  class LineConfigPanel extends LineBarPieConfigPanel {
22510
22735
  static template = "o-spreadsheet-LineConfigPanel";
@@ -22515,24 +22740,42 @@ class LineConfigPanel extends LineBarPieConfigPanel {
22515
22740
  }
22516
22741
  return false;
22517
22742
  }
22518
- onUpdateLabelsAsText(ev) {
22743
+ get stackedLabel() {
22744
+ return _t("Stacked linechart");
22745
+ }
22746
+ get cumulativeLabel() {
22747
+ return _t("Cumulative data");
22748
+ }
22749
+ getLabelRangeOptions() {
22750
+ const options = super.getLabelRangeOptions();
22751
+ if (this.canTreatLabelsAsText) {
22752
+ options.push({
22753
+ name: "labelsAsText",
22754
+ value: this.props.definition.labelsAsText,
22755
+ label: _t("Treat labels as text"),
22756
+ onChange: this.onUpdateLabelsAsText.bind(this),
22757
+ });
22758
+ }
22759
+ return options;
22760
+ }
22761
+ onUpdateLabelsAsText(labelsAsText) {
22519
22762
  this.props.updateChart(this.props.figureId, {
22520
- labelsAsText: ev.target.checked,
22763
+ labelsAsText,
22521
22764
  });
22522
22765
  }
22523
- onUpdateStacked(ev) {
22766
+ onUpdateStacked(stacked) {
22524
22767
  this.props.updateChart(this.props.figureId, {
22525
- stacked: ev.target.checked,
22768
+ stacked,
22526
22769
  });
22527
22770
  }
22528
- onUpdateAggregated(ev) {
22771
+ onUpdateAggregated(aggregated) {
22529
22772
  this.props.updateChart(this.props.figureId, {
22530
- aggregated: ev.target.checked,
22773
+ aggregated,
22531
22774
  });
22532
22775
  }
22533
- onUpdateCumulative(ev) {
22776
+ onUpdateCumulative(cumulative) {
22534
22777
  this.props.updateChart(this.props.figureId, {
22535
- cumulative: ev.target.checked,
22778
+ cumulative,
22536
22779
  });
22537
22780
  }
22538
22781
  }
@@ -22543,7 +22786,13 @@ class LineChartDesignPanel extends LineBarPieDesignPanel {
22543
22786
 
22544
22787
  class ScorecardChartConfigPanel extends owl.Component {
22545
22788
  static template = "o-spreadsheet-ScorecardChartConfigPanel";
22546
- static components = { SelectionInput, ValidationMessages };
22789
+ static components = { SelectionInput, ValidationMessages, ChartErrorSection, Section };
22790
+ static props = {
22791
+ figureId: String,
22792
+ definition: Object,
22793
+ updateChart: Function,
22794
+ canUpdateChart: Function,
22795
+ };
22547
22796
  state = owl.useState({
22548
22797
  keyValueDispatchResult: undefined,
22549
22798
  baselineDispatchResult: undefined,
@@ -22595,28 +22844,27 @@ class ScorecardChartConfigPanel extends owl.Component {
22595
22844
  this.props.updateChart(this.props.figureId, { baselineMode: ev.target.value });
22596
22845
  }
22597
22846
  }
22598
- ScorecardChartConfigPanel.props = {
22599
- figureId: String,
22600
- definition: Object,
22601
- updateChart: Function,
22602
- canUpdateChart: Function,
22603
- };
22604
22847
 
22605
22848
  class ScorecardChartDesignPanel extends owl.Component {
22606
22849
  static template = "o-spreadsheet-ScorecardChartDesignPanel";
22607
- static components = { ColorPickerWidget };
22850
+ static components = { ColorPickerWidget, ChartColor, ChartTitle, Section };
22851
+ static props = {
22852
+ figureId: String,
22853
+ definition: Object,
22854
+ updateChart: Function,
22855
+ canUpdateChart: Function,
22856
+ };
22608
22857
  state = owl.useState({
22609
- title: "",
22610
22858
  openedColorPicker: undefined,
22611
22859
  });
22612
22860
  setup() {
22613
- this.state.title = _t(this.props.definition.title);
22614
22861
  owl.useExternalListener(window, "click", this.closeMenus);
22615
22862
  }
22616
- updateTitle() {
22617
- this.props.updateChart(this.props.figureId, {
22618
- title: this.state.title,
22619
- });
22863
+ get title() {
22864
+ return _t(this.props.definition.title);
22865
+ }
22866
+ updateTitle(title) {
22867
+ this.props.updateChart(this.props.figureId, { title });
22620
22868
  }
22621
22869
  translate(term) {
22622
22870
  return _t(term);
@@ -22650,12 +22898,6 @@ class ScorecardChartDesignPanel extends owl.Component {
22650
22898
  this.state.openedColorPicker = undefined;
22651
22899
  }
22652
22900
  }
22653
- ScorecardChartDesignPanel.props = {
22654
- figureId: String,
22655
- definition: Object,
22656
- updateChart: Function,
22657
- canUpdateChart: Function,
22658
- };
22659
22901
 
22660
22902
  const chartSidePanelComponentRegistry = new Registry();
22661
22903
  chartSidePanelComponentRegistry
@@ -22706,6 +22948,8 @@ css /* scss */ `
22706
22948
  `;
22707
22949
  class ChartPanel extends owl.Component {
22708
22950
  static template = "o-spreadsheet-ChartPanel";
22951
+ static components = { Section };
22952
+ static props = { onCloseSidePanel: Function };
22709
22953
  state;
22710
22954
  get figureId() {
22711
22955
  return this.state.figureId;
@@ -22792,9 +23036,6 @@ class ChartPanel extends owl.Component {
22792
23036
  this.state.panel = panel;
22793
23037
  }
22794
23038
  }
22795
- ChartPanel.props = {
22796
- onCloseSidePanel: Function,
22797
- };
22798
23039
 
22799
23040
  css /* scss */ `
22800
23041
  .o-spreadsheet {
@@ -22905,6 +23146,9 @@ css /* scss */ `
22905
23146
  `;
22906
23147
  class IconPicker extends owl.Component {
22907
23148
  static template = "o-spreadsheet-IconPicker";
23149
+ static props = {
23150
+ onIconPicked: Function,
23151
+ };
22908
23152
  icons = ICONS;
22909
23153
  iconSets = ICON_SETS;
22910
23154
  onIconClick(icon) {
@@ -22913,9 +23157,6 @@ class IconPicker extends owl.Component {
22913
23157
  }
22914
23158
  }
22915
23159
  }
22916
- IconPicker.props = {
22917
- onIconPicked: Function,
22918
- };
22919
23160
 
22920
23161
  function useDragAndDropListItems() {
22921
23162
  let dndHelper;
@@ -23270,6 +23511,11 @@ css /* scss */ `
23270
23511
  `;
23271
23512
  class ConditionalFormatPreviewList extends owl.Component {
23272
23513
  static template = "o-spreadsheet-ConditionalFormatPreviewList";
23514
+ static props = {
23515
+ conditionalFormats: Array,
23516
+ onPreviewClick: Function,
23517
+ onAddConditionalFormat: Function,
23518
+ };
23273
23519
  icons = ICONS;
23274
23520
  dragAndDrop = useDragAndDropListItems();
23275
23521
  cfListRef = owl.useRef("cfList");
@@ -23350,11 +23596,6 @@ class ConditionalFormatPreviewList extends owl.Component {
23350
23596
  }
23351
23597
  }
23352
23598
  }
23353
- ConditionalFormatPreviewList.props = {
23354
- conditionalFormats: Array,
23355
- onPreviewClick: Function,
23356
- onAddConditionalFormat: Function,
23357
- };
23358
23599
 
23359
23600
  css /* scss */ `
23360
23601
  label {
@@ -23527,11 +23768,16 @@ css /* scss */ `
23527
23768
  `;
23528
23769
  class ConditionalFormattingEditor extends owl.Component {
23529
23770
  static template = "o-spreadsheet-ConditionalFormattingEditor";
23771
+ static props = {
23772
+ editedCf: { type: Object, optional: true },
23773
+ onExitEdition: Function,
23774
+ };
23530
23775
  static components = {
23531
23776
  SelectionInput,
23532
23777
  IconPicker,
23533
23778
  ColorPickerWidget,
23534
23779
  ConditionalFormatPreviewList,
23780
+ Section,
23535
23781
  };
23536
23782
  icons = ICONS;
23537
23783
  cellIsOperators = CellIsOperators;
@@ -23791,13 +24037,13 @@ class ConditionalFormattingEditor extends owl.Component {
23791
24037
  this.state.rules.iconSet.icons[target] = icon;
23792
24038
  }
23793
24039
  }
23794
- ConditionalFormattingEditor.props = {
23795
- editedCf: { type: Object, optional: true },
23796
- onExitEdition: Function,
23797
- };
23798
24040
 
23799
24041
  class ConditionalFormattingPanel extends owl.Component {
23800
24042
  static template = "o-spreadsheet-ConditionalFormattingPanel";
24043
+ static props = {
24044
+ selection: { type: Object, optional: true },
24045
+ onCloseSidePanel: Function,
24046
+ };
23801
24047
  static components = {
23802
24048
  ConditionalFormatPreviewList,
23803
24049
  ConditionalFormattingEditor,
@@ -23856,10 +24102,6 @@ class ConditionalFormattingPanel extends owl.Component {
23856
24102
  this.state.editedCf = cf;
23857
24103
  }
23858
24104
  }
23859
- ConditionalFormattingPanel.props = {
23860
- selection: { type: Object, optional: true },
23861
- onCloseSidePanel: Function,
23862
- };
23863
24105
 
23864
24106
  css /* scss */ `
23865
24107
  .o-custom-currency {
@@ -23870,6 +24112,8 @@ css /* scss */ `
23870
24112
  `;
23871
24113
  class CustomCurrencyPanel extends owl.Component {
23872
24114
  static template = "o-spreadsheet-CustomCurrencyPanel";
24115
+ static components = { Section };
24116
+ static props = { onCloseSidePanel: Function };
23873
24117
  availableCurrencies;
23874
24118
  state;
23875
24119
  setup() {
@@ -23992,9 +24236,6 @@ class CustomCurrencyPanel extends owl.Component {
23992
24236
  return currency.name + (currency.code ? ` (${currency.code})` : "");
23993
24237
  }
23994
24238
  }
23995
- CustomCurrencyPanel.props = {
23996
- onCloseSidePanel: Function,
23997
- };
23998
24239
 
23999
24240
  css /* scss */ `
24000
24241
  .o-find-and-replace {
@@ -24014,11 +24255,20 @@ css /* scss */ `
24014
24255
  padding: 4px 0 4px 4px;
24015
24256
  }
24016
24257
  }
24258
+
24259
+ .o-matches-count div {
24260
+ text-overflow: ellipsis;
24261
+ overflow: hidden;
24262
+ white-space: nowrap;
24263
+ }
24017
24264
  }
24018
24265
  `;
24019
24266
  class FindAndReplacePanel extends owl.Component {
24020
24267
  static template = "o-spreadsheet-FindAndReplacePanel";
24021
- static components = { SelectionInput };
24268
+ static components = { SelectionInput, Section, Checkbox };
24269
+ static props = {
24270
+ onCloseSidePanel: Function,
24271
+ };
24022
24272
  debounceTimeoutId;
24023
24273
  initialShowFormulaState = false;
24024
24274
  dataRange = "";
@@ -24093,19 +24343,16 @@ class FindAndReplacePanel extends owl.Component {
24093
24343
  this.replace();
24094
24344
  }
24095
24345
  }
24096
- searchFormulas(ev) {
24097
- const showFormula = ev.target.checked;
24346
+ searchFormulas(showFormula) {
24098
24347
  this.env.model.dispatch("SET_FORMULA_VISIBILITY", {
24099
24348
  show: showFormula,
24100
24349
  });
24101
24350
  this.updateSearch({ searchFormulas: showFormula });
24102
24351
  }
24103
- searchExactMatch(ev) {
24104
- const exactMatch = ev.target.checked;
24352
+ searchExactMatch(exactMatch) {
24105
24353
  this.updateSearch({ exactMatch });
24106
24354
  }
24107
- searchMatchCase(ev) {
24108
- const matchCase = ev.target.checked;
24355
+ searchMatchCase(matchCase) {
24109
24356
  this.updateSearch({ matchCase });
24110
24357
  }
24111
24358
  changeSearchScope(ev) {
@@ -24158,9 +24405,6 @@ class FindAndReplacePanel extends owl.Component {
24158
24405
  });
24159
24406
  }
24160
24407
  }
24161
- FindAndReplacePanel.props = {
24162
- onCloseSidePanel: Function,
24163
- };
24164
24408
 
24165
24409
  css /* scss */ `
24166
24410
  .o-more-formats-panel {
@@ -24192,13 +24436,13 @@ const DATE_FORMAT_ACTIONS = createActions([
24192
24436
  ]);
24193
24437
  class MoreFormatsPanel extends owl.Component {
24194
24438
  static template = "o-spreadsheet-MoreFormatsPanel";
24439
+ static props = {
24440
+ onCloseSidePanel: Function,
24441
+ };
24195
24442
  get dateFormatsActions() {
24196
24443
  return DATE_FORMAT_ACTIONS;
24197
24444
  }
24198
24445
  }
24199
- MoreFormatsPanel.props = {
24200
- onCloseSidePanel: Function,
24201
- };
24202
24446
 
24203
24447
  css /* scss */ `
24204
24448
  .o-checkbox-selection {
@@ -24207,7 +24451,7 @@ css /* scss */ `
24207
24451
  `;
24208
24452
  class RemoveDuplicatesPanel extends owl.Component {
24209
24453
  static template = "o-spreadsheet-RemoveDuplicatesPanel";
24210
- static components = { ValidationMessages };
24454
+ static components = { ValidationMessages, Section, Checkbox };
24211
24455
  state = owl.useState({
24212
24456
  hasHeader: false,
24213
24457
  columns: {},
@@ -24296,6 +24540,8 @@ css /* scss */ `
24296
24540
  `;
24297
24541
  class SettingsPanel extends owl.Component {
24298
24542
  static template = "o-spreadsheet-SettingsPanel";
24543
+ static components = { Section };
24544
+ static props = { onCloseSidePanel: Function };
24299
24545
  loadedLocales = [];
24300
24546
  setup() {
24301
24547
  owl.onWillStart(() => this.loadLocales());
@@ -24344,9 +24590,6 @@ class SettingsPanel extends owl.Component {
24344
24590
  return this.loadedLocales;
24345
24591
  }
24346
24592
  }
24347
- SettingsPanel.props = {
24348
- onCloseSidePanel: Function,
24349
- };
24350
24593
 
24351
24594
  const SplitToColumnsInteractiveContent = {
24352
24595
  SplitIsDestructive: _t("This will overwrite data in the subsequent columns. Split anyway?"),
@@ -24442,7 +24685,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
24442
24685
  return false;
24443
24686
  }
24444
24687
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
24445
- const today = jsDateToRoundNumber(new Date());
24688
+ const today = jsDateToRoundNumber(DateTime.now());
24446
24689
  return isDateBetween(dateValue, today, criterionValue);
24447
24690
  }
24448
24691
  return areDatesSameDay(dateValue, criterionValue);
@@ -24934,7 +25177,8 @@ const SEPARATORS = [
24934
25177
  ];
24935
25178
  class SplitIntoColumnsPanel extends owl.Component {
24936
25179
  static template = "o-spreadsheet-SplitIntoColumnsPanel";
24937
- static components = { ValidationMessages };
25180
+ static components = { ValidationMessages, Section, Checkbox };
25181
+ static props = { onCloseSidePanel: Function };
24938
25182
  state = owl.useState({ separatorValue: "auto", addNewColumns: false, customSeparator: "" });
24939
25183
  setup() {
24940
25184
  owl.onWillUpdateProps(() => {
@@ -24956,10 +25200,8 @@ class SplitIntoColumnsPanel extends owl.Component {
24956
25200
  return;
24957
25201
  this.state.customSeparator = ev.target.value;
24958
25202
  }
24959
- updateAddNewColumnsCheckbox(ev) {
24960
- if (!ev.target)
24961
- return;
24962
- this.state.addNewColumns = ev.target.checked;
25203
+ updateAddNewColumnsCheckbox(addNewColumns) {
25204
+ this.state.addNewColumns = addNewColumns;
24963
25205
  }
24964
25206
  confirm() {
24965
25207
  const result = interactiveSplitToColumns(this.env, this.separatorValue, this.state.addNewColumns);
@@ -25013,13 +25255,15 @@ class SplitIntoColumnsPanel extends owl.Component {
25013
25255
  return !this.separatorValue || this.errorMessages.length > 0;
25014
25256
  }
25015
25257
  }
25016
- SplitIntoColumnsPanel.props = {
25017
- onCloseSidePanel: Function,
25018
- };
25019
25258
 
25020
25259
  /** This component looks like a select input, but on click it opens a Menu with the items given as props instead of a dropdown */
25021
25260
  class SelectMenu extends owl.Component {
25022
25261
  static template = "o-spreadsheet-SelectMenu";
25262
+ static props = {
25263
+ menuItems: Array,
25264
+ selectedValue: String,
25265
+ class: { type: String, optional: true },
25266
+ };
25023
25267
  static components = { Menu };
25024
25268
  selectRef = owl.useRef("select");
25025
25269
  selectRect = useAbsoluteBoundingRect(this.selectRef);
@@ -25039,13 +25283,12 @@ class SelectMenu extends owl.Component {
25039
25283
  };
25040
25284
  }
25041
25285
  }
25042
- SelectMenu.props = {
25043
- menuItems: Array,
25044
- selectedValue: String,
25045
- class: { type: String, optional: true },
25046
- };
25047
25286
 
25048
25287
  class DataValidationCriterionForm extends owl.Component {
25288
+ static props = {
25289
+ criterion: Object,
25290
+ onCriterionChanged: Function,
25291
+ };
25049
25292
  setup() {
25050
25293
  owl.onMounted(() => {
25051
25294
  interactiveStopEdition(this.env);
@@ -25059,10 +25302,6 @@ class DataValidationCriterionForm extends owl.Component {
25059
25302
  this.props.onCriterionChanged(filteredCriterion);
25060
25303
  }
25061
25304
  }
25062
- DataValidationCriterionForm.props = {
25063
- criterion: Object,
25064
- onCriterionChanged: Function,
25065
- };
25066
25305
 
25067
25306
  css /* scss */ `
25068
25307
  .o-dv-input {
@@ -25077,6 +25316,15 @@ css /* scss */ `
25077
25316
  `;
25078
25317
  class DataValidationInput extends owl.Component {
25079
25318
  static template = "o-spreadsheet-DataValidationInput";
25319
+ static props = {
25320
+ value: { type: String, optional: true },
25321
+ criterionType: String,
25322
+ onValueChanged: Function,
25323
+ onKeyDown: { type: Function, optional: true },
25324
+ focused: { type: Boolean, optional: true },
25325
+ onBlur: { type: Function, optional: true },
25326
+ onFocus: { type: Function, optional: true },
25327
+ };
25080
25328
  static defaultProps = {
25081
25329
  value: "",
25082
25330
  onKeyDown: () => { },
@@ -25115,15 +25363,6 @@ class DataValidationInput extends owl.Component {
25115
25363
  return this.env.model.getters.getDataValidationInvalidCriterionValueMessage(this.props.criterionType, canonicalizeContent(this.props.value, this.env.model.getters.getLocale()));
25116
25364
  }
25117
25365
  }
25118
- DataValidationInput.props = {
25119
- value: { type: String, optional: true },
25120
- criterionType: String,
25121
- onValueChanged: Function,
25122
- onKeyDown: { type: Function, optional: true },
25123
- focused: { type: Boolean, optional: true },
25124
- onBlur: { type: Function, optional: true },
25125
- onFocus: { type: Function, optional: true },
25126
- };
25127
25366
 
25128
25367
  const DATES_VALUES = {
25129
25368
  today: _t("today"),
@@ -25459,7 +25698,11 @@ css /* scss */ `
25459
25698
  `;
25460
25699
  class DataValidationEditor extends owl.Component {
25461
25700
  static template = "o-spreadsheet-DataValidationEditor";
25462
- static components = { SelectionInput, SelectMenu };
25701
+ static components = { SelectionInput, SelectMenu, Section };
25702
+ static props = {
25703
+ rule: { type: Object, optional: true },
25704
+ onExit: Function,
25705
+ };
25463
25706
  state = owl.useState({ rule: this.defaultDataValidationRule });
25464
25707
  setup() {
25465
25708
  if (this.props.rule) {
@@ -25535,10 +25778,6 @@ class DataValidationEditor extends owl.Component {
25535
25778
  return dataValidationPanelCriteriaRegistry.get(this.state.rule.criterion.type).component;
25536
25779
  }
25537
25780
  }
25538
- DataValidationEditor.props = {
25539
- rule: { type: Object, optional: true },
25540
- onExit: Function,
25541
- };
25542
25781
 
25543
25782
  css /* scss */ `
25544
25783
  .o-sidePanel {
@@ -25568,6 +25807,10 @@ css /* scss */ `
25568
25807
  `;
25569
25808
  class DataValidationPreview extends owl.Component {
25570
25809
  static template = "o-spreadsheet-DataValidationPreview";
25810
+ static props = {
25811
+ onClick: Function,
25812
+ rule: Object,
25813
+ };
25571
25814
  deleteDataValidation() {
25572
25815
  const sheetId = this.env.model.getters.getActiveSheetId();
25573
25816
  this.env.model.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: this.props.rule.id });
@@ -25584,13 +25827,12 @@ class DataValidationPreview extends owl.Component {
25584
25827
  .getPreview(this.props.rule.criterion, this.env.model.getters);
25585
25828
  }
25586
25829
  }
25587
- DataValidationPreview.props = {
25588
- onClick: Function,
25589
- rule: Object,
25590
- };
25591
25830
 
25592
25831
  class DataValidationPanel extends owl.Component {
25593
25832
  static template = "o-spreadsheet-DataValidationPanel";
25833
+ static props = {
25834
+ onCloseSidePanel: Function,
25835
+ };
25594
25836
  static components = { DataValidationPreview, DataValidationEditor };
25595
25837
  state = owl.useState({ mode: "list", activeRule: undefined });
25596
25838
  onPreviewClick(id) {
@@ -25620,9 +25862,6 @@ class DataValidationPanel extends owl.Component {
25620
25862
  return this.env.model.getters.getDataValidationRules(sheetId);
25621
25863
  }
25622
25864
  }
25623
- DataValidationPanel.props = {
25624
- onCloseSidePanel: Function,
25625
- };
25626
25865
 
25627
25866
  const sidePanelRegistry = new Registry();
25628
25867
  sidePanelRegistry.add("ConditionalFormatting", {
@@ -25754,6 +25993,13 @@ css /*SCSS*/ `
25754
25993
  `;
25755
25994
  class FigureComponent extends owl.Component {
25756
25995
  static template = "o-spreadsheet-FigureComponent";
25996
+ static props = {
25997
+ figure: Object,
25998
+ style: { type: String, optional: true },
25999
+ onFigureDeleted: { type: Function, optional: true },
26000
+ onMouseDown: { type: Function, optional: true },
26001
+ onClickAnchor: { type: Function, optional: true },
26002
+ };
25757
26003
  static components = { Menu };
25758
26004
  static defaultProps = {
25759
26005
  onFigureDeleted: () => { },
@@ -25896,13 +26142,6 @@ class FigureComponent extends owl.Component {
25896
26142
  .menuBuilder(this.props.figure.id, this.props.onFigureDeleted, this.env);
25897
26143
  }
25898
26144
  }
25899
- FigureComponent.props = {
25900
- figure: Object,
25901
- style: { type: String, optional: true },
25902
- onFigureDeleted: { type: Function, optional: true },
25903
- onMouseDown: { type: Function, optional: true },
25904
- onClickAnchor: { type: Function, optional: true },
25905
- };
25906
26145
 
25907
26146
  const ToggleGroupInteractiveContent = {
25908
26147
  CannotHideAllRows: _t("Cannot hide all the rows of a sheet."),
@@ -26040,6 +26279,10 @@ css /* scss */ `
26040
26279
  `;
26041
26280
  class Autofill extends owl.Component {
26042
26281
  static template = "o-spreadsheet-Autofill";
26282
+ static props = {
26283
+ position: Object,
26284
+ isVisible: Boolean,
26285
+ };
26043
26286
  state = owl.useState({
26044
26287
  position: { left: 0, top: 0 },
26045
26288
  handler: false,
@@ -26108,18 +26351,14 @@ class Autofill extends owl.Component {
26108
26351
  this.env.model.dispatch("AUTOFILL_AUTO");
26109
26352
  }
26110
26353
  }
26111
- Autofill.props = {
26112
- position: Object,
26113
- isVisible: Boolean,
26114
- };
26115
26354
  class TooltipComponent extends owl.Component {
26355
+ static props = {
26356
+ content: String,
26357
+ };
26116
26358
  static template = owl.xml /* xml */ `
26117
26359
  <div t-esc="props.content"/>
26118
26360
  `;
26119
26361
  }
26120
- TooltipComponent.props = {
26121
- content: String,
26122
- };
26123
26362
 
26124
26363
  css /* scss */ `
26125
26364
  .o-client-tag {
@@ -26133,6 +26372,13 @@ css /* scss */ `
26133
26372
  `;
26134
26373
  class ClientTag extends owl.Component {
26135
26374
  static template = "o-spreadsheet-ClientTag";
26375
+ static props = {
26376
+ active: Boolean,
26377
+ name: String,
26378
+ color: String,
26379
+ col: Number,
26380
+ row: Number,
26381
+ };
26136
26382
  get tagStyle() {
26137
26383
  const { col, row, color } = this.props;
26138
26384
  const { height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
@@ -26150,13 +26396,6 @@ class ClientTag extends owl.Component {
26150
26396
  });
26151
26397
  }
26152
26398
  }
26153
- ClientTag.props = {
26154
- active: Boolean,
26155
- name: String,
26156
- color: String,
26157
- col: Number,
26158
- row: Number,
26159
- };
26160
26399
 
26161
26400
  function getHtmlContentFromPattern(pattern, value, highlightColor, className) {
26162
26401
  const pendingHtmlContent = [];
@@ -26196,14 +26435,14 @@ css /* scss */ `
26196
26435
  `;
26197
26436
  class TextValueProvider extends owl.Component {
26198
26437
  static template = "o-spreadsheet-TextValueProvider";
26438
+ static props = {
26439
+ values: Array,
26440
+ selectedIndex: { type: Number, optional: true },
26441
+ getHtmlContent: Function,
26442
+ onValueSelected: Function,
26443
+ onValueHovered: Function,
26444
+ };
26199
26445
  }
26200
- TextValueProvider.props = {
26201
- values: Array,
26202
- selectedIndex: { type: Number, optional: true },
26203
- getHtmlContent: Function,
26204
- onValueSelected: Function,
26205
- onValueHovered: Function,
26206
- };
26207
26446
 
26208
26447
  class ContentEditableHelper {
26209
26448
  // todo make el private and expose dedicated methods
@@ -26565,6 +26804,11 @@ css /* scss */ `
26565
26804
  `;
26566
26805
  class FunctionDescriptionProvider extends owl.Component {
26567
26806
  static template = "o-spreadsheet-FunctionDescriptionProvider";
26807
+ static props = {
26808
+ functionName: String,
26809
+ functionDescription: Object,
26810
+ argToFocus: Number,
26811
+ };
26568
26812
  assistantState = owl.useState({
26569
26813
  allowCellSelectionBehind: false,
26570
26814
  });
@@ -26589,11 +26833,6 @@ class FunctionDescriptionProvider extends owl.Component {
26589
26833
  }, 2000);
26590
26834
  }
26591
26835
  }
26592
- FunctionDescriptionProvider.props = {
26593
- functionName: String,
26594
- functionDescription: Object,
26595
- argToFocus: Number,
26596
- };
26597
26836
 
26598
26837
  const functions$2 = functionRegistry.content;
26599
26838
  const ASSISTANT_WIDTH = 300;
@@ -26659,6 +26898,16 @@ css /* scss */ `
26659
26898
  `;
26660
26899
  class Composer extends owl.Component {
26661
26900
  static template = "o-spreadsheet-Composer";
26901
+ static props = {
26902
+ focus: {
26903
+ validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
26904
+ },
26905
+ onComposerContentFocused: Function,
26906
+ inputStyle: { type: String, optional: true },
26907
+ rect: { type: Object, optional: true },
26908
+ delimitation: { type: Object, optional: true },
26909
+ onComposerUnmounted: { type: Function, optional: true },
26910
+ };
26662
26911
  static components = { TextValueProvider, FunctionDescriptionProvider };
26663
26912
  static defaultProps = {
26664
26913
  inputStyle: "",
@@ -27215,14 +27464,6 @@ class Composer extends owl.Component {
27215
27464
  this.autoCompleteState.getHtmlContent = (value) => [{ value }];
27216
27465
  }
27217
27466
  }
27218
- Composer.props = {
27219
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27220
- onComposerContentFocused: Function,
27221
- inputStyle: { type: String, optional: true },
27222
- rect: { type: Object, optional: true },
27223
- delimitation: { type: Object, optional: true },
27224
- onComposerUnmounted: { type: Function, optional: true },
27225
- };
27226
27467
 
27227
27468
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
27228
27469
  const GRID_CELL_REFERENCE_TOP_OFFSET = 28;
@@ -27254,6 +27495,14 @@ css /* scss */ `
27254
27495
  */
27255
27496
  class GridComposer extends owl.Component {
27256
27497
  static template = "o-spreadsheet-GridComposer";
27498
+ static props = {
27499
+ focus: {
27500
+ validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
27501
+ },
27502
+ onComposerUnmounted: Function,
27503
+ onComposerContentFocused: Function,
27504
+ gridDims: Object,
27505
+ };
27257
27506
  static components = { Composer };
27258
27507
  gridComposerRef;
27259
27508
  zone;
@@ -27360,22 +27609,22 @@ class GridComposer extends owl.Component {
27360
27609
  });
27361
27610
  }
27362
27611
  }
27363
- GridComposer.props = {
27364
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27365
- onComposerUnmounted: Function,
27366
- onComposerContentFocused: Function,
27367
- gridDims: Object,
27368
- };
27369
27612
 
27370
- const CSS$1 = css /* scss */ `
27613
+ css /* scss */ `
27371
27614
  .o-grid-cell-icon {
27372
27615
  width: ${GRID_ICON_EDGE_LENGTH}px;
27373
27616
  height: ${GRID_ICON_EDGE_LENGTH}px;
27374
27617
  }
27375
27618
  `;
27376
27619
  class GridCellIcon extends owl.Component {
27377
- static style = CSS$1;
27378
27620
  static template = "o-spreadsheet-GridCellIcon";
27621
+ static props = {
27622
+ cellPosition: Object,
27623
+ horizontalAlign: { type: String, optional: true },
27624
+ verticalAlign: { type: String, optional: true },
27625
+ offset: { type: Object, optional: true },
27626
+ slots: Object,
27627
+ };
27379
27628
  get iconStyle() {
27380
27629
  const x = this.getIconHorizontalPosition();
27381
27630
  const y = this.getIconVerticalPosition();
@@ -27424,15 +27673,8 @@ class GridCellIcon extends owl.Component {
27424
27673
  return !(rect.width === 0 || rect.height === 0);
27425
27674
  }
27426
27675
  }
27427
- GridCellIcon.props = {
27428
- cellPosition: Object,
27429
- horizontalAlign: { type: String, optional: true },
27430
- verticalAlign: { type: String, optional: true },
27431
- offset: { type: Object, optional: true },
27432
- slots: Object,
27433
- };
27434
27676
 
27435
- const CSS = css /* scss */ `
27677
+ css /* scss */ `
27436
27678
  .o-filter-icon {
27437
27679
  color: ${FILTERS_COLOR};
27438
27680
  display: flex;
@@ -27447,8 +27689,10 @@ const CSS = css /* scss */ `
27447
27689
  }
27448
27690
  `;
27449
27691
  class FilterIcon extends owl.Component {
27450
- static style = CSS;
27451
27692
  static template = "o-spreadsheet-FilterIcon";
27693
+ static props = {
27694
+ cellPosition: Object,
27695
+ };
27452
27696
  onClick() {
27453
27697
  const position = this.props.cellPosition;
27454
27698
  const activePopoverType = this.env.model.getters.getPersistentPopoverTypeAtPosition(position);
@@ -27467,12 +27711,12 @@ class FilterIcon extends owl.Component {
27467
27711
  return this.env.model.getters.isFilterActive(this.props.cellPosition);
27468
27712
  }
27469
27713
  }
27470
- FilterIcon.props = {
27471
- cellPosition: Object,
27472
- };
27473
27714
 
27474
27715
  class FilterIconsOverlay extends owl.Component {
27475
27716
  static template = "o-spreadsheet-FilterIconsOverlay";
27717
+ static props = {
27718
+ gridPosition: { type: Object, optional: true },
27719
+ };
27476
27720
  static components = {
27477
27721
  GridCellIcon,
27478
27722
  FilterIcon,
@@ -27486,9 +27730,6 @@ class FilterIconsOverlay extends owl.Component {
27486
27730
  return headerPositions.map((position) => ({ sheetId, ...position }));
27487
27731
  }
27488
27732
  }
27489
- FilterIconsOverlay.props = {
27490
- gridPosition: { type: Object, optional: true },
27491
- };
27492
27733
 
27493
27734
  const CHECKBOX_WIDTH = 15;
27494
27735
  const MARGIN = (GRID_ICON_EDGE_LENGTH - CHECKBOX_WIDTH) / 2;
@@ -27502,6 +27743,9 @@ css /* scss */ `
27502
27743
  `;
27503
27744
  class DataValidationCheckbox extends owl.Component {
27504
27745
  static template = "o-spreadsheet-DataValidationCheckbox";
27746
+ static props = {
27747
+ cellPosition: Object,
27748
+ };
27505
27749
  onCheckboxChange(ev) {
27506
27750
  const newValue = ev.target.checked;
27507
27751
  const { sheetId, col, row } = this.props.cellPosition;
@@ -27516,9 +27760,6 @@ class DataValidationCheckbox extends owl.Component {
27516
27760
  return !!cell?.isFormula;
27517
27761
  }
27518
27762
  }
27519
- DataValidationCheckbox.props = {
27520
- cellPosition: Object,
27521
- };
27522
27763
 
27523
27764
  const ICON_WIDTH = 13;
27524
27765
  css /* scss */ `
@@ -27541,18 +27782,19 @@ css /* scss */ `
27541
27782
  `;
27542
27783
  class DataValidationListIcon extends owl.Component {
27543
27784
  static template = "o-spreadsheet-DataValidationListIcon";
27785
+ static props = {
27786
+ cellPosition: Object,
27787
+ };
27544
27788
  onClick() {
27545
27789
  const { col, row } = this.props.cellPosition;
27546
27790
  this.env.model.selection.selectCell(col, row);
27547
27791
  this.env.startCellEdition();
27548
27792
  }
27549
27793
  }
27550
- DataValidationListIcon.props = {
27551
- cellPosition: Object,
27552
- };
27553
27794
 
27554
27795
  class DataValidationOverlay extends owl.Component {
27555
27796
  static template = "o-spreadsheet-DataValidationOverlay";
27797
+ static props = {};
27556
27798
  static components = { GridCellIcon, DataValidationCheckbox, DataValidationListIcon };
27557
27799
  get checkBoxCellPositions() {
27558
27800
  return this.env.model.getters.getDataValidationCheckBoxCellPositions();
@@ -27563,7 +27805,6 @@ class DataValidationOverlay extends owl.Component {
27563
27805
  : this.env.model.getters.getDataValidationListCellsPositions();
27564
27806
  }
27565
27807
  }
27566
- DataValidationOverlay.props = {};
27567
27808
 
27568
27809
  /**
27569
27810
  * Transform a figure with coordinates from the model, to coordinates as they are shown on the screen,
@@ -27899,6 +28140,9 @@ css /*SCSS*/ `
27899
28140
  */
27900
28141
  class FiguresContainer extends owl.Component {
27901
28142
  static template = "o-spreadsheet-FiguresContainer";
28143
+ static props = {
28144
+ onFigureDeleted: Function,
28145
+ };
27902
28146
  static components = { FigureComponent };
27903
28147
  dnd = owl.useState({
27904
28148
  draggedFigure: undefined,
@@ -28148,9 +28392,6 @@ class FiguresContainer extends owl.Component {
28148
28392
  }
28149
28393
  }
28150
28394
  }
28151
- FiguresContainer.props = {
28152
- onFigureDeleted: Function,
28153
- };
28154
28395
 
28155
28396
  css /* scss */ `
28156
28397
  .o-grid-add-rows {
@@ -28169,6 +28410,9 @@ css /* scss */ `
28169
28410
  `;
28170
28411
  class GridAddRowsFooter extends owl.Component {
28171
28412
  static template = "o-spreadsheet-GridAddRowsFooter";
28413
+ static props = {
28414
+ focusGrid: Function,
28415
+ };
28172
28416
  static components = { ValidationMessages };
28173
28417
  inputRef = owl.useRef("inputRef");
28174
28418
  state = owl.useState({
@@ -28235,9 +28479,6 @@ class GridAddRowsFooter extends owl.Component {
28235
28479
  this.props.focusGrid();
28236
28480
  }
28237
28481
  }
28238
- GridAddRowsFooter.props = {
28239
- focusGrid: Function,
28240
- };
28241
28482
 
28242
28483
  /**
28243
28484
  * Manages an event listener on a ref. Useful for hooks that want to manage
@@ -28393,6 +28634,16 @@ function useTouchMove(gridRef, handler, canMoveUp) {
28393
28634
  }
28394
28635
  class GridOverlay extends owl.Component {
28395
28636
  static template = "o-spreadsheet-GridOverlay";
28637
+ static props = {
28638
+ onCellHovered: { type: Function, optional: true },
28639
+ onCellDoubleClicked: { type: Function, optional: true },
28640
+ onCellClicked: { type: Function, optional: true },
28641
+ onCellRightClicked: { type: Function, optional: true },
28642
+ onGridResized: { type: Function, optional: true },
28643
+ onFigureDeleted: { type: Function, optional: true },
28644
+ onGridMoved: Function,
28645
+ gridOverlayDimensions: String,
28646
+ };
28396
28647
  static components = { FiguresContainer, DataValidationOverlay, GridAddRowsFooter };
28397
28648
  static defaultProps = {
28398
28649
  onCellHovered: () => { },
@@ -28444,7 +28695,10 @@ class GridOverlay extends owl.Component {
28444
28695
  return;
28445
28696
  }
28446
28697
  const [col, row] = this.getCartesianCoordinates(ev);
28447
- this.props.onCellClicked(col, row, { shiftKey: ev.shiftKey, ctrlKey: ev.ctrlKey });
28698
+ this.props.onCellClicked(col, row, {
28699
+ expandZone: ev.shiftKey,
28700
+ addZone: isCtrlKey(ev),
28701
+ });
28448
28702
  }
28449
28703
  onDoubleClick(ev) {
28450
28704
  const [col, row] = this.getCartesianCoordinates(ev);
@@ -28463,19 +28717,15 @@ class GridOverlay extends owl.Component {
28463
28717
  return [colIndex, rowIndex];
28464
28718
  }
28465
28719
  }
28466
- GridOverlay.props = {
28467
- onCellHovered: { type: Function, optional: true },
28468
- onCellDoubleClicked: { type: Function, optional: true },
28469
- onCellClicked: { type: Function, optional: true },
28470
- onCellRightClicked: { type: Function, optional: true },
28471
- onGridResized: { type: Function, optional: true },
28472
- onFigureDeleted: { type: Function, optional: true },
28473
- onGridMoved: Function,
28474
- gridOverlayDimensions: String,
28475
- };
28476
28720
 
28477
28721
  class GridPopover extends owl.Component {
28478
28722
  static template = "o-spreadsheet-GridPopover";
28723
+ static props = {
28724
+ hoveredCell: Object,
28725
+ onClosePopover: Function,
28726
+ onMouseWheel: Function,
28727
+ gridRect: Object,
28728
+ };
28479
28729
  static components = { Popover };
28480
28730
  zIndex = ComponentsImportance.GridPopover;
28481
28731
  get cellPopover() {
@@ -28495,14 +28745,11 @@ class GridPopover extends owl.Component {
28495
28745
  };
28496
28746
  }
28497
28747
  }
28498
- GridPopover.props = {
28499
- hoveredCell: Object,
28500
- onClosePopover: Function,
28501
- onMouseWheel: Function,
28502
- gridRect: Object,
28503
- };
28504
28748
 
28505
28749
  class AbstractResizer extends owl.Component {
28750
+ static props = {
28751
+ onOpenContextMenu: Function,
28752
+ };
28506
28753
  PADDING = 0;
28507
28754
  MAX_SIZE_MARGIN = 0;
28508
28755
  MIN_ELEMENT_SIZE = 0;
@@ -28674,7 +28921,7 @@ class AbstractResizer extends owl.Component {
28674
28921
  this._increaseSelection(index);
28675
28922
  }
28676
28923
  else {
28677
- this._selectElement(index, ev.ctrlKey);
28924
+ this._selectElement(index, isCtrlKey(ev));
28678
28925
  }
28679
28926
  this.lastSelectedElementIndex = index;
28680
28927
  const mouseMoveSelect = (col, row) => {
@@ -28759,10 +29006,10 @@ css /* scss */ `
28759
29006
  }
28760
29007
  }
28761
29008
  `;
28762
- AbstractResizer.props = {
28763
- onOpenContextMenu: Function,
28764
- };
28765
29009
  class ColResizer extends AbstractResizer {
29010
+ static props = {
29011
+ onOpenContextMenu: Function,
29012
+ };
28766
29013
  static template = "o-spreadsheet-ColResizer";
28767
29014
  colResizerRef;
28768
29015
  setup() {
@@ -28831,8 +29078,8 @@ class ColResizer extends AbstractResizer {
28831
29078
  this.env.raiseError(MergeErrorMessage);
28832
29079
  }
28833
29080
  }
28834
- _selectElement(index, ctrlKey) {
28835
- this.env.model.selection.selectColumn(index, ctrlKey ? "newAnchor" : "overrideSelection");
29081
+ _selectElement(index, addDistinctHeader) {
29082
+ this.env.model.selection.selectColumn(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
28836
29083
  }
28837
29084
  _increaseSelection(index) {
28838
29085
  this.env.model.selection.selectColumn(index, "updateAnchor");
@@ -28924,10 +29171,10 @@ css /* scss */ `
28924
29171
  }
28925
29172
  }
28926
29173
  `;
28927
- ColResizer.props = {
28928
- onOpenContextMenu: Function,
28929
- };
28930
29174
  class RowResizer extends AbstractResizer {
29175
+ static props = {
29176
+ onOpenContextMenu: Function,
29177
+ };
28931
29178
  static template = "o-spreadsheet-RowResizer";
28932
29179
  setup() {
28933
29180
  super.setup();
@@ -28996,8 +29243,8 @@ class RowResizer extends AbstractResizer {
28996
29243
  this.env.raiseError(MergeErrorMessage);
28997
29244
  }
28998
29245
  }
28999
- _selectElement(index, ctrlKey) {
29000
- this.env.model.selection.selectRow(index, ctrlKey ? "newAnchor" : "overrideSelection");
29246
+ _selectElement(index, addDistinctHeader) {
29247
+ this.env.model.selection.selectRow(index, addDistinctHeader ? "newAnchor" : "overrideSelection");
29001
29248
  }
29002
29249
  _increaseSelection(index) {
29003
29250
  this.env.model.selection.selectRow(index, "updateAnchor");
@@ -29048,19 +29295,16 @@ css /* scss */ `
29048
29295
  }
29049
29296
  }
29050
29297
  `;
29051
- RowResizer.props = {
29052
- onOpenContextMenu: Function,
29053
- };
29054
29298
  class HeadersOverlay extends owl.Component {
29299
+ static props = {
29300
+ onOpenContextMenu: Function,
29301
+ };
29055
29302
  static template = "o-spreadsheet-HeadersOverlay";
29056
29303
  static components = { ColResizer, RowResizer };
29057
29304
  selectAll() {
29058
29305
  this.env.model.selection.selectAll();
29059
29306
  }
29060
29307
  }
29061
- HeadersOverlay.props = {
29062
- onOpenContextMenu: Function,
29063
- };
29064
29308
 
29065
29309
  function useGridDrawing(refName, model, canvasSize) {
29066
29310
  const canvasRef = owl.useRef(refName);
@@ -29118,6 +29362,12 @@ css /* scss */ `
29118
29362
  `;
29119
29363
  class Border extends owl.Component {
29120
29364
  static template = "o-spreadsheet-Border";
29365
+ static props = {
29366
+ zone: Object,
29367
+ orientation: String,
29368
+ isMoving: Boolean,
29369
+ onMoveHighlight: Function,
29370
+ };
29121
29371
  get style() {
29122
29372
  const isTop = ["n", "w", "e"].includes(this.props.orientation);
29123
29373
  const isLeft = ["n", "w", "s"].includes(this.props.orientation);
@@ -29146,12 +29396,6 @@ class Border extends owl.Component {
29146
29396
  this.props.onMoveHighlight(ev.clientX, ev.clientY);
29147
29397
  }
29148
29398
  }
29149
- Border.props = {
29150
- zone: Object,
29151
- orientation: String,
29152
- isMoving: Boolean,
29153
- onMoveHighlight: Function,
29154
- };
29155
29399
 
29156
29400
  css /* scss */ `
29157
29401
  .o-corner {
@@ -29178,6 +29422,13 @@ css /* scss */ `
29178
29422
  `;
29179
29423
  class Corner extends owl.Component {
29180
29424
  static template = "o-spreadsheet-Corner";
29425
+ static props = {
29426
+ zone: Object,
29427
+ color: String,
29428
+ orientation: String,
29429
+ isResizing: Boolean,
29430
+ onResizeHighlight: Function,
29431
+ };
29181
29432
  isTop = this.props.orientation[0] === "n";
29182
29433
  isLeft = this.props.orientation[1] === "w";
29183
29434
  get style() {
@@ -29206,13 +29457,6 @@ class Corner extends owl.Component {
29206
29457
  this.props.onResizeHighlight(this.isLeft, this.isTop);
29207
29458
  }
29208
29459
  }
29209
- Corner.props = {
29210
- zone: Object,
29211
- color: String,
29212
- orientation: String,
29213
- isResizing: Boolean,
29214
- onResizeHighlight: Function,
29215
- };
29216
29460
 
29217
29461
  css /*SCSS*/ `
29218
29462
  .o-highlight {
@@ -29221,6 +29465,10 @@ css /*SCSS*/ `
29221
29465
  `;
29222
29466
  class Highlight extends owl.Component {
29223
29467
  static template = "o-spreadsheet-Highlight";
29468
+ static props = {
29469
+ zone: Object,
29470
+ color: String,
29471
+ };
29224
29472
  static components = {
29225
29473
  Corner,
29226
29474
  Border,
@@ -29304,10 +29552,6 @@ class Highlight extends owl.Component {
29304
29552
  dragAndDropBeyondTheViewport(this.env, mouseMove, mouseUp);
29305
29553
  }
29306
29554
  }
29307
- Highlight.props = {
29308
- zone: Object,
29309
- color: String,
29310
- };
29311
29555
 
29312
29556
  let ScrollBar$1 = class ScrollBar {
29313
29557
  direction;
@@ -29347,6 +29591,14 @@ css /* scss */ `
29347
29591
  }
29348
29592
  `;
29349
29593
  class ScrollBar extends owl.Component {
29594
+ static props = {
29595
+ width: { type: Number, optional: true },
29596
+ height: { type: Number, optional: true },
29597
+ direction: String,
29598
+ position: Object,
29599
+ offset: Number,
29600
+ onScroll: Function,
29601
+ };
29350
29602
  static template = owl.xml /*xml*/ `
29351
29603
  <div
29352
29604
  t-attf-class="o-scrollbar {{props.direction}}"
@@ -29390,16 +29642,11 @@ class ScrollBar extends owl.Component {
29390
29642
  }
29391
29643
  }
29392
29644
  }
29393
- ScrollBar.props = {
29394
- width: { type: Number, optional: true },
29395
- height: { type: Number, optional: true },
29396
- direction: String,
29397
- position: Object,
29398
- offset: Number,
29399
- onScroll: Function,
29400
- };
29401
29645
 
29402
29646
  class HorizontalScrollBar extends owl.Component {
29647
+ static props = {
29648
+ leftOffset: { type: Number, optional: true },
29649
+ };
29403
29650
  static components = { ScrollBar };
29404
29651
  static template = owl.xml /*xml*/ `
29405
29652
  <ScrollBar
@@ -29440,11 +29687,11 @@ class HorizontalScrollBar extends owl.Component {
29440
29687
  });
29441
29688
  }
29442
29689
  }
29443
- HorizontalScrollBar.props = {
29444
- leftOffset: { type: Number, optional: true },
29445
- };
29446
29690
 
29447
29691
  class VerticalScrollBar extends owl.Component {
29692
+ static props = {
29693
+ topOffset: { type: Number, optional: true },
29694
+ };
29448
29695
  static components = { ScrollBar };
29449
29696
  static template = owl.xml /*xml*/ `
29450
29697
  <ScrollBar
@@ -29485,9 +29732,6 @@ class VerticalScrollBar extends owl.Component {
29485
29732
  });
29486
29733
  }
29487
29734
  }
29488
- VerticalScrollBar.props = {
29489
- topOffset: { type: Number, optional: true },
29490
- };
29491
29735
 
29492
29736
  const registries$1 = {
29493
29737
  ROW: rowMenuRegistry,
@@ -29501,6 +29745,13 @@ const registries$1 = {
29501
29745
  // -----------------------------------------------------------------------------
29502
29746
  class Grid extends owl.Component {
29503
29747
  static template = "o-spreadsheet-Grid";
29748
+ static props = {
29749
+ sidePanelIsOpen: Boolean,
29750
+ exposeFocus: Function,
29751
+ focusComposer: String,
29752
+ onComposerContentFocused: Function,
29753
+ onGridComposerCellFocused: Function,
29754
+ };
29504
29755
  static components = {
29505
29756
  GridComposer,
29506
29757
  GridOverlay,
@@ -29690,7 +29941,7 @@ class Grid extends owl.Component {
29690
29941
  "Ctrl+Shift+E": () => this.setHorizontalAlign("center"),
29691
29942
  "Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
29692
29943
  "Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
29693
- "Ctrl+Shift+V": () => PASTE_VALUE_ACTION(this.env),
29944
+ "Ctrl+Shift+V": () => PASTE_AS_VALUE_ACTION(this.env),
29694
29945
  "Ctrl+Shift+<": () => this.clearFormatting(),
29695
29946
  "Ctrl+<": () => this.clearFormatting(),
29696
29947
  "Ctrl+Shift+ ": () => {
@@ -29798,17 +30049,17 @@ class Grid extends owl.Component {
29798
30049
  // ---------------------------------------------------------------------------
29799
30050
  // Zone selection with mouse
29800
30051
  // ---------------------------------------------------------------------------
29801
- onCellClicked(col, row, { ctrlKey, shiftKey }) {
30052
+ onCellClicked(col, row, { addZone, expandZone }) {
29802
30053
  if (this.env.model.getters.hasOpenedPopover()) {
29803
30054
  this.closeOpenedPopover();
29804
30055
  }
29805
30056
  if (this.env.model.getters.getEditionMode() === "editing") {
29806
30057
  interactiveStopEdition(this.env);
29807
30058
  }
29808
- if (shiftKey) {
30059
+ if (expandZone) {
29809
30060
  this.env.model.selection.setAnchorCorner(col, row);
29810
30061
  }
29811
- else if (ctrlKey) {
30062
+ else if (addZone) {
29812
30063
  this.env.model.selection.addCellToSelection(col, row);
29813
30064
  }
29814
30065
  else {
@@ -30105,13 +30356,6 @@ class Grid extends owl.Component {
30105
30356
  }
30106
30357
  }
30107
30358
  }
30108
- Grid.props = {
30109
- sidePanelIsOpen: Boolean,
30110
- exposeFocus: Function,
30111
- focusComposer: String,
30112
- onComposerContentFocused: Function,
30113
- onGridComposerCellFocused: Function,
30114
- };
30115
30359
 
30116
30360
  /**
30117
30361
  * Represent a raw XML string
@@ -30611,7 +30855,7 @@ const XLSX_FORMATS_CONVERSION_MAP = {
30611
30855
  46: "hhhh:mm:ss",
30612
30856
  47: "hhhh:mm:ss",
30613
30857
  48: undefined,
30614
- 49: undefined,
30858
+ 49: PLAIN_TEXT_FORMAT,
30615
30859
  };
30616
30860
  /**
30617
30861
  * Mapping format index to format defined by default
@@ -31532,20 +31776,26 @@ function convertFigures(sheetData) {
31532
31776
  .filter(isDefined$1);
31533
31777
  }
31534
31778
  function convertFigure(figure, id, sheetData) {
31535
- const x1 = getColPosition(figure.anchors[0].col, sheetData) +
31536
- convertEMUToDotValue(figure.anchors[0].colOffset);
31537
- const x2 = getColPosition(figure.anchors[1].col, sheetData) +
31538
- convertEMUToDotValue(figure.anchors[1].colOffset);
31539
- const y1 = getRowPosition(figure.anchors[0].row, sheetData) +
31540
- convertEMUToDotValue(figure.anchors[0].rowOffset);
31541
- const y2 = getRowPosition(figure.anchors[1].row, sheetData) +
31542
- convertEMUToDotValue(figure.anchors[1].rowOffset);
31779
+ let x1, y1;
31780
+ let height, width;
31781
+ if (figure.anchors.length === 1) {
31782
+ // one cell anchor
31783
+ ({ x: x1, y: y1 } = getPositionFromAnchor(figure.anchors[0], sheetData));
31784
+ width = convertEMUToDotValue(figure.figureSize.cx);
31785
+ height = convertEMUToDotValue(figure.figureSize.cy);
31786
+ }
31787
+ else {
31788
+ ({ x: x1, y: y1 } = getPositionFromAnchor(figure.anchors[0], sheetData));
31789
+ const { x: x2, y: y2 } = getPositionFromAnchor(figure.anchors[1], sheetData);
31790
+ width = x2 - x1;
31791
+ height = y2 - y1;
31792
+ }
31543
31793
  const figureData = { id, x: x1, y: y1 };
31544
31794
  if (isChartData(figure.data)) {
31545
31795
  return {
31546
31796
  ...figureData,
31547
- width: x2 - x1,
31548
- height: y2 - y1,
31797
+ width,
31798
+ height,
31549
31799
  tag: "chart",
31550
31800
  data: convertChartData(figure.data),
31551
31801
  };
@@ -31611,6 +31861,12 @@ function convertExcelRangeToSheetXC(range, dataSetsHaveTitle) {
31611
31861
  const dataXC = zoneToXc(zone);
31612
31862
  return getFullReference(sheetName, dataXC);
31613
31863
  }
31864
+ function getPositionFromAnchor(anchor, sheetData) {
31865
+ return {
31866
+ x: getColPosition(anchor.col, sheetData) + convertEMUToDotValue(anchor.colOffset),
31867
+ y: getRowPosition(anchor.row, sheetData) + convertEMUToDotValue(anchor.rowOffset),
31868
+ };
31869
+ }
31614
31870
 
31615
31871
  /**
31616
31872
  * Match external reference (ex. '[1]Sheet 3'!$B$4)
@@ -32734,27 +32990,50 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
32734
32990
  }
32735
32991
  }
32736
32992
 
32993
+ const ONE_CELL_ANCHOR = "oneCellAnchor";
32994
+ const TWO_CELL_ANCHOR = "twoCellAnchor";
32737
32995
  class XlsxFigureExtractor extends XlsxBaseExtractor {
32738
32996
  extractFigures() {
32739
32997
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "xdr:wsDr", children: true }, (figureElement) => {
32740
32998
  const anchorType = removeTagEscapedNamespaces(figureElement.tagName);
32741
- if (anchorType !== "twoCellAnchor") {
32742
- throw new Error("Only twoCellAnchor are supported for xlsx drawings.");
32743
- }
32999
+ const anchors = this.extractFigureAnchorsByType(figureElement, anchorType);
32744
33000
  const chartElement = this.querySelector(figureElement, "c:chart");
32745
33001
  const imageElement = this.querySelector(figureElement, "a:blip");
32746
33002
  if (!chartElement && !imageElement) {
32747
33003
  throw new Error("Only chart and image figures are currently supported.");
32748
33004
  }
32749
33005
  return {
32750
- anchors: [
32751
- this.extractFigureAnchor("xdr:from", figureElement),
32752
- this.extractFigureAnchor("xdr:to", figureElement),
32753
- ],
33006
+ anchors,
32754
33007
  data: chartElement ? this.extractChart(chartElement) : this.extractImage(figureElement),
33008
+ figureSize: anchorType === ONE_CELL_ANCHOR
33009
+ ? this.extractFigureSizeFromSizeTag(figureElement, "xdr:ext")
33010
+ : undefined,
32755
33011
  };
32756
33012
  });
32757
33013
  }
33014
+ extractFigureAnchorsByType(figureElement, anchorType) {
33015
+ switch (anchorType) {
33016
+ case ONE_CELL_ANCHOR:
33017
+ return [this.extractFigureAnchor("xdr:from", figureElement)];
33018
+ case TWO_CELL_ANCHOR:
33019
+ return [
33020
+ this.extractFigureAnchor("xdr:from", figureElement),
33021
+ this.extractFigureAnchor("xdr:to", figureElement),
33022
+ ];
33023
+ default:
33024
+ throw new Error(`${anchorType} is not supported for xlsx drawings. `);
33025
+ }
33026
+ }
33027
+ extractFigureSizeFromSizeTag(figureElement, sizeTag) {
33028
+ const sizeElement = this.querySelector(figureElement, sizeTag);
33029
+ if (!sizeElement) {
33030
+ throw new Error(`Missing size element '${sizeTag}'`);
33031
+ }
33032
+ return {
33033
+ cx: this.extractAttr(sizeElement, "cx", { required: true }).asNum(),
33034
+ cy: this.extractAttr(sizeElement, "cy", { required: true }).asNum(),
33035
+ };
33036
+ }
32758
33037
  extractFigureAnchor(anchorTag, figureElement) {
32759
33038
  const anchor = this.querySelector(figureElement, anchorTag);
32760
33039
  if (!anchor) {
@@ -32783,15 +33062,15 @@ class XlsxFigureExtractor extends XlsxBaseExtractor {
32783
33062
  if (!image) {
32784
33063
  throw new Error("Unable to extract image");
32785
33064
  }
32786
- const shapePropertyElement = this.querySelector(figureElement, "a:xfrm");
32787
33065
  const extension = image.fileName.split(".").at(-1);
33066
+ const anchorType = removeTagEscapedNamespaces(figureElement.tagName);
33067
+ const sizeElement = anchorType === TWO_CELL_ANCHOR ? this.querySelector(figureElement, "a:xfrm") : figureElement;
33068
+ const sizeTag = anchorType === TWO_CELL_ANCHOR ? "a:ext" : "xdr:ext";
33069
+ const size = this.extractFigureSizeFromSizeTag(sizeElement, sizeTag);
32788
33070
  return {
32789
33071
  imageSrc: image.imageSrc,
32790
33072
  mimetype: extension ? IMAGE_EXTENSION_TO_MIMETYPE_MAPPING[extension] : undefined,
32791
- size: {
32792
- cx: this.extractChildAttr(shapePropertyElement, "a:ext", "cx", { required: true }).asNum(),
32793
- cy: this.extractChildAttr(shapePropertyElement, "a:ext", "cy", { required: true }).asNum(),
32794
- },
33073
+ size,
32795
33074
  };
32796
33075
  }
32797
33076
  }
@@ -34744,7 +35023,7 @@ class BordersPlugin extends CorePlugin {
34744
35023
  */
34745
35024
  function getBorderId(border) {
34746
35025
  for (let [key, value] of Object.entries(borders)) {
34747
- if (stringify(value) === stringify(border)) {
35026
+ if (deepEquals(value, border)) {
34748
35027
  return parseInt(key, 10);
34749
35028
  }
34750
35029
  }
@@ -35915,7 +36194,9 @@ class CellPlugin extends CorePlugin {
35915
36194
  }
35916
36195
  createLiteralCell(id, content, format, style) {
35917
36196
  const locale = this.getters.getLocale();
35918
- content = parseLiteral(content, locale).toString();
36197
+ if (format !== PLAIN_TEXT_FORMAT) {
36198
+ content = toString(parseLiteral(content, locale));
36199
+ }
35919
36200
  return {
35920
36201
  id,
35921
36202
  content,
@@ -40062,56 +40343,758 @@ class CompilationParametersBuilder {
40062
40343
  }
40063
40344
  }
40064
40345
 
40346
+ function quickselect(arr, k, left, right, compare) {
40347
+ quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
40348
+ }
40349
+
40350
+ function quickselectStep(arr, k, left, right, compare) {
40351
+
40352
+ while (right > left) {
40353
+ if (right - left > 600) {
40354
+ var n = right - left + 1;
40355
+ var m = k - left + 1;
40356
+ var z = Math.log(n);
40357
+ var s = 0.5 * Math.exp(2 * z / 3);
40358
+ var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
40359
+ var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
40360
+ var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
40361
+ quickselectStep(arr, k, newLeft, newRight, compare);
40362
+ }
40363
+
40364
+ var t = arr[k];
40365
+ var i = left;
40366
+ var j = right;
40367
+
40368
+ swap(arr, left, k);
40369
+ if (compare(arr[right], t) > 0) swap(arr, left, right);
40370
+
40371
+ while (i < j) {
40372
+ swap(arr, i, j);
40373
+ i++;
40374
+ j--;
40375
+ while (compare(arr[i], t) < 0) i++;
40376
+ while (compare(arr[j], t) > 0) j--;
40377
+ }
40378
+
40379
+ if (compare(arr[left], t) === 0) swap(arr, left, j);
40380
+ else {
40381
+ j++;
40382
+ swap(arr, j, right);
40383
+ }
40384
+
40385
+ if (j <= k) left = j + 1;
40386
+ if (k <= j) right = j - 1;
40387
+ }
40388
+ }
40389
+
40390
+ function swap(arr, i, j) {
40391
+ var tmp = arr[i];
40392
+ arr[i] = arr[j];
40393
+ arr[j] = tmp;
40394
+ }
40395
+
40396
+ function defaultCompare(a, b) {
40397
+ return a < b ? -1 : a > b ? 1 : 0;
40398
+ }
40399
+
40400
+ class RBush {
40401
+ constructor(maxEntries = 9) {
40402
+ // max entries in a node is 9 by default; min node fill is 40% for best performance
40403
+ this._maxEntries = Math.max(4, maxEntries);
40404
+ this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
40405
+ this.clear();
40406
+ }
40407
+
40408
+ all() {
40409
+ return this._all(this.data, []);
40410
+ }
40411
+
40412
+ search(bbox) {
40413
+ let node = this.data;
40414
+ const result = [];
40415
+
40416
+ if (!intersects(bbox, node)) return result;
40417
+
40418
+ const toBBox = this.toBBox;
40419
+ const nodesToSearch = [];
40420
+
40421
+ while (node) {
40422
+ for (let i = 0; i < node.children.length; i++) {
40423
+ const child = node.children[i];
40424
+ const childBBox = node.leaf ? toBBox(child) : child;
40425
+
40426
+ if (intersects(bbox, childBBox)) {
40427
+ if (node.leaf) result.push(child);
40428
+ else if (contains(bbox, childBBox)) this._all(child, result);
40429
+ else nodesToSearch.push(child);
40430
+ }
40431
+ }
40432
+ node = nodesToSearch.pop();
40433
+ }
40434
+
40435
+ return result;
40436
+ }
40437
+
40438
+ collides(bbox) {
40439
+ let node = this.data;
40440
+
40441
+ if (!intersects(bbox, node)) return false;
40442
+
40443
+ const nodesToSearch = [];
40444
+ while (node) {
40445
+ for (let i = 0; i < node.children.length; i++) {
40446
+ const child = node.children[i];
40447
+ const childBBox = node.leaf ? this.toBBox(child) : child;
40448
+
40449
+ if (intersects(bbox, childBBox)) {
40450
+ if (node.leaf || contains(bbox, childBBox)) return true;
40451
+ nodesToSearch.push(child);
40452
+ }
40453
+ }
40454
+ node = nodesToSearch.pop();
40455
+ }
40456
+
40457
+ return false;
40458
+ }
40459
+
40460
+ load(data) {
40461
+ if (!(data && data.length)) return this;
40462
+
40463
+ if (data.length < this._minEntries) {
40464
+ for (let i = 0; i < data.length; i++) {
40465
+ this.insert(data[i]);
40466
+ }
40467
+ return this;
40468
+ }
40469
+
40470
+ // recursively build the tree with the given data from scratch using OMT algorithm
40471
+ let node = this._build(data.slice(), 0, data.length - 1, 0);
40472
+
40473
+ if (!this.data.children.length) {
40474
+ // save as is if tree is empty
40475
+ this.data = node;
40476
+
40477
+ } else if (this.data.height === node.height) {
40478
+ // split root if trees have the same height
40479
+ this._splitRoot(this.data, node);
40480
+
40481
+ } else {
40482
+ if (this.data.height < node.height) {
40483
+ // swap trees if inserted one is bigger
40484
+ const tmpNode = this.data;
40485
+ this.data = node;
40486
+ node = tmpNode;
40487
+ }
40488
+
40489
+ // insert the small tree into the large tree at appropriate level
40490
+ this._insert(node, this.data.height - node.height - 1, true);
40491
+ }
40492
+
40493
+ return this;
40494
+ }
40495
+
40496
+ insert(item) {
40497
+ if (item) this._insert(item, this.data.height - 1);
40498
+ return this;
40499
+ }
40500
+
40501
+ clear() {
40502
+ this.data = createNode([]);
40503
+ return this;
40504
+ }
40505
+
40506
+ remove(item, equalsFn) {
40507
+ if (!item) return this;
40508
+
40509
+ let node = this.data;
40510
+ const bbox = this.toBBox(item);
40511
+ const path = [];
40512
+ const indexes = [];
40513
+ let i, parent, goingUp;
40514
+
40515
+ // depth-first iterative tree traversal
40516
+ while (node || path.length) {
40517
+
40518
+ if (!node) { // go up
40519
+ node = path.pop();
40520
+ parent = path[path.length - 1];
40521
+ i = indexes.pop();
40522
+ goingUp = true;
40523
+ }
40524
+
40525
+ if (node.leaf) { // check current node
40526
+ const index = findItem(item, node.children, equalsFn);
40527
+
40528
+ if (index !== -1) {
40529
+ // item found, remove the item and condense tree upwards
40530
+ node.children.splice(index, 1);
40531
+ path.push(node);
40532
+ this._condense(path);
40533
+ return this;
40534
+ }
40535
+ }
40536
+
40537
+ if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
40538
+ path.push(node);
40539
+ indexes.push(i);
40540
+ i = 0;
40541
+ parent = node;
40542
+ node = node.children[0];
40543
+
40544
+ } else if (parent) { // go right
40545
+ i++;
40546
+ node = parent.children[i];
40547
+ goingUp = false;
40548
+
40549
+ } else node = null; // nothing found
40550
+ }
40551
+
40552
+ return this;
40553
+ }
40554
+
40555
+ toBBox(item) { return item; }
40556
+
40557
+ compareMinX(a, b) { return a.minX - b.minX; }
40558
+ compareMinY(a, b) { return a.minY - b.minY; }
40559
+
40560
+ toJSON() { return this.data; }
40561
+
40562
+ fromJSON(data) {
40563
+ this.data = data;
40564
+ return this;
40565
+ }
40566
+
40567
+ _all(node, result) {
40568
+ const nodesToSearch = [];
40569
+ while (node) {
40570
+ if (node.leaf) result.push(...node.children);
40571
+ else nodesToSearch.push(...node.children);
40572
+
40573
+ node = nodesToSearch.pop();
40574
+ }
40575
+ return result;
40576
+ }
40577
+
40578
+ _build(items, left, right, height) {
40579
+
40580
+ const N = right - left + 1;
40581
+ let M = this._maxEntries;
40582
+ let node;
40583
+
40584
+ if (N <= M) {
40585
+ // reached leaf level; return leaf
40586
+ node = createNode(items.slice(left, right + 1));
40587
+ calcBBox(node, this.toBBox);
40588
+ return node;
40589
+ }
40590
+
40591
+ if (!height) {
40592
+ // target height of the bulk-loaded tree
40593
+ height = Math.ceil(Math.log(N) / Math.log(M));
40594
+
40595
+ // target number of root entries to maximize storage utilization
40596
+ M = Math.ceil(N / Math.pow(M, height - 1));
40597
+ }
40598
+
40599
+ node = createNode([]);
40600
+ node.leaf = false;
40601
+ node.height = height;
40602
+
40603
+ // split the items into M mostly square tiles
40604
+
40605
+ const N2 = Math.ceil(N / M);
40606
+ const N1 = N2 * Math.ceil(Math.sqrt(M));
40607
+
40608
+ multiSelect(items, left, right, N1, this.compareMinX);
40609
+
40610
+ for (let i = left; i <= right; i += N1) {
40611
+
40612
+ const right2 = Math.min(i + N1 - 1, right);
40613
+
40614
+ multiSelect(items, i, right2, N2, this.compareMinY);
40615
+
40616
+ for (let j = i; j <= right2; j += N2) {
40617
+
40618
+ const right3 = Math.min(j + N2 - 1, right2);
40619
+
40620
+ // pack each entry recursively
40621
+ node.children.push(this._build(items, j, right3, height - 1));
40622
+ }
40623
+ }
40624
+
40625
+ calcBBox(node, this.toBBox);
40626
+
40627
+ return node;
40628
+ }
40629
+
40630
+ _chooseSubtree(bbox, node, level, path) {
40631
+ while (true) {
40632
+ path.push(node);
40633
+
40634
+ if (node.leaf || path.length - 1 === level) break;
40635
+
40636
+ let minArea = Infinity;
40637
+ let minEnlargement = Infinity;
40638
+ let targetNode;
40639
+
40640
+ for (let i = 0; i < node.children.length; i++) {
40641
+ const child = node.children[i];
40642
+ const area = bboxArea(child);
40643
+ const enlargement = enlargedArea(bbox, child) - area;
40644
+
40645
+ // choose entry with the least area enlargement
40646
+ if (enlargement < minEnlargement) {
40647
+ minEnlargement = enlargement;
40648
+ minArea = area < minArea ? area : minArea;
40649
+ targetNode = child;
40650
+
40651
+ } else if (enlargement === minEnlargement) {
40652
+ // otherwise choose one with the smallest area
40653
+ if (area < minArea) {
40654
+ minArea = area;
40655
+ targetNode = child;
40656
+ }
40657
+ }
40658
+ }
40659
+
40660
+ node = targetNode || node.children[0];
40661
+ }
40662
+
40663
+ return node;
40664
+ }
40665
+
40666
+ _insert(item, level, isNode) {
40667
+ const bbox = isNode ? item : this.toBBox(item);
40668
+ const insertPath = [];
40669
+
40670
+ // find the best node for accommodating the item, saving all nodes along the path too
40671
+ const node = this._chooseSubtree(bbox, this.data, level, insertPath);
40672
+
40673
+ // put the item into the node
40674
+ node.children.push(item);
40675
+ extend(node, bbox);
40676
+
40677
+ // split on node overflow; propagate upwards if necessary
40678
+ while (level >= 0) {
40679
+ if (insertPath[level].children.length > this._maxEntries) {
40680
+ this._split(insertPath, level);
40681
+ level--;
40682
+ } else break;
40683
+ }
40684
+
40685
+ // adjust bboxes along the insertion path
40686
+ this._adjustParentBBoxes(bbox, insertPath, level);
40687
+ }
40688
+
40689
+ // split overflowed node into two
40690
+ _split(insertPath, level) {
40691
+ const node = insertPath[level];
40692
+ const M = node.children.length;
40693
+ const m = this._minEntries;
40694
+
40695
+ this._chooseSplitAxis(node, m, M);
40696
+
40697
+ const splitIndex = this._chooseSplitIndex(node, m, M);
40698
+
40699
+ const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
40700
+ newNode.height = node.height;
40701
+ newNode.leaf = node.leaf;
40702
+
40703
+ calcBBox(node, this.toBBox);
40704
+ calcBBox(newNode, this.toBBox);
40705
+
40706
+ if (level) insertPath[level - 1].children.push(newNode);
40707
+ else this._splitRoot(node, newNode);
40708
+ }
40709
+
40710
+ _splitRoot(node, newNode) {
40711
+ // split root node
40712
+ this.data = createNode([node, newNode]);
40713
+ this.data.height = node.height + 1;
40714
+ this.data.leaf = false;
40715
+ calcBBox(this.data, this.toBBox);
40716
+ }
40717
+
40718
+ _chooseSplitIndex(node, m, M) {
40719
+ let index;
40720
+ let minOverlap = Infinity;
40721
+ let minArea = Infinity;
40722
+
40723
+ for (let i = m; i <= M - m; i++) {
40724
+ const bbox1 = distBBox(node, 0, i, this.toBBox);
40725
+ const bbox2 = distBBox(node, i, M, this.toBBox);
40726
+
40727
+ const overlap = intersectionArea(bbox1, bbox2);
40728
+ const area = bboxArea(bbox1) + bboxArea(bbox2);
40729
+
40730
+ // choose distribution with minimum overlap
40731
+ if (overlap < minOverlap) {
40732
+ minOverlap = overlap;
40733
+ index = i;
40734
+
40735
+ minArea = area < minArea ? area : minArea;
40736
+
40737
+ } else if (overlap === minOverlap) {
40738
+ // otherwise choose distribution with minimum area
40739
+ if (area < minArea) {
40740
+ minArea = area;
40741
+ index = i;
40742
+ }
40743
+ }
40744
+ }
40745
+
40746
+ return index || M - m;
40747
+ }
40748
+
40749
+ // sorts node children by the best axis for split
40750
+ _chooseSplitAxis(node, m, M) {
40751
+ const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
40752
+ const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
40753
+ const xMargin = this._allDistMargin(node, m, M, compareMinX);
40754
+ const yMargin = this._allDistMargin(node, m, M, compareMinY);
40755
+
40756
+ // if total distributions margin value is minimal for x, sort by minX,
40757
+ // otherwise it's already sorted by minY
40758
+ if (xMargin < yMargin) node.children.sort(compareMinX);
40759
+ }
40760
+
40761
+ // total margin of all possible split distributions where each node is at least m full
40762
+ _allDistMargin(node, m, M, compare) {
40763
+ node.children.sort(compare);
40764
+
40765
+ const toBBox = this.toBBox;
40766
+ const leftBBox = distBBox(node, 0, m, toBBox);
40767
+ const rightBBox = distBBox(node, M - m, M, toBBox);
40768
+ let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
40769
+
40770
+ for (let i = m; i < M - m; i++) {
40771
+ const child = node.children[i];
40772
+ extend(leftBBox, node.leaf ? toBBox(child) : child);
40773
+ margin += bboxMargin(leftBBox);
40774
+ }
40775
+
40776
+ for (let i = M - m - 1; i >= m; i--) {
40777
+ const child = node.children[i];
40778
+ extend(rightBBox, node.leaf ? toBBox(child) : child);
40779
+ margin += bboxMargin(rightBBox);
40780
+ }
40781
+
40782
+ return margin;
40783
+ }
40784
+
40785
+ _adjustParentBBoxes(bbox, path, level) {
40786
+ // adjust bboxes along the given tree path
40787
+ for (let i = level; i >= 0; i--) {
40788
+ extend(path[i], bbox);
40789
+ }
40790
+ }
40791
+
40792
+ _condense(path) {
40793
+ // go through the path, removing empty nodes and updating bboxes
40794
+ for (let i = path.length - 1, siblings; i >= 0; i--) {
40795
+ if (path[i].children.length === 0) {
40796
+ if (i > 0) {
40797
+ siblings = path[i - 1].children;
40798
+ siblings.splice(siblings.indexOf(path[i]), 1);
40799
+
40800
+ } else this.clear();
40801
+
40802
+ } else calcBBox(path[i], this.toBBox);
40803
+ }
40804
+ }
40805
+ }
40806
+
40807
+ function findItem(item, items, equalsFn) {
40808
+ if (!equalsFn) return items.indexOf(item);
40809
+
40810
+ for (let i = 0; i < items.length; i++) {
40811
+ if (equalsFn(item, items[i])) return i;
40812
+ }
40813
+ return -1;
40814
+ }
40815
+
40816
+ // calculate node's bbox from bboxes of its children
40817
+ function calcBBox(node, toBBox) {
40818
+ distBBox(node, 0, node.children.length, toBBox, node);
40819
+ }
40820
+
40821
+ // min bounding rectangle of node children from k to p-1
40822
+ function distBBox(node, k, p, toBBox, destNode) {
40823
+ if (!destNode) destNode = createNode(null);
40824
+ destNode.minX = Infinity;
40825
+ destNode.minY = Infinity;
40826
+ destNode.maxX = -Infinity;
40827
+ destNode.maxY = -Infinity;
40828
+
40829
+ for (let i = k; i < p; i++) {
40830
+ const child = node.children[i];
40831
+ extend(destNode, node.leaf ? toBBox(child) : child);
40832
+ }
40833
+
40834
+ return destNode;
40835
+ }
40836
+
40837
+ function extend(a, b) {
40838
+ a.minX = Math.min(a.minX, b.minX);
40839
+ a.minY = Math.min(a.minY, b.minY);
40840
+ a.maxX = Math.max(a.maxX, b.maxX);
40841
+ a.maxY = Math.max(a.maxY, b.maxY);
40842
+ return a;
40843
+ }
40844
+
40845
+ function compareNodeMinX(a, b) { return a.minX - b.minX; }
40846
+ function compareNodeMinY(a, b) { return a.minY - b.minY; }
40847
+
40848
+ function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
40849
+ function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
40850
+
40851
+ function enlargedArea(a, b) {
40852
+ return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
40853
+ (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
40854
+ }
40855
+
40856
+ function intersectionArea(a, b) {
40857
+ const minX = Math.max(a.minX, b.minX);
40858
+ const minY = Math.max(a.minY, b.minY);
40859
+ const maxX = Math.min(a.maxX, b.maxX);
40860
+ const maxY = Math.min(a.maxY, b.maxY);
40861
+
40862
+ return Math.max(0, maxX - minX) *
40863
+ Math.max(0, maxY - minY);
40864
+ }
40865
+
40866
+ function contains(a, b) {
40867
+ return a.minX <= b.minX &&
40868
+ a.minY <= b.minY &&
40869
+ b.maxX <= a.maxX &&
40870
+ b.maxY <= a.maxY;
40871
+ }
40872
+
40873
+ function intersects(a, b) {
40874
+ return b.minX <= a.maxX &&
40875
+ b.minY <= a.maxY &&
40876
+ b.maxX >= a.minX &&
40877
+ b.maxY >= a.minY;
40878
+ }
40879
+
40880
+ function createNode(children) {
40881
+ return {
40882
+ children,
40883
+ height: 1,
40884
+ leaf: true,
40885
+ minX: Infinity,
40886
+ minY: Infinity,
40887
+ maxX: -Infinity,
40888
+ maxY: -Infinity
40889
+ };
40890
+ }
40891
+
40892
+ // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
40893
+ // combines selection algorithm with binary divide & conquer approach
40894
+
40895
+ function multiSelect(arr, left, right, n, compare) {
40896
+ const stack = [left, right];
40897
+
40898
+ while (stack.length) {
40899
+ right = stack.pop();
40900
+ left = stack.pop();
40901
+
40902
+ if (right - left <= n) continue;
40903
+
40904
+ const mid = left + Math.ceil((right - left) / n / 2) * n;
40905
+ quickselect(arr, mid, left, right, compare);
40906
+
40907
+ stack.push(left, mid, mid, right);
40908
+ }
40909
+ }
40910
+
40065
40911
  /**
40066
- * This class is an implementation of a dependency Graph.
40912
+ * R-Tree Data Structure
40913
+ *
40914
+ * R-Tree is a spatial data structure used for efficient indexing and querying
40915
+ * of multi-dimensional objects, particularly in geometric and spatial applications.
40916
+ *
40917
+ * It organizes objects into a tree hierarchy, grouping nearby objects together
40918
+ * in bounding boxes. Each node in the tree represents a bounding box that
40919
+ * contains its child nodes or leaf objects. This hierarchical structure allows
40920
+ * for faster spatial queries.
40921
+ *
40922
+ * @see https://en.wikipedia.org/wiki/R-tree
40923
+ *
40924
+ * Consider a 2D Space with four zones: A, B, C, D
40925
+ * +--------------------------+
40926
+ * | |
40927
+ * | +---+ +-------+ |
40928
+ * | | A | | B | |
40929
+ * | +---+ +-------+ |
40930
+ * | |
40931
+ * | |
40932
+ * | +---+ |
40933
+ * | | C | |
40934
+ * | +---+ |
40935
+ * | +-----------+ |
40936
+ * | | D | |
40937
+ * | +-----------+ |
40938
+ * | |
40939
+ * +--------------------------+
40940
+ *
40941
+ * It groups together zones that are spatially close into a minimum bounding box.
40942
+ * For example, A and B are grouped together in rectangle R1, and C and D are grouped
40943
+ * in R2.
40944
+ *
40945
+ * R0
40946
+ * +--------------------------+
40947
+ * | R1 |
40948
+ * | +-----------------+ |
40949
+ * | | A | | B | |
40950
+ * | +-----------------+ |
40951
+ * | |
40952
+ * | R2 |
40953
+ * | +---+---+---+ |
40954
+ * | | | C | | |
40955
+ * | | +---+ | |
40956
+ * | +-----------+ |
40957
+ * | | D | |
40958
+ * | +-----------+ |
40959
+ * | |
40960
+ * +--------------------------+
40961
+ *
40962
+ * The tree would look like this:
40963
+ * R0
40964
+ * / \
40965
+ * / \
40966
+ * R1 R2
40967
+ * | |
40968
+ * A,B C,D
40969
+
40970
+ * Choosing how to group the zones is crucial for the performance of the tree.
40971
+ * Key considerations include avoiding excessive empty space coverage and minimizing overlap
40972
+ * to reduce the number of subtrees processed during searches.
40973
+ *
40974
+ * Various heuristics exist for determining the optimal grouping strategy, such as "least enlargement"
40975
+ * which prioritizes grouping nodes resulting in the smallest increase in bounding box size. In cases where
40976
+ * the choice cannot be made based on this criterion due to the same enlargement for different groupings,
40977
+ * we then evaluate "least area," aiming to minimize the overall area of bounding boxes.
40978
+ *
40979
+ * This implementation is tailored for spreadsheet use, indexing objects associated
40980
+ * with a zone and a sheet.
40981
+ *
40982
+ * It uses the RBush library under the hood. One 2D RBush R-tree per sheet.
40983
+ * @see https://github.com/mourner/rbush
40984
+ */
40985
+ class SpreadsheetRTree {
40986
+ /**
40987
+ * One 2D R-tree per sheet
40988
+ */
40989
+ rTrees = {};
40990
+ /**
40991
+ * Bulk-inserts the given items into the tree. Bulk insertion is usually ~2-3 times
40992
+ * faster than inserting items one by one. After bulk loading (bulk insertion into
40993
+ * an empty tree), subsequent query performance is also ~20-30% better.
40994
+ */
40995
+ constructor(items = []) {
40996
+ const rangesPerSheet = {};
40997
+ for (const item of items) {
40998
+ const sheetId = item.boundingBox.sheetId;
40999
+ if (!rangesPerSheet[sheetId]) {
41000
+ rangesPerSheet[sheetId] = [];
41001
+ }
41002
+ rangesPerSheet[sheetId].push(item);
41003
+ }
41004
+ for (const sheetId in rangesPerSheet) {
41005
+ this.rTrees[sheetId] = new ZoneRBush();
41006
+ this.rTrees[sheetId].load(rangesPerSheet[sheetId]); // bulk-insert
41007
+ }
41008
+ }
41009
+ insert(item) {
41010
+ const sheetId = item.boundingBox.sheetId;
41011
+ if (!this.rTrees[sheetId]) {
41012
+ this.rTrees[sheetId] = new ZoneRBush();
41013
+ }
41014
+ this.rTrees[sheetId].insert(item);
41015
+ }
41016
+ search({ zone, sheetId }) {
41017
+ if (!this.rTrees[sheetId]) {
41018
+ return [];
41019
+ }
41020
+ return this.rTrees[sheetId].search({
41021
+ minX: zone.left,
41022
+ minY: zone.top,
41023
+ maxX: zone.right,
41024
+ maxY: zone.bottom,
41025
+ });
41026
+ }
41027
+ remove(item) {
41028
+ const sheetId = item.boundingBox.sheetId;
41029
+ if (!this.rTrees[sheetId]) {
41030
+ return;
41031
+ }
41032
+ this.rTrees[sheetId].remove(item, deepEquals);
41033
+ }
41034
+ }
41035
+ /**
41036
+ * RBush extension to use zones as bounding boxes
41037
+ */
41038
+ class ZoneRBush extends RBush {
41039
+ toBBox({ boundingBox }) {
41040
+ const zone = boundingBox.zone;
41041
+ return {
41042
+ minX: zone.left,
41043
+ minY: zone.top,
41044
+ maxX: zone.right,
41045
+ maxY: zone.bottom,
41046
+ };
41047
+ }
41048
+ compareMinX(a, b) {
41049
+ return a.boundingBox.zone.left - b.boundingBox.zone.left;
41050
+ }
41051
+ compareMinY(a, b) {
41052
+ return a.boundingBox.zone.top - b.boundingBox.zone.top;
41053
+ }
41054
+ }
41055
+
41056
+ /**
41057
+ * Implementation of a dependency Graph.
40067
41058
  * The graph is used to evaluate the cells in the correct
40068
41059
  * order, and should be updated each time a cell's content is modified
40069
41060
  *
41061
+ * It uses an R-Tree data structure to efficiently find dependent cells.
40070
41062
  */
40071
41063
  class FormulaDependencyGraph {
40072
- /**
40073
- * Internal structure:
40074
- * - key: a cell position (encoded as an integer)
40075
- * - value: a set of cell positions that depends on the key
40076
- *
40077
- * Given
40078
- * - A1:"= B1 + SQRT(B2)"
40079
- * - C1:"= B1";
40080
- * - C2:"= C1"
40081
- *
40082
- * we will have something like:
40083
- * - B1 ---> (A1, C1) meaning A1 and C1 depends on B1
40084
- * - B2 ---> (A1) meaning A1 depends on B2
40085
- * - C1 ---> (C2) meaning C2 depends on C1
40086
- */
40087
- inverseDependencies = new Map();
41064
+ encoder;
40088
41065
  dependencies = new Map();
41066
+ rTree;
41067
+ constructor(encoder, data = []) {
41068
+ this.encoder = encoder;
41069
+ this.rTree = new SpreadsheetRTree(data);
41070
+ }
40089
41071
  removeAllDependencies(formulaPositionId) {
40090
- const dependencies = this.dependencies.get(formulaPositionId);
40091
- if (!dependencies) {
41072
+ const ranges = this.dependencies.get(formulaPositionId);
41073
+ if (!ranges) {
40092
41074
  return;
40093
41075
  }
40094
- for (const dependency of dependencies) {
40095
- this.inverseDependencies.get(dependency)?.delete(formulaPositionId);
41076
+ for (const range of ranges) {
41077
+ this.rTree.remove(range);
40096
41078
  }
40097
41079
  this.dependencies.delete(formulaPositionId);
40098
41080
  }
40099
41081
  addDependencies(formulaPositionId, dependencies) {
40100
- for (const dependency of dependencies) {
40101
- const inverseDependencies = this.inverseDependencies.get(dependency);
40102
- if (inverseDependencies) {
40103
- inverseDependencies.add(formulaPositionId);
40104
- }
40105
- else {
40106
- this.inverseDependencies.set(dependency, new Set([formulaPositionId]));
40107
- }
41082
+ const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
41083
+ data: formulaPositionId,
41084
+ boundingBox: {
41085
+ zone,
41086
+ sheetId,
41087
+ },
41088
+ }));
41089
+ for (const item of rTreeItems) {
41090
+ this.rTree.insert(item);
40108
41091
  }
40109
41092
  const existingDependencies = this.dependencies.get(formulaPositionId);
40110
41093
  if (existingDependencies) {
40111
- existingDependencies.push(...dependencies);
41094
+ existingDependencies.push(...rTreeItems);
40112
41095
  }
40113
41096
  else {
40114
- this.dependencies.set(formulaPositionId, dependencies);
41097
+ this.dependencies.set(formulaPositionId, rTreeItems);
40115
41098
  }
40116
41099
  }
40117
41100
  /**
@@ -40119,20 +41102,20 @@ class FormulaDependencyGraph {
40119
41102
  * in the correct order they should be evaluated.
40120
41103
  * This is called a topological ordering (excluding cycles)
40121
41104
  */
40122
- getCellsDependingOn(positionIds) {
41105
+ getCellsDependingOn(ranges) {
40123
41106
  const visited = new JetSet();
40124
- const queue = Array.from(positionIds).reverse();
41107
+ const queue = Array.from(ranges).reverse();
40125
41108
  while (queue.length > 0) {
40126
- const node = queue.pop();
40127
- visited.add(node);
40128
- const adjacentNodes = this.inverseDependencies.get(node) || new Set();
40129
- for (const adjacentNode of adjacentNodes) {
40130
- if (!visited.has(adjacentNode)) {
40131
- queue.push(adjacentNode);
41109
+ const range = queue.pop();
41110
+ visited.add(...this.encoder.encodeBoundingBox(range));
41111
+ const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
41112
+ for (const positionId of impactedPositionIds) {
41113
+ if (!visited.has(positionId)) {
41114
+ queue.push(this.encoder.decodeToBoundingBox(positionId));
40132
41115
  }
40133
41116
  }
40134
41117
  }
40135
- visited.delete(...positionIds);
41118
+ visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
40136
41119
  return visited;
40137
41120
  }
40138
41121
  }
@@ -40220,9 +41203,9 @@ class Evaluator {
40220
41203
  context;
40221
41204
  getters;
40222
41205
  compilationParams;
40223
- positionEncoder = new PositionBitsEncoder();
41206
+ encoder = new PositionBitsEncoder();
40224
41207
  evaluatedCells = new Map();
40225
- formulaDependencies = lazy(new FormulaDependencyGraph());
41208
+ formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
40226
41209
  blockedArrayFormulas = new Set();
40227
41210
  spreadingRelations = new SpreadingRelation();
40228
41211
  constructor(context, getters) {
@@ -40231,23 +41214,23 @@ class Evaluator {
40231
41214
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40232
41215
  }
40233
41216
  getEvaluatedCell(position) {
40234
- return (this.evaluatedCells.get(this.encodePosition(position)) ||
41217
+ return (this.evaluatedCells.get(this.encoder.encode(position)) ||
40235
41218
  createEvaluatedCell("", { locale: this.getters.getLocale() }));
40236
41219
  }
40237
41220
  getSpreadPositionsOf(position) {
40238
- const positionId = this.encodePosition(position);
41221
+ const positionId = this.encoder.encode(position);
40239
41222
  if (!this.spreadingRelations.isArrayFormula(positionId)) {
40240
41223
  return [];
40241
41224
  }
40242
- return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.decodePosition.bind(this));
41225
+ return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
40243
41226
  }
40244
41227
  getArrayFormulaSpreadingOn(position) {
40245
- const positionId = this.encodePosition(position);
41228
+ const positionId = this.encoder.encode(position);
40246
41229
  const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
40247
- return formulaPosition !== undefined ? this.decodePosition(formulaPosition) : undefined;
41230
+ return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
40248
41231
  }
40249
41232
  getEvaluatedPositions() {
40250
- return [...this.evaluatedCells.keys()].map(this.decodePosition.bind(this));
41233
+ return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
40251
41234
  }
40252
41235
  getArrayFormulaSpreadingOnId(positionId) {
40253
41236
  if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
@@ -40257,7 +41240,7 @@ class Evaluator {
40257
41240
  return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
40258
41241
  }
40259
41242
  updateDependencies(position) {
40260
- const positionId = this.encodePosition(position);
41243
+ const positionId = this.encoder.encode(position);
40261
41244
  this.formulaDependencies().removeAllDependencies(positionId);
40262
41245
  const dependencies = this.getDirectDependencies(positionId);
40263
41246
  this.formulaDependencies().addDependencies(positionId, dependencies);
@@ -40267,12 +41250,12 @@ class Evaluator {
40267
41250
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
40268
41251
  }
40269
41252
  evaluateCells(positions) {
40270
- const cells = positions.map(this.encodePosition.bind(this));
41253
+ const cells = positions.map((p) => this.encoder.encode(p));
40271
41254
  const cellsToCompute = new JetSet(cells);
40272
- const arrayFormulas = this.getArrayFormulasImpactedByChangesOf(cells);
41255
+ const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
40273
41256
  cellsToCompute.add(...this.getCellsDependingOn(cells));
40274
- cellsToCompute.add(...arrayFormulas);
40275
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulas));
41257
+ cellsToCompute.add(...arrayFormulasPositionIds);
41258
+ cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
40276
41259
  this.evaluate(cellsToCompute);
40277
41260
  }
40278
41261
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -40295,12 +41278,14 @@ class Evaluator {
40295
41278
  this.blockedArrayFormulas = new Set();
40296
41279
  this.spreadingRelations = new SpreadingRelation();
40297
41280
  this.formulaDependencies = lazy(() => {
40298
- const dependencyGraph = new FormulaDependencyGraph();
40299
- for (const positionId of this.getAllCells()) {
40300
- const dependencies = this.getDirectDependencies(positionId);
40301
- dependencyGraph.addDependencies(positionId, dependencies);
40302
- }
40303
- return dependencyGraph;
41281
+ const dependencies = [...this.getAllCells()].flatMap((positionId) => this.getDirectDependencies(positionId).map((range) => ({
41282
+ data: positionId,
41283
+ boundingBox: {
41284
+ zone: range.zone,
41285
+ sheetId: range.sheetId,
41286
+ },
41287
+ })));
41288
+ return new FormulaDependencyGraph(this.encoder, dependencies);
40304
41289
  });
40305
41290
  }
40306
41291
  evaluateAllCells() {
@@ -40322,7 +41307,7 @@ class Evaluator {
40322
41307
  for (const sheetId of this.getters.getSheetIds()) {
40323
41308
  const cellIds = this.getters.getCells(sheetId);
40324
41309
  for (const cellId in cellIds) {
40325
- positionIds.add(this.encodePosition(this.getters.getCellPosition(cellId)));
41310
+ positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
40326
41311
  }
40327
41312
  }
40328
41313
  return positionIds;
@@ -40367,7 +41352,7 @@ class Evaluator {
40367
41352
  if (!this.blockedArrayFormulas.has(positionId)) {
40368
41353
  this.invalidateSpreading(positionId);
40369
41354
  }
40370
- const cellPosition = this.decodePosition(positionId);
41355
+ const cellPosition = this.encoder.decode(positionId);
40371
41356
  const cell = this.getters.getCell(cellPosition);
40372
41357
  if (cell === undefined) {
40373
41358
  return createEvaluatedCell("", { locale: this.getters.getLocale() });
@@ -40390,7 +41375,7 @@ class Evaluator {
40390
41375
  }
40391
41376
  }
40392
41377
  computeAndSave(position) {
40393
- const positionId = this.encodePosition(position);
41378
+ const positionId = this.encoder.encode(position);
40394
41379
  const evaluatedCell = this.computeCell(positionId);
40395
41380
  if (!this.evaluatedCells.has(positionId)) {
40396
41381
  this.setEvaluatedCell(positionId, evaluatedCell);
@@ -40445,15 +41430,15 @@ class Evaluator {
40445
41430
  throw new Error(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
40446
41431
  }
40447
41432
  updateSpreadRelation({ sheetId, col, row, }) {
40448
- const arrayFormulaPositionId = this.encodePosition({ sheetId, col, row });
41433
+ const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
40449
41434
  return (i, j) => {
40450
41435
  const position = { sheetId, col: i + col, row: j + row };
40451
- const resultPositionId = this.encodePosition(position);
41436
+ const resultPositionId = this.encoder.encode(position);
40452
41437
  this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
40453
41438
  };
40454
41439
  }
40455
41440
  checkCollision({ sheetId, col, row }) {
40456
- const formulaPositionId = this.encodePosition({ sheetId, col, row });
41441
+ const formulaPositionId = this.encoder.encode({ sheetId, col, row });
40457
41442
  return (i, j) => {
40458
41443
  const position = { sheetId: sheetId, col: i + col, row: j + row };
40459
41444
  const rawCell = this.getters.getCell(position);
@@ -40474,7 +41459,7 @@ class Evaluator {
40474
41459
  format: format || matrixResult[i][j]?.format,
40475
41460
  locale: this.getters.getLocale(),
40476
41461
  });
40477
- const positionId = this.encodePosition(position);
41462
+ const positionId = this.encoder.encode(position);
40478
41463
  this.setEvaluatedCell(positionId, evaluatedCell);
40479
41464
  // check if formula dependencies present in the spread zone
40480
41465
  // if so, they need to be recomputed
@@ -40506,29 +41491,17 @@ class Evaluator {
40506
41491
  if (!cell?.isFormula) {
40507
41492
  return [];
40508
41493
  }
40509
- const dependencies = [];
40510
- for (const range of cell.compiledFormula.dependencies) {
40511
- if (range.invalidSheetName || range.invalidXc) {
40512
- continue;
40513
- }
40514
- const sheetId = range.sheetId;
40515
- forEachPositionsInZone(range.zone, (col, row) => {
40516
- dependencies.push(this.encodePosition({ sheetId, col, row }));
40517
- });
40518
- }
40519
- return dependencies;
41494
+ return cell.compiledFormula.dependencies;
40520
41495
  }
40521
41496
  getCellsDependingOn(positionIds) {
40522
- return this.formulaDependencies().getCellsDependingOn(positionIds);
41497
+ const ranges = [];
41498
+ for (const positionId of positionIds) {
41499
+ ranges.push(this.encoder.decodeToBoundingBox(positionId));
41500
+ }
41501
+ return this.formulaDependencies().getCellsDependingOn(ranges);
40523
41502
  }
40524
41503
  getCell(positionId) {
40525
- return this.getters.getCell(this.decodePosition(positionId));
40526
- }
40527
- encodePosition(position) {
40528
- return this.positionEncoder.encode(position);
40529
- }
40530
- decodePosition(positionId) {
40531
- return this.positionEncoder.decode(positionId);
41504
+ return this.getters.getCell(this.encoder.decode(positionId));
40532
41505
  }
40533
41506
  }
40534
41507
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -40584,6 +41557,13 @@ class PositionBitsEncoder {
40584
41557
  encode({ sheetId, col, row }) {
40585
41558
  return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
40586
41559
  }
41560
+ encodeBoundingBox({ sheetId, zone }) {
41561
+ const positions = [];
41562
+ forEachPositionsInZone(zone, (col, row) => {
41563
+ positions.push(this.encode({ sheetId, col, row }));
41564
+ });
41565
+ return positions;
41566
+ }
40587
41567
  decode(id) {
40588
41568
  // keep only the last 21 bits by AND-ing the bit sequence with 21 ones
40589
41569
  const row = Number(id & 2097151n);
@@ -40591,6 +41571,10 @@ class PositionBitsEncoder {
40591
41571
  const sheetId = this.decodeSheet(id >> 42n);
40592
41572
  return { sheetId, col, row };
40593
41573
  }
41574
+ decodeToBoundingBox(id) {
41575
+ const { sheetId, col, row } = this.decode(id);
41576
+ return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
41577
+ }
40594
41578
  encodeSheet(sheetId) {
40595
41579
  const sheetKey = this.sheetMapping[sheetId];
40596
41580
  if (sheetKey === undefined) {
@@ -40773,7 +41757,12 @@ class EvaluationPlugin extends UIPlugin {
40773
41757
  // Getters
40774
41758
  // ---------------------------------------------------------------------------
40775
41759
  evaluateFormula(sheetId, formulaString) {
40776
- return this.evaluator.evaluateFormula(sheetId, formulaString);
41760
+ try {
41761
+ return this.evaluator.evaluateFormula(sheetId, formulaString);
41762
+ }
41763
+ catch (error) {
41764
+ return error instanceof EvaluationError ? error.errorType : CellErrorType.GenericError;
41765
+ }
40777
41766
  }
40778
41767
  /**
40779
41768
  * Return the value of each cell in the range as they are displayed in the grid.
@@ -41042,7 +42031,7 @@ class CustomColorsPlugin extends UIPlugin {
41042
42031
  }
41043
42032
 
41044
42033
  class EvaluationChartPlugin extends UIPlugin {
41045
- static getters = ["getChartRuntime", "getBackgroundOfSingleCellChart"];
42034
+ static getters = ["getChartRuntime", "getStyleOfSingleCellChart"];
41046
42035
  charts = {};
41047
42036
  createRuntimeChart = chartRuntimeFactory(this.getters);
41048
42037
  handle(cmd) {
@@ -41080,25 +42069,26 @@ class EvaluationChartPlugin extends UIPlugin {
41080
42069
  return this.charts[figureId];
41081
42070
  }
41082
42071
  /**
41083
- * Get the background color of a chart based on the color of the first cell of the main range
41084
- * of the chart. In order of priority, it will return :
41085
- *
41086
- * - the chart background color if one is defined
41087
- * - the fill color of the cell if one is defined
41088
- * - the fill color of the cell from conditional formats if one is defined
41089
- * - the default chart color if no other color is defined
42072
+ * Get the background and textColor of a chart based on the color of the first cell of the main range of the chart.
41090
42073
  */
41091
- getBackgroundOfSingleCellChart(chartBackground, mainRange) {
42074
+ getStyleOfSingleCellChart(chartBackground, mainRange) {
41092
42075
  if (chartBackground)
41093
- return chartBackground;
42076
+ return { background: chartBackground, fontColor: chartFontColor(chartBackground) };
41094
42077
  if (!mainRange) {
41095
- return BACKGROUND_CHART_COLOR;
42078
+ return {
42079
+ background: BACKGROUND_CHART_COLOR,
42080
+ fontColor: chartFontColor(BACKGROUND_CHART_COLOR),
42081
+ };
41096
42082
  }
41097
42083
  const col = mainRange.zone.left;
41098
42084
  const row = mainRange.zone.top;
41099
42085
  const sheetId = mainRange.sheetId;
41100
42086
  const style = this.getters.getCellComputedStyle({ sheetId, col, row });
41101
- return style.fillColor || BACKGROUND_CHART_COLOR;
42087
+ const background = style.fillColor || BACKGROUND_CHART_COLOR;
42088
+ return {
42089
+ background,
42090
+ fontColor: style.textColor || chartFontColor(background),
42091
+ };
41102
42092
  }
41103
42093
  exportForExcel(data) {
41104
42094
  for (const sheet of data.sheets) {
@@ -41209,45 +42199,40 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
41209
42199
  getComputedStyles(sheetId) {
41210
42200
  const computedStyle = {};
41211
42201
  for (let cf of this.getters.getConditionalFormats(sheetId).reverse()) {
41212
- try {
41213
- switch (cf.rule.type) {
41214
- case "ColorScaleRule":
41215
- for (let range of cf.ranges) {
41216
- this.applyColorScale(sheetId, range, cf.rule, computedStyle);
41217
- }
41218
- break;
41219
- case "CellIsRule":
41220
- const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
41221
- for (let ref of cf.ranges) {
41222
- const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
41223
- for (let row = zone.top; row <= zone.bottom; row++) {
41224
- for (let col = zone.left; col <= zone.right; col++) {
41225
- const predicate = this.rulePredicate[cf.rule.type];
41226
- const target = { sheetId, col, row };
41227
- const values = cf.rule.values.map((value, i) => {
41228
- const compiledFormula = formulas[i];
41229
- if (compiledFormula) {
41230
- return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
41231
- ...compiledFormula,
41232
- dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
41233
- });
41234
- }
41235
- return value;
41236
- });
41237
- if (predicate && predicate(target, { ...cf.rule, values })) {
41238
- if (!computedStyle[col])
41239
- computedStyle[col] = [];
41240
- // we must combine all the properties of all the CF rules applied to the given cell
41241
- computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
42202
+ switch (cf.rule.type) {
42203
+ case "ColorScaleRule":
42204
+ for (let range of cf.ranges) {
42205
+ this.applyColorScale(sheetId, range, cf.rule, computedStyle);
42206
+ }
42207
+ break;
42208
+ case "CellIsRule":
42209
+ const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
42210
+ for (let ref of cf.ranges) {
42211
+ const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
42212
+ for (let row = zone.top; row <= zone.bottom; row++) {
42213
+ for (let col = zone.left; col <= zone.right; col++) {
42214
+ const predicate = this.rulePredicate[cf.rule.type];
42215
+ const target = { sheetId, col, row };
42216
+ const values = cf.rule.values.map((value, i) => {
42217
+ const compiledFormula = formulas[i];
42218
+ if (compiledFormula) {
42219
+ return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
42220
+ ...compiledFormula,
42221
+ dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
42222
+ });
41242
42223
  }
42224
+ return value;
42225
+ });
42226
+ if (predicate && predicate(target, { ...cf.rule, values })) {
42227
+ if (!computedStyle[col])
42228
+ computedStyle[col] = [];
42229
+ // we must combine all the properties of all the CF rules applied to the given cell
42230
+ computedStyle[col][row] = Object.assign(computedStyle[col]?.[row] || {}, cf.rule.style);
41243
42231
  }
41244
42232
  }
41245
42233
  }
41246
- break;
41247
- }
41248
- }
41249
- catch (_) {
41250
- // we don't care about the errors within the evaluation of a rule
42234
+ }
42235
+ break;
41251
42236
  }
41252
42237
  }
41253
42238
  return computedStyle;
@@ -41858,11 +42843,6 @@ class AutofillPlugin extends UIPlugin {
41858
42843
  return "Success" /* CommandResult.Success */;
41859
42844
  }
41860
42845
  return "InvalidAutofillSelection" /* CommandResult.InvalidAutofillSelection */;
41861
- case "AUTOFILL_AUTO":
41862
- const zone = this.getters.getSelectedZone();
41863
- return zone.top === zone.bottom
41864
- ? "Success" /* CommandResult.Success */
41865
- : "CancelledForUnknownReason" /* CommandResult.CancelledForUnknownReason */;
41866
42846
  }
41867
42847
  return "Success" /* CommandResult.Success */;
41868
42848
  }
@@ -42019,7 +42999,7 @@ class AutofillPlugin extends UIPlugin {
42019
42999
  let row = zone.bottom;
42020
43000
  if (col > 0) {
42021
43001
  let leftPosition = { sheetId, col: col - 1, row };
42022
- while (this.getters.getEvaluatedCell(leftPosition).type !== CellValueType.empty ||
43002
+ while (this.getters.getCorrespondingFormulaCell(leftPosition) ||
42023
43003
  this.getters.getCell(leftPosition)?.content) {
42024
43004
  row += 1;
42025
43005
  leftPosition = { sheetId, col: col - 1, row };
@@ -42029,7 +43009,7 @@ class AutofillPlugin extends UIPlugin {
42029
43009
  col = zone.right;
42030
43010
  if (col <= this.getters.getNumberCols(sheetId)) {
42031
43011
  let rightPosition = { sheetId, col: col + 1, row };
42032
- while (this.getters.getEvaluatedCell(rightPosition).type !== CellValueType.empty ||
43012
+ while (this.getters.getCorrespondingFormulaCell(rightPosition) ||
42033
43013
  this.getters.getCell(rightPosition)?.content) {
42034
43014
  row += 1;
42035
43015
  rightPosition = { sheetId, col: col + 1, row };
@@ -43624,7 +44604,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43624
44604
  isPasteAllowed(target, clipboardOption) {
43625
44605
  const sheetId = this.getters.getActiveSheetId();
43626
44606
  if (this.operation === "CUT" && clipboardOption?.pasteOption !== undefined) {
43627
- // cannot paste only format or only value if the previous operation is a CUT
44607
+ // cannot paste only format or as value if the previous operation is a CUT
43628
44608
  return "WrongPasteOption" /* CommandResult.WrongPasteOption */;
43629
44609
  }
43630
44610
  if (target.length > 1) {
@@ -43806,7 +44786,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43806
44786
  // This condition is used to determine if we have to paste the CF or not.
43807
44787
  // We have to do it when the command handled is "PASTE", not "INSERT_CELL"
43808
44788
  // or "DELETE_CELL". So, the state should be the local state
43809
- const shouldPasteCF = clipboardOptions?.pasteOption !== "onlyValue" && clipboardOptions?.shouldPasteCF;
44789
+ const shouldPasteCF = clipboardOptions?.pasteOption !== "asValue" && clipboardOptions?.shouldPasteCF;
43810
44790
  const shouldPasteDV = !clipboardOptions?.pasteOption;
43811
44791
  const sheetId = this.getters.getActiveSheetId();
43812
44792
  // first, add missing cols/rows if needed
@@ -43842,10 +44822,11 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43842
44822
  pasteCell(origin, target, operation, clipboardOption) {
43843
44823
  const { sheetId, col, row } = target;
43844
44824
  const targetCell = this.getters.getEvaluatedCell(target);
43845
- if (clipboardOption?.pasteOption === "onlyValue") {
44825
+ const originFormat = origin.cell?.format ?? origin.evaluatedCell.format;
44826
+ if (clipboardOption?.pasteOption === "asValue") {
43846
44827
  const locale = this.getters.getLocale();
43847
44828
  const content = formatValue(origin.evaluatedCell.value, { locale });
43848
- this.dispatch("UPDATE_CELL", { ...target, content });
44829
+ this.dispatch("UPDATE_CELL", { ...target, content, format: originFormat });
43849
44830
  return;
43850
44831
  }
43851
44832
  const targetBorders = this.getters.getCellBorder(target);
@@ -43861,7 +44842,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43861
44842
  this.dispatch("UPDATE_CELL", {
43862
44843
  ...target,
43863
44844
  style: origin.cell?.style ?? null,
43864
- format: origin.cell?.format ?? origin.evaluatedCell.format ?? targetCell.format,
44845
+ format: originFormat ?? targetCell.format,
43865
44846
  });
43866
44847
  return;
43867
44848
  }
@@ -50064,6 +51045,20 @@ css /* scss */ `
50064
51045
  `;
50065
51046
  class RippleEffect extends owl.Component {
50066
51047
  static template = "o-spreadsheet-RippleEffect";
51048
+ static props = {
51049
+ x: String,
51050
+ y: String,
51051
+ color: String,
51052
+ opacity: Number,
51053
+ duration: Number,
51054
+ width: Number,
51055
+ height: Number,
51056
+ offsetY: Number,
51057
+ offsetX: Number,
51058
+ allowOverflow: Boolean,
51059
+ onAnimationEnd: Function,
51060
+ style: String,
51061
+ };
50067
51062
  rippleRef = owl.useRef("ripple");
50068
51063
  setup() {
50069
51064
  let animation = undefined;
@@ -50099,22 +51094,23 @@ class RippleEffect extends owl.Component {
50099
51094
  });
50100
51095
  }
50101
51096
  }
50102
- RippleEffect.props = {
50103
- x: String,
50104
- y: String,
50105
- color: String,
50106
- opacity: Number,
50107
- duration: Number,
50108
- width: Number,
50109
- height: Number,
50110
- offsetY: Number,
50111
- offsetX: Number,
50112
- allowOverflow: Boolean,
50113
- onAnimationEnd: Function,
50114
- style: String,
50115
- };
50116
51097
  class Ripple extends owl.Component {
50117
51098
  static template = "o-spreadsheet-Ripple";
51099
+ static props = {
51100
+ color: { type: String, optional: true },
51101
+ opacity: { type: Number, optional: true },
51102
+ duration: { type: Number, optional: true },
51103
+ ignoreClickPosition: { type: Boolean, optional: true },
51104
+ width: { type: Number, optional: true },
51105
+ height: { type: Number, optional: true },
51106
+ offsetY: { type: Number, optional: true },
51107
+ offsetX: { type: Number, optional: true },
51108
+ allowOverflow: { type: Boolean, optional: true },
51109
+ enabled: { type: Boolean, optional: true },
51110
+ onAnimationEnd: { type: Function, optional: true },
51111
+ slots: Object,
51112
+ class: { type: String, optional: true },
51113
+ };
50118
51114
  static components = { RippleEffect };
50119
51115
  static defaultProps = {
50120
51116
  color: "#aaaaaa",
@@ -50200,21 +51196,6 @@ class Ripple extends owl.Component {
50200
51196
  };
50201
51197
  }
50202
51198
  }
50203
- Ripple.props = {
50204
- color: { type: String, optional: true },
50205
- opacity: { type: Number, optional: true },
50206
- duration: { type: Number, optional: true },
50207
- ignoreClickPosition: { type: Boolean, optional: true },
50208
- width: { type: Number, optional: true },
50209
- height: { type: Number, optional: true },
50210
- offsetY: { type: Number, optional: true },
50211
- offsetX: { type: Number, optional: true },
50212
- allowOverflow: { type: Boolean, optional: true },
50213
- enabled: { type: Boolean, optional: true },
50214
- onAnimationEnd: { type: Function, optional: true },
50215
- slots: Object,
50216
- class: { type: String, optional: true },
50217
- };
50218
51199
 
50219
51200
  function interactiveRenameSheet(env, sheetId, name, errorCallback) {
50220
51201
  const result = env.model.dispatch("RENAME_SHEET", { sheetId, name });
@@ -50272,6 +51253,12 @@ css /* scss */ `
50272
51253
  `;
50273
51254
  class BottomBarSheet extends owl.Component {
50274
51255
  static template = "o-spreadsheet-BottomBarSheet";
51256
+ static props = {
51257
+ sheetId: String,
51258
+ openContextMenu: Function,
51259
+ style: { type: String, optional: true },
51260
+ onMouseDown: { type: Function, optional: true },
51261
+ };
50275
51262
  static components = { Ripple };
50276
51263
  static defaultProps = {
50277
51264
  onMouseDown: () => { },
@@ -50393,12 +51380,6 @@ class BottomBarSheet extends owl.Component {
50393
51380
  return this.env.model.getters.getSheetName(this.props.sheetId);
50394
51381
  }
50395
51382
  }
50396
- BottomBarSheet.props = {
50397
- sheetId: String,
50398
- openContextMenu: Function,
50399
- style: { type: String, optional: true },
50400
- onMouseDown: { type: Function, optional: true },
50401
- };
50402
51383
 
50403
51384
  // -----------------------------------------------------------------------------
50404
51385
  // SpreadSheet
@@ -50416,6 +51397,10 @@ css /* scss */ `
50416
51397
  `;
50417
51398
  class BottomBarStatistic extends owl.Component {
50418
51399
  static template = "o-spreadsheet-BottomBarStatisic";
51400
+ static props = {
51401
+ openContextMenu: Function,
51402
+ closeContextMenu: Function,
51403
+ };
50419
51404
  static components = { Ripple };
50420
51405
  selectedStatisticFn = "";
50421
51406
  statisticFnResults = {};
@@ -50462,10 +51447,6 @@ class BottomBarStatistic extends owl.Component {
50462
51447
  return fnName + ": " + (fnValue !== undefined ? formatValue(fnValue, { locale }) : "__");
50463
51448
  }
50464
51449
  }
50465
- BottomBarStatistic.props = {
50466
- openContextMenu: Function,
50467
- closeContextMenu: Function,
50468
- };
50469
51450
 
50470
51451
  // -----------------------------------------------------------------------------
50471
51452
  // SpreadSheet
@@ -50516,6 +51497,9 @@ css /* scss */ `
50516
51497
  `;
50517
51498
  class BottomBar extends owl.Component {
50518
51499
  static template = "o-spreadsheet-BottomBar";
51500
+ static props = {
51501
+ onClick: Function,
51502
+ };
50519
51503
  static components = { Menu, Ripple, BottomBarSheet, BottomBarStatistic };
50520
51504
  bottomBarRef = owl.useRef("bottomBar");
50521
51505
  sheetListRef = owl.useRef("sheetList");
@@ -50701,9 +51685,6 @@ class BottomBar extends owl.Component {
50701
51685
  return this.sheetListRef.el.scrollWidth - this.sheetListRef.el.clientWidth;
50702
51686
  }
50703
51687
  }
50704
- BottomBar.props = {
50705
- onClick: Function,
50706
- };
50707
51688
 
50708
51689
  css /* scss */ `
50709
51690
  .o-dashboard-clickable-cell {
@@ -50714,6 +51695,7 @@ css /* scss */ `
50714
51695
  let tKey = 1;
50715
51696
  class SpreadsheetDashboard extends owl.Component {
50716
51697
  static template = "o-spreadsheet-SpreadsheetDashboard";
51698
+ static props = {};
50717
51699
  static components = {
50718
51700
  GridOverlay,
50719
51701
  GridPopover,
@@ -50832,7 +51814,6 @@ class SpreadsheetDashboard extends owl.Component {
50832
51814
  return { ...this.canvasPosition, ...this.env.model.getters.getSheetViewDimensionWithHeaders() };
50833
51815
  }
50834
51816
  }
50835
- SpreadsheetDashboard.props = {};
50836
51817
 
50837
51818
  css /* scss */ `
50838
51819
  .o-header-group {
@@ -50860,6 +51841,11 @@ css /* scss */ `
50860
51841
  `;
50861
51842
  class AbstractHeaderGroup extends owl.Component {
50862
51843
  static template = "o-spreadsheet-HeaderGroup";
51844
+ static props = {
51845
+ group: Object,
51846
+ layerOffset: Number,
51847
+ openContextMenu: Function,
51848
+ };
50863
51849
  toggleGroup() {
50864
51850
  const sheetId = this.env.model.getters.getActiveSheetId();
50865
51851
  const { start, end } = this.props.group;
@@ -50896,11 +51882,6 @@ class AbstractHeaderGroup extends owl.Component {
50896
51882
  this.props.openContextMenu(position, menuItems);
50897
51883
  }
50898
51884
  }
50899
- AbstractHeaderGroup.props = {
50900
- group: Object,
50901
- layerOffset: Number,
50902
- openContextMenu: Function,
50903
- };
50904
51885
  class RowGroup extends AbstractHeaderGroup {
50905
51886
  dimension = "ROW";
50906
51887
  get groupBorderStyle() {
@@ -51031,6 +52012,10 @@ css /* scss */ `
51031
52012
  `;
51032
52013
  class HeaderGroupContainer extends owl.Component {
51033
52014
  static template = "o-spreadsheet-HeaderGroupContainer";
52015
+ static props = {
52016
+ dimension: String,
52017
+ layers: Array,
52018
+ };
51034
52019
  static components = { RowGroup, ColGroup, Menu };
51035
52020
  menu = owl.useState({ isOpen: false, position: null, menuItems: [] });
51036
52021
  getLayerOffset(layerIndex) {
@@ -51093,10 +52078,6 @@ class HeaderGroupContainer extends owl.Component {
51093
52078
  }
51094
52079
  }
51095
52080
  }
51096
- HeaderGroupContainer.props = {
51097
- dimension: String,
51098
- layers: Array,
51099
- };
51100
52081
 
51101
52082
  css /* scss */ `
51102
52083
  .o-sidePanel {
@@ -51207,14 +52188,6 @@ css /* scss */ `
51207
52188
  text-align: left;
51208
52189
  }
51209
52190
 
51210
- .o-checkbox {
51211
- display: flex;
51212
- justify-items: center;
51213
- input {
51214
- margin-right: 5px;
51215
- }
51216
- }
51217
-
51218
52191
  .o-inflection {
51219
52192
  table {
51220
52193
  table-layout: fixed;
@@ -51255,6 +52228,11 @@ css /* scss */ `
51255
52228
  `;
51256
52229
  class SidePanel extends owl.Component {
51257
52230
  static template = "o-spreadsheet-SidePanel";
52231
+ static props = {
52232
+ component: String,
52233
+ panelProps: { type: Object, optional: true },
52234
+ onCloseSidePanel: Function,
52235
+ };
51258
52236
  state;
51259
52237
  setup() {
51260
52238
  this.state = owl.useState({
@@ -51268,11 +52246,6 @@ class SidePanel extends owl.Component {
51268
52246
  : this.state.panel.title;
51269
52247
  }
51270
52248
  }
51271
- SidePanel.props = {
51272
- component: String,
51273
- panelProps: { type: Object, optional: true },
51274
- onCloseSidePanel: Function,
51275
- };
51276
52249
 
51277
52250
  css /* scss */ `
51278
52251
  .o-menu-item-button {
@@ -51290,6 +52263,13 @@ css /* scss */ `
51290
52263
  `;
51291
52264
  class ActionButton extends owl.Component {
51292
52265
  static template = "o-spreadsheet-ActionButton";
52266
+ static props = {
52267
+ action: Object,
52268
+ hasTriangleDownIcon: { type: Boolean, optional: true },
52269
+ selectedColor: { type: String, optional: true },
52270
+ class: { type: String, optional: true },
52271
+ onClick: { type: Function, optional: true },
52272
+ };
51293
52273
  actionButton = createAction(this.props.action);
51294
52274
  setup() {
51295
52275
  owl.onWillUpdateProps((nextProps) => {
@@ -51332,13 +52312,6 @@ class ActionButton extends owl.Component {
51332
52312
  return "";
51333
52313
  }
51334
52314
  }
51335
- ActionButton.props = {
51336
- action: Object,
51337
- hasTriangleDownIcon: { type: Boolean, optional: true },
51338
- selectedColor: { type: String, optional: true },
51339
- class: { type: String, optional: true },
51340
- onClick: { type: Function, optional: true },
51341
- };
51342
52315
 
51343
52316
  /**
51344
52317
  * List the available borders positions and the corresponding icons.
@@ -51435,6 +52408,17 @@ css /* scss */ `
51435
52408
  `;
51436
52409
  class BorderEditor extends owl.Component {
51437
52410
  static template = "o-spreadsheet-BorderEditor";
52411
+ static props = {
52412
+ class: { type: String, optional: true },
52413
+ currentBorderColor: { type: String, optional: false },
52414
+ currentBorderStyle: { type: String, optional: false },
52415
+ currentBorderPosition: { type: String, optional: true },
52416
+ onBorderColorPicked: Function,
52417
+ onBorderStylePicked: Function,
52418
+ onBorderPositionPicked: Function,
52419
+ maxHeight: { type: Number, optional: true },
52420
+ anchorRect: Object,
52421
+ };
51438
52422
  static components = { ColorPickerWidget, Popover };
51439
52423
  BORDER_POSITIONS = BORDER_POSITIONS;
51440
52424
  lineStyleButtonRef = owl.useRef("lineStyleButton");
@@ -51490,20 +52474,16 @@ class BorderEditor extends owl.Component {
51490
52474
  };
51491
52475
  }
51492
52476
  }
51493
- BorderEditor.props = {
51494
- class: { type: String, optional: true },
51495
- currentBorderColor: { type: String, optional: false },
51496
- currentBorderStyle: { type: String, optional: false },
51497
- currentBorderPosition: { type: String, optional: true },
51498
- onBorderColorPicked: Function,
51499
- onBorderStylePicked: Function,
51500
- onBorderPositionPicked: Function,
51501
- maxHeight: { type: Number, optional: true },
51502
- anchorRect: Object,
51503
- };
51504
52477
 
51505
52478
  class BorderEditorWidget extends owl.Component {
51506
52479
  static template = "o-spreadsheet-BorderEditorWidget";
52480
+ static props = {
52481
+ toggleBorderEditor: Function,
52482
+ showBorderEditor: Boolean,
52483
+ disabled: { type: Boolean, optional: true },
52484
+ dropdownMaxHeight: { type: Number, optional: true },
52485
+ class: { type: String, optional: true },
52486
+ };
51507
52487
  static components = { BorderEditor };
51508
52488
  borderEditorButtonRef = owl.useRef("borderEditorButton");
51509
52489
  state = owl.useState({
@@ -51548,13 +52528,6 @@ class BorderEditorWidget extends owl.Component {
51548
52528
  });
51549
52529
  }
51550
52530
  }
51551
- BorderEditorWidget.props = {
51552
- toggleBorderEditor: Function,
51553
- showBorderEditor: Boolean,
51554
- disabled: { type: Boolean, optional: true },
51555
- dropdownMaxHeight: { type: Number, optional: true },
51556
- class: { type: String, optional: true },
51557
- };
51558
52531
 
51559
52532
  const COMPOSER_MAX_HEIGHT = 100;
51560
52533
  /* svg free of use from https://uxwing.com/formula-fx-icon/ */
@@ -51583,6 +52556,12 @@ css /* scss */ `
51583
52556
  `;
51584
52557
  class TopBarComposer extends owl.Component {
51585
52558
  static template = "o-spreadsheet-TopBarComposer";
52559
+ static props = {
52560
+ focus: {
52561
+ validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
52562
+ },
52563
+ onComposerContentFocused: Function,
52564
+ };
51586
52565
  static components = { Composer };
51587
52566
  get composerStyle() {
51588
52567
  const style = {
@@ -51605,10 +52584,6 @@ class TopBarComposer extends owl.Component {
51605
52584
  });
51606
52585
  }
51607
52586
  }
51608
- TopBarComposer.props = {
51609
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
51610
- onComposerContentFocused: Function,
51611
- };
51612
52587
 
51613
52588
  css /* scss */ `
51614
52589
  .o-font-size-editor {
@@ -51637,6 +52612,11 @@ css /* scss */ `
51637
52612
  `;
51638
52613
  class FontSizeEditor extends owl.Component {
51639
52614
  static template = "o-spreadsheet-FontSizeEditor";
52615
+ static props = {
52616
+ onToggle: Function,
52617
+ dropdownStyle: String,
52618
+ class: String,
52619
+ };
51640
52620
  static components = {};
51641
52621
  fontSizes = FONT_SIZES;
51642
52622
  dropdown = owl.useState({ isOpen: false });
@@ -51693,14 +52673,12 @@ class FontSizeEditor extends owl.Component {
51693
52673
  }
51694
52674
  }
51695
52675
  }
51696
- FontSizeEditor.props = {
51697
- onToggle: Function,
51698
- dropdownStyle: String,
51699
- class: String,
51700
- };
51701
52676
 
51702
52677
  class PaintFormatButton extends owl.Component {
51703
52678
  static template = "o-spreadsheet-PaintFormatButton";
52679
+ static props = {
52680
+ class: { type: String, optional: true },
52681
+ };
51704
52682
  get isActive() {
51705
52683
  return this.env.model.getters.isPaintingFormat();
51706
52684
  }
@@ -51716,9 +52694,6 @@ class PaintFormatButton extends owl.Component {
51716
52694
  }
51717
52695
  }
51718
52696
  }
51719
- PaintFormatButton.props = {
51720
- class: { type: String, optional: true },
51721
- };
51722
52697
 
51723
52698
  // -----------------------------------------------------------------------------
51724
52699
  // TopBar
@@ -51811,6 +52786,12 @@ css /* scss */ `
51811
52786
  `;
51812
52787
  class TopBar extends owl.Component {
51813
52788
  static template = "o-spreadsheet-TopBar";
52789
+ static props = {
52790
+ onClick: Function,
52791
+ focusComposer: String,
52792
+ onComposerContentFocused: Function,
52793
+ dropdownMaxHeight: Number,
52794
+ };
51814
52795
  get dropdownStyle() {
51815
52796
  return `max-height:${this.props.dropdownMaxHeight}px`;
51816
52797
  }
@@ -51927,12 +52908,6 @@ class TopBar extends owl.Component {
51927
52908
  this.onClick();
51928
52909
  }
51929
52910
  }
51930
- TopBar.props = {
51931
- onClick: Function,
51932
- focusComposer: String,
51933
- onComposerContentFocused: Function,
51934
- dropdownMaxHeight: Number,
51935
- };
51936
52911
 
51937
52912
  function instantiateClipboard() {
51938
52913
  return new WebClipboardWrapper(navigator.clipboard);
@@ -52165,6 +53140,9 @@ css /* scss */ `
52165
53140
  `;
52166
53141
  class Spreadsheet extends owl.Component {
52167
53142
  static template = "o-spreadsheet-Spreadsheet";
53143
+ static props = {
53144
+ model: Object,
53145
+ };
52168
53146
  static components = {
52169
53147
  TopBar,
52170
53148
  Grid,
@@ -52309,7 +53287,7 @@ class Spreadsheet extends owl.Component {
52309
53287
  }
52310
53288
  onKeydown(ev) {
52311
53289
  let keyDownString = "";
52312
- if (ev.ctrlKey || ev.metaKey) {
53290
+ if (isCtrlKey(ev)) {
52313
53291
  keyDownString += "CTRL+";
52314
53292
  }
52315
53293
  keyDownString += ev.key.toUpperCase();
@@ -52377,9 +53355,6 @@ class Spreadsheet extends owl.Component {
52377
53355
  return this.env.model.getters.getVisibleGroupLayers(sheetId, "COL");
52378
53356
  }
52379
53357
  }
52380
- Spreadsheet.props = {
52381
- model: Object,
52382
- };
52383
53358
 
52384
53359
  class LocalTransportService {
52385
53360
  listeners = [];
@@ -55046,7 +56021,8 @@ function addRows(construct, data, sheet) {
55046
56021
  }
55047
56022
  else if (cell.content && cell.content !== "") {
55048
56023
  const isTableHeader = isCellTableHeader(c, r, sheet);
55049
- ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
56024
+ const isPlainText = !!(cell.format && data.formats[cell.format] === PLAIN_TEXT_FORMAT);
56025
+ ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
55050
56026
  }
55051
56027
  attributes.push(...additionalAttrs);
55052
56028
  cellNodes.push(escapeXml /*xml*/ `
@@ -55975,6 +56951,7 @@ const helpers = {
55975
56951
  colorToRGBA,
55976
56952
  positionToZone,
55977
56953
  isDefined: isDefined$1,
56954
+ isMatrix,
55978
56955
  lazy,
55979
56956
  genericRepeat,
55980
56957
  createAction,
@@ -56059,6 +57036,6 @@ exports.setTranslationMethod = setTranslationMethod;
56059
57036
  exports.tokenize = tokenize;
56060
57037
 
56061
57038
 
56062
- __info__.version = "17.1.0-alpha.5";
56063
- __info__.date = "2023-12-05T09:51:40.034Z";
56064
- __info__.hash = "c2823eb";
57039
+ __info__.version = "17.1.0-alpha.7";
57040
+ __info__.date = "2024-01-12T13:45:00.505Z";
57041
+ __info__.hash = "cbce1ed";