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