@odoo/o-spreadsheet 17.2.0-alpha.6 → 17.2.0

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.2.0-alpha.6
7
- * @date 2024-02-28T12:28:44.114Z
8
- * @hash 318a04e
6
+ * @version 17.2.0
7
+ * @date 2024-03-20T08:12:44.398Z
8
+ * @hash 1869c24
9
9
  */
10
10
 
11
11
  (function (exports, owl) {
@@ -190,8 +190,8 @@
190
190
  const DEFAULT_GAUGE_MIDDLE_COLOR = "#f1c232";
191
191
  const DEFAULT_GAUGE_UPPER_COLOR = "#6aa84f";
192
192
  const DEFAULT_SCORECARD_BASELINE_MODE = "difference";
193
- const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#00A04A";
194
- const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#DC6965";
193
+ const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#6aa84f";
194
+ const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#E06666";
195
195
  const LINE_FILL_TRANSPARENCY = 0.4;
196
196
  // session
197
197
  const DEBOUNCE_TIME = 200;
@@ -641,21 +641,6 @@
641
641
  }
642
642
  return true;
643
643
  }
644
- class JetSet extends Set {
645
- addMany(iterable) {
646
- for (const element of iterable) {
647
- super.add(element);
648
- }
649
- return this;
650
- }
651
- deleteMany(iterable) {
652
- let wasDeleted = false;
653
- for (const element of iterable) {
654
- wasDeleted ||= super.delete(element);
655
- }
656
- return wasDeleted;
657
- }
658
- }
659
644
  /**
660
645
  * Creates a version of the function that's memoized on the value of its first
661
646
  * argument, if any.
@@ -2145,7 +2130,6 @@
2145
2130
  Background: 0,
2146
2131
  Highlights: 1,
2147
2132
  Clipboard: 2,
2148
- Search: 3,
2149
2133
  Chart: 4,
2150
2134
  Autofill: 5,
2151
2135
  Selection: 6,
@@ -4102,14 +4086,6 @@
4102
4086
  }
4103
4087
  return positions;
4104
4088
  }
4105
- function forEachPositionsInZone(zone, callback) {
4106
- const { left, right, top, bottom } = zone;
4107
- for (let col = left; col <= right; col++) {
4108
- for (let row = top; row <= bottom; row++) {
4109
- callback(col, row);
4110
- }
4111
- }
4112
- }
4113
4089
  /**
4114
4090
  * This function returns a zone with coordinates modified according to the change
4115
4091
  * applied to the zone. It may be possible to change the zone by resizing or moving
@@ -6317,6 +6293,11 @@
6317
6293
  this.pasteTableCell(sheetId, tableCell, position, clipboardOptions);
6318
6294
  }
6319
6295
  }
6296
+ if (tableCells.length === 1) {
6297
+ for (let c = 0; c < tableCells[0].length; c++) {
6298
+ this.dispatch("AUTOFILL_TABLE_COLUMN", { col: col + c, row, sheetId });
6299
+ }
6300
+ }
6320
6301
  }
6321
6302
  pasteTableCell(sheetId, tableCell, position, options) {
6322
6303
  if (tableCell.table && !options?.pasteOption) {
@@ -7443,6 +7424,7 @@
7443
7424
  firstColumn: _t("First column"),
7444
7425
  lastColumn: _t("Last column"),
7445
7426
  bandedColumns: _t("Banded columns"),
7427
+ automaticAutofill: _t("Automatically autofill formulas"),
7446
7428
  totalRow: _t("Total row"),
7447
7429
  },
7448
7430
  Tooltips: {
@@ -8189,11 +8171,18 @@ stores.inject(MyMetaStore, storeInstance);
8189
8171
  }
8190
8172
  const color = highlight.color || HIGHLIGHT_COLOR;
8191
8173
  const { ctx } = renderingContext;
8192
- ctx.lineWidth = 2;
8193
- ctx.strokeStyle = color;
8194
- /** + 0.5 offset to have sharp lines. See comment in {@link RendererPlugin#drawBorders} for more details */
8195
- ctx.strokeRect(x + 0.5, y + 0.5, width, height);
8196
- ctx.globalCompositeOperation = "source-over";
8174
+ if (!highlight.noBorder) {
8175
+ ctx.strokeStyle = color;
8176
+ if (highlight.thinLine) {
8177
+ ctx.lineWidth = 1;
8178
+ ctx.strokeRect(x, y, width, height);
8179
+ }
8180
+ else {
8181
+ ctx.lineWidth = 2;
8182
+ /** + 0.5 offset to have sharp lines. See comment in {@link RendererPlugin#drawBorder} for more details */
8183
+ ctx.strokeRect(x + 0.5, y + 0.5, width, height);
8184
+ }
8185
+ }
8197
8186
  if (!highlight.noFill) {
8198
8187
  ctx.fillStyle = setColorAlpha(color, highlight.fillAlpha ?? 0.12);
8199
8188
  ctx.fillRect(x, y, width, height);
@@ -8567,6 +8556,7 @@ stores.inject(MyMetaStore, storeInstance);
8567
8556
  else if (cell.link) {
8568
8557
  content = markdownLink(content, cell.link.url);
8569
8558
  }
8559
+ this.addHeadersForSpreadingFormula(content);
8570
8560
  this.model.dispatch("UPDATE_CELL", {
8571
8561
  sheetId: this.sheetId,
8572
8562
  col,
@@ -8582,6 +8572,7 @@ stores.inject(MyMetaStore, storeInstance);
8582
8572
  row,
8583
8573
  });
8584
8574
  }
8575
+ this.model.dispatch("AUTOFILL_TABLE_COLUMN", { col, row, sheetId: this.sheetId });
8585
8576
  this.setContent("");
8586
8577
  }
8587
8578
  }
@@ -8784,6 +8775,38 @@ stores.inject(MyMetaStore, storeInstance);
8784
8775
  }
8785
8776
  this.colorIndexByRange = colorsToKeep;
8786
8777
  }
8778
+ /** Add headers at the end of the sheet so the formula in the composer has enough space to spread */
8779
+ addHeadersForSpreadingFormula(content) {
8780
+ if (!content.startsWith("=")) {
8781
+ return;
8782
+ }
8783
+ const evaluated = this.getters.evaluateFormula(this.sheetId, content);
8784
+ if (!isMatrix(evaluated)) {
8785
+ return;
8786
+ }
8787
+ const numberOfRows = this.getters.getNumberRows(this.sheetId);
8788
+ const numberOfCols = this.getters.getNumberCols(this.sheetId);
8789
+ const missingRows = this.row + evaluated[0].length - numberOfRows;
8790
+ const missingCols = this.col + evaluated.length - numberOfCols;
8791
+ if (missingCols > 0) {
8792
+ this.model.dispatch("ADD_COLUMNS_ROWS", {
8793
+ sheetId: this.sheetId,
8794
+ dimension: "COL",
8795
+ base: numberOfCols - 1,
8796
+ position: "after",
8797
+ quantity: missingCols + 20,
8798
+ });
8799
+ }
8800
+ if (missingRows > 0) {
8801
+ this.model.dispatch("ADD_COLUMNS_ROWS", {
8802
+ sheetId: this.sheetId,
8803
+ dimension: "ROW",
8804
+ base: numberOfRows - 1,
8805
+ position: "after",
8806
+ quantity: missingRows + 50,
8807
+ });
8808
+ }
8809
+ }
8787
8810
  /**
8788
8811
  * Highlight all ranges that can be found in the composer content.
8789
8812
  */
@@ -10008,6 +10031,9 @@ stores.inject(MyMetaStore, storeInstance);
10008
10031
  result.default = true;
10009
10032
  result.defaultValue = defaultValue;
10010
10033
  }
10034
+ if (types.some((t) => t.startsWith("RANGE"))) {
10035
+ result.acceptMatrix = true;
10036
+ }
10011
10037
  return result;
10012
10038
  }
10013
10039
  /**
@@ -17417,10 +17443,11 @@ stores.inject(MyMetaStore, storeInstance);
17417
17443
  const DEFAULT_MATCH_MODE = 0;
17418
17444
  const DEFAULT_SEARCH_MODE = 1;
17419
17445
  const DEFAULT_ABSOLUTE_RELATIVE_MODE = 1;
17420
- function assertAvailable(variable, searchKey) {
17421
- if (variable === undefined) {
17422
- throw new NotAvailableError(_t("Did not find value '%s' in [[FUNCTION_NAME]] evaluation.", toString(searchKey)));
17423
- }
17446
+ function valueNotAvailable(searchKey) {
17447
+ return {
17448
+ value: CellErrorType.NotAvailable,
17449
+ message: _t("Did not find value '%s' in [[FUNCTION_NAME]] evaluation.", toString(searchKey)),
17450
+ };
17424
17451
  }
17425
17452
  // -----------------------------------------------------------------------------
17426
17453
  // ADDRESS
@@ -17517,7 +17544,9 @@ stores.inject(MyMetaStore, storeInstance);
17517
17544
  ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
17518
17545
  : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
17519
17546
  const col = range[colIndex];
17520
- assertAvailable(col, searchKey?.value);
17547
+ if (col === undefined) {
17548
+ return valueNotAvailable(searchKey);
17549
+ }
17521
17550
  return col[_index - 1];
17522
17551
  },
17523
17552
  isExported: true,
@@ -17636,11 +17665,11 @@ stores.inject(MyMetaStore, storeInstance);
17636
17665
  : (range, index) => range[index][0].value;
17637
17666
  const rangeLength = verticalSearch ? nbRow : nbCol;
17638
17667
  const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
17639
- if (index === -1)
17640
- assertAvailable(undefined, searchKey?.value);
17641
- verticalSearch
17642
- ? assertAvailable(searchArray[0][index], searchKey?.value)
17643
- : assertAvailable(searchArray[index][nbRow - 1], searchKey?.value);
17668
+ if (index === -1 ||
17669
+ (verticalSearch && searchArray[0][index] === undefined) ||
17670
+ (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
17671
+ return valueNotAvailable(searchKey);
17672
+ }
17644
17673
  if (resultRange === undefined) {
17645
17674
  return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
17646
17675
  }
@@ -17690,7 +17719,10 @@ stores.inject(MyMetaStore, storeInstance);
17690
17719
  index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
17691
17720
  break;
17692
17721
  }
17693
- assertAvailable(nbCol === 1 ? range[0][index] : range[index], searchKey);
17722
+ if ((nbCol === 1 && range[0][index] === undefined) ||
17723
+ (nbCol !== 1 && range[index] === undefined)) {
17724
+ return valueNotAvailable(searchKey);
17725
+ }
17694
17726
  return index + 1;
17695
17727
  },
17696
17728
  isExported: true,
@@ -17749,7 +17781,9 @@ stores.inject(MyMetaStore, storeInstance);
17749
17781
  ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
17750
17782
  : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
17751
17783
  const value = range[_index - 1][rowIndex];
17752
- assertAvailable(value, searchKey);
17784
+ if (value === undefined) {
17785
+ return valueNotAvailable(searchKey);
17786
+ }
17753
17787
  return value;
17754
17788
  },
17755
17789
  isExported: true,
@@ -17809,7 +17843,9 @@ stores.inject(MyMetaStore, storeInstance);
17809
17843
  ? returnRange.map((col) => [col[index]])
17810
17844
  : [returnRange[index]];
17811
17845
  }
17812
- assertAvailable(defaultValue, searchKey);
17846
+ if (defaultValue === undefined) {
17847
+ return valueNotAvailable(searchKey);
17848
+ }
17813
17849
  return [[defaultValue]];
17814
17850
  },
17815
17851
  isExported: true,
@@ -18568,16 +18604,31 @@ stores.inject(MyMetaStore, storeInstance);
18568
18604
  }
18569
18605
  const descr = addMetaInfoFromArg(addDescr);
18570
18606
  validateArguments(descr.args);
18571
- this.mapping[name] = addErrorHandling(addResultHandling(descr.compute), name);
18607
+ this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
18572
18608
  super.add(name, descr);
18573
18609
  return this;
18574
18610
  }
18575
18611
  }
18612
+ function addInputHandling(descr) {
18613
+ function computeWithInputHandling(...args) {
18614
+ for (let i = 0; i < args.length; i++) {
18615
+ const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
18616
+ const arg = args[i];
18617
+ if (isMatrix(arg) && !argDefinition.acceptMatrix) {
18618
+ if (arg.length !== 1 || arg[0].length !== 1) {
18619
+ throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be a single value or a single cell reference, not a range.", argDefinition.name));
18620
+ }
18621
+ args[i] = arg[0][0];
18622
+ }
18623
+ }
18624
+ return descr.compute.apply(this, args);
18625
+ }
18626
+ return computeWithInputHandling;
18627
+ }
18576
18628
  function addErrorHandling(compute, functionName) {
18577
18629
  return function (...args) {
18578
18630
  try {
18579
- const computeFormula = compute.bind(this);
18580
- return computeFormula(...args);
18631
+ return compute.apply(this, args);
18581
18632
  }
18582
18633
  catch (e) {
18583
18634
  return handleError(e, functionName);
@@ -18611,8 +18662,7 @@ stores.inject(MyMetaStore, storeInstance);
18611
18662
  }
18612
18663
  function addResultHandling(compute) {
18613
18664
  return function (...args) {
18614
- const computeResult = compute.bind(this);
18615
- const result = computeResult(...args);
18665
+ const result = compute.apply(this, args);
18616
18666
  if (!isMatrix(result)) {
18617
18667
  if (typeof result === "object" && result !== null && "value" in result) {
18618
18668
  return result;
@@ -18827,8 +18877,10 @@ stores.inject(MyMetaStore, storeInstance);
18827
18877
  if (x === cell) {
18828
18878
  found = true;
18829
18879
  }
18830
- const cellValue = evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
18831
- if (filter(cellValue)) {
18880
+ const cellValue = x?.isFormula
18881
+ ? undefined
18882
+ : evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
18883
+ if (cellValue && filter(cellValue)) {
18832
18884
  group.push(cellValue);
18833
18885
  }
18834
18886
  else {
@@ -19477,9 +19529,15 @@ stores.inject(MyMetaStore, storeInstance);
19477
19529
  : (label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`);
19478
19530
  }
19479
19531
  else {
19480
- label = label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`;
19532
+ label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`;
19481
19533
  }
19482
19534
  let data = ds.dataRange ? getData(getters, ds) : [];
19535
+ if (data.every((e) => typeof e === "string" && !isEvaluationError(e))) {
19536
+ // In this case, we want a chart based on the string occurrences count
19537
+ // This will be done by associating each string with a value of 1 and
19538
+ // the using the classical aggregation method to sum the values.
19539
+ data.fill(1);
19540
+ }
19483
19541
  datasetValues.push({ data, label });
19484
19542
  }
19485
19543
  return datasetValues;
@@ -19578,7 +19636,7 @@ stores.inject(MyMetaStore, storeInstance);
19578
19636
  dataSets: context.range ? context.range : [],
19579
19637
  dataSetsHaveTitle: false,
19580
19638
  stacked: false,
19581
- aggregated: false,
19639
+ aggregated: context.aggregated ?? false,
19582
19640
  legendPosition: "top",
19583
19641
  title: context.title || "",
19584
19642
  type: "bar",
@@ -19594,6 +19652,7 @@ stores.inject(MyMetaStore, storeInstance);
19594
19652
  auxiliaryRange: this.labelRange
19595
19653
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
19596
19654
  : undefined,
19655
+ aggregated: this.aggregated,
19597
19656
  };
19598
19657
  }
19599
19658
  copyForSheetId(sheetId) {
@@ -20343,7 +20402,7 @@ stores.inject(MyMetaStore, storeInstance);
20343
20402
  verticalAxisPosition: "left",
20344
20403
  labelRange: context.auxiliaryRange || undefined,
20345
20404
  stacked: false,
20346
- aggregated: false,
20405
+ aggregated: context.aggregated ?? false,
20347
20406
  cumulative: false,
20348
20407
  };
20349
20408
  }
@@ -20376,6 +20435,7 @@ stores.inject(MyMetaStore, storeInstance);
20376
20435
  auxiliaryRange: this.labelRange
20377
20436
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
20378
20437
  : undefined,
20438
+ aggregated: this.aggregated,
20379
20439
  };
20380
20440
  }
20381
20441
  updateRanges(applyChange) {
@@ -20450,7 +20510,7 @@ stores.inject(MyMetaStore, storeInstance);
20450
20510
  title: context.title || "",
20451
20511
  type: "pie",
20452
20512
  labelRange: context.auxiliaryRange || undefined,
20453
- aggregated: false,
20513
+ aggregated: context.aggregated ?? false,
20454
20514
  };
20455
20515
  }
20456
20516
  getDefinition() {
@@ -20464,6 +20524,7 @@ stores.inject(MyMetaStore, storeInstance);
20464
20524
  auxiliaryRange: this.labelRange
20465
20525
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
20466
20526
  : undefined,
20527
+ aggregated: this.aggregated,
20467
20528
  };
20468
20529
  }
20469
20530
  getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
@@ -20655,7 +20716,7 @@ stores.inject(MyMetaStore, storeInstance);
20655
20716
  type: "scatter",
20656
20717
  verticalAxisPosition: "left",
20657
20718
  labelRange: context.auxiliaryRange || undefined,
20658
- aggregated: false,
20719
+ aggregated: context.aggregated ?? false,
20659
20720
  };
20660
20721
  }
20661
20722
  getDefinition() {
@@ -20685,6 +20746,7 @@ stores.inject(MyMetaStore, storeInstance);
20685
20746
  auxiliaryRange: this.labelRange
20686
20747
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
20687
20748
  : undefined,
20749
+ aggregated: this.aggregated,
20688
20750
  };
20689
20751
  }
20690
20752
  updateRanges(applyChange) {
@@ -22352,6 +22414,50 @@ stores.inject(MyMetaStore, storeInstance);
22352
22414
  return isMacOS() ? ev.metaKey : ev.ctrlKey;
22353
22415
  }
22354
22416
 
22417
+ /**
22418
+ * Repeatedly calls a callback function with a time delay between calls.
22419
+ */
22420
+ function useInterval(callback, delay) {
22421
+ let intervalId;
22422
+ const { setInterval, clearInterval } = window;
22423
+ owl.useEffect(() => {
22424
+ intervalId = setInterval(callback, delay);
22425
+ return () => clearInterval(intervalId);
22426
+ }, () => [delay]);
22427
+ return {
22428
+ pause: () => {
22429
+ clearInterval(intervalId);
22430
+ intervalId = undefined;
22431
+ },
22432
+ resume: () => {
22433
+ if (intervalId === undefined) {
22434
+ intervalId = setInterval(callback, delay);
22435
+ }
22436
+ },
22437
+ };
22438
+ }
22439
+ /**
22440
+ * Calls a callback function with a time delay
22441
+ */
22442
+ function useTimeOut() {
22443
+ let timeOutId;
22444
+ function clear() {
22445
+ if (timeOutId !== undefined) {
22446
+ clearTimeout(timeOutId);
22447
+ timeOutId = undefined;
22448
+ }
22449
+ }
22450
+ function schedule(callback, delay) {
22451
+ clear();
22452
+ timeOutId = setTimeout(callback, delay);
22453
+ }
22454
+ owl.onWillUnmount(clear);
22455
+ return {
22456
+ clear,
22457
+ schedule,
22458
+ };
22459
+ }
22460
+
22355
22461
  //------------------------------------------------------------------------------
22356
22462
  // Context Menu Component
22357
22463
  //------------------------------------------------------------------------------
@@ -22402,6 +22508,7 @@ stores.inject(MyMetaStore, storeInstance);
22402
22508
  }
22403
22509
  }
22404
22510
  `;
22511
+ const TIMEOUT_DELAY = 250;
22405
22512
  class Menu extends owl.Component {
22406
22513
  static template = "o-spreadsheet-Menu";
22407
22514
  static props = {
@@ -22412,6 +22519,7 @@ stores.inject(MyMetaStore, storeInstance);
22412
22519
  onClose: Function,
22413
22520
  onMenuClicked: { type: Function, optional: true },
22414
22521
  menuId: { type: String, optional: true },
22522
+ onMouseOver: { type: Function, optional: true },
22415
22523
  };
22416
22524
  static components = { Menu, Popover };
22417
22525
  static defaultProps = {
@@ -22422,10 +22530,12 @@ stores.inject(MyMetaStore, storeInstance);
22422
22530
  position: null,
22423
22531
  scrollOffset: 0,
22424
22532
  menuItems: [],
22533
+ isHoveringChild: false,
22425
22534
  });
22426
22535
  menuRef = owl.useRef("menu");
22427
22536
  hoveredMenu = undefined;
22428
22537
  position = useAbsoluteBoundingRect(this.menuRef);
22538
+ openingTimeOut = useTimeOut();
22429
22539
  setup() {
22430
22540
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
22431
22541
  owl.useExternalListener(window, "contextmenu", this.onExternalClick, { capture: true });
@@ -22525,6 +22635,9 @@ stores.inject(MyMetaStore, storeInstance);
22525
22635
  }
22526
22636
  return false;
22527
22637
  }
22638
+ isActive(menuItem) {
22639
+ return (this.subMenu?.isHoveringChild || false) && this.isParentMenu(this.subMenu, menuItem);
22640
+ }
22528
22641
  onScroll(ev) {
22529
22642
  this.subMenu.scrollOffset = ev.target.scrollTop;
22530
22643
  }
@@ -22532,10 +22645,10 @@ stores.inject(MyMetaStore, storeInstance);
22532
22645
  * If the given menu is not disabled, open it's submenu at the
22533
22646
  * correct position according to available surrounding space.
22534
22647
  */
22535
- openSubMenu(menu, ev) {
22536
- const parentMenuEl = ev.currentTarget;
22537
- if (!parentMenuEl)
22648
+ openSubMenu(menu, parentMenuEl) {
22649
+ if (!parentMenuEl) {
22538
22650
  return;
22651
+ }
22539
22652
  const y = parentMenuEl.getBoundingClientRect().top;
22540
22653
  this.subMenu.position = {
22541
22654
  x: this.position.x + this.props.depth * MENU_WIDTH,
@@ -22549,32 +22662,50 @@ stores.inject(MyMetaStore, storeInstance);
22549
22662
  return subMenu.parentMenu?.id === menuItem.id;
22550
22663
  }
22551
22664
  closeSubMenu() {
22665
+ if (this.subMenu.isHoveringChild) {
22666
+ return;
22667
+ }
22552
22668
  this.subMenu.isOpen = false;
22553
22669
  this.subMenu.parentMenu = undefined;
22554
22670
  }
22555
22671
  onClickMenu(menu, ev) {
22556
22672
  if (this.isEnabled(menu)) {
22557
22673
  if (this.isRoot(menu)) {
22558
- this.openSubMenu(menu, ev);
22674
+ this.openSubMenu(menu, ev.currentTarget);
22559
22675
  }
22560
22676
  else {
22561
22677
  this.activateMenu(menu);
22562
22678
  }
22563
22679
  }
22564
22680
  }
22565
- onMouseEnter(menu, ev) {
22566
- this.hoveredMenu = menu;
22567
- menu.onStartHover?.(this.env);
22681
+ onMouseOver(menu, ev) {
22568
22682
  if (this.isEnabled(menu)) {
22569
- if (this.isRoot(menu)) {
22570
- this.openSubMenu(menu, ev);
22683
+ if (this.isParentMenu(this.subMenu, menu)) {
22684
+ this.openingTimeOut.clear();
22685
+ return;
22571
22686
  }
22572
- else {
22573
- this.closeSubMenu();
22687
+ const currentTarget = ev.currentTarget;
22688
+ if (this.isRoot(menu)) {
22689
+ this.openingTimeOut.schedule(() => {
22690
+ this.openSubMenu(menu, currentTarget);
22691
+ }, TIMEOUT_DELAY);
22574
22692
  }
22575
22693
  }
22576
22694
  }
22695
+ onMouseOverMainMenu() {
22696
+ this.props.onMouseOver?.();
22697
+ this.subMenu.isHoveringChild = false;
22698
+ }
22699
+ onMouseOverChildMenu() {
22700
+ this.subMenu.isHoveringChild = true;
22701
+ this.openingTimeOut.clear();
22702
+ }
22703
+ onMouseEnter(menu, ev) {
22704
+ this.hoveredMenu = menu;
22705
+ menu.onStartHover?.(this.env);
22706
+ }
22577
22707
  onMouseLeave(menu) {
22708
+ this.openingTimeOut.schedule(this.closeSubMenu.bind(this), TIMEOUT_DELAY);
22578
22709
  this.hoveredMenu = undefined;
22579
22710
  menu.onStopHover?.(this.env);
22580
22711
  }
@@ -22826,7 +22957,8 @@ stores.inject(MyMetaStore, storeInstance);
22826
22957
  */
22827
22958
  function getSmartChartDefinition(zone, getters) {
22828
22959
  let dataSetZone = zone;
22829
- if (zone.left !== zone.right) {
22960
+ const singleColumn = zoneToDimension(zone).numberOfCols === 1;
22961
+ if (!singleColumn) {
22830
22962
  dataSetZone = { ...zone, left: zone.left + 1 };
22831
22963
  }
22832
22964
  const dataSets = [zoneToXc(dataSetZone)];
@@ -22860,7 +22992,7 @@ stores.inject(MyMetaStore, storeInstance);
22860
22992
  }
22861
22993
  }
22862
22994
  let labelRangeXc;
22863
- if (zone.left !== zone.right) {
22995
+ if (!singleColumn) {
22864
22996
  labelRangeXc = zoneToXc({
22865
22997
  ...zone,
22866
22998
  right: zone.left,
@@ -22884,6 +23016,19 @@ stores.inject(MyMetaStore, storeInstance);
22884
23016
  legendPosition: newLegendPos,
22885
23017
  };
22886
23018
  }
23019
+ const _dataSets = createDataSets(getters, dataSets, sheetId, dataSetsHaveTitle);
23020
+ if (singleColumn &&
23021
+ getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
23022
+ return {
23023
+ title: "",
23024
+ dataSets,
23025
+ aggregated: true,
23026
+ labelRange: dataSets[0],
23027
+ type: "pie",
23028
+ legendPosition: "top",
23029
+ dataSetsHaveTitle: false,
23030
+ };
23031
+ }
22887
23032
  return {
22888
23033
  title,
22889
23034
  dataSets,
@@ -22911,6 +23056,7 @@ stores.inject(MyMetaStore, storeInstance);
22911
23056
  numberOfHeaders: 1,
22912
23057
  bandedRows: true,
22913
23058
  bandedColumns: false,
23059
+ automaticAutofill: true,
22914
23060
  styleId: "TableStyleMedium2",
22915
23061
  };
22916
23062
  function generateColorSet(name, highlightColor) {
@@ -26394,7 +26540,7 @@ stores.inject(MyMetaStore, storeInstance);
26394
26540
  {
26395
26541
  name: "aggregated",
26396
26542
  label: _t("Aggregate"),
26397
- value: this.props.definition.aggregated,
26543
+ value: this.props.definition.aggregated ?? false,
26398
26544
  onChange: this.onUpdateAggregated.bind(this),
26399
26545
  },
26400
26546
  ];
@@ -27565,15 +27711,15 @@ stores.inject(MyMetaStore, storeInstance);
27565
27711
  // -----------------------------------------------------------------------------
27566
27712
  // We need here the svg of the icons that we need to convert to images for the renderer
27567
27713
  // -----------------------------------------------------------------------------
27568
- const ARROW_DOWN = '<svg class="o-cf-icon arrow-down" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#DC6965" d="M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z"></path></svg>';
27569
- const ARROW_UP = '<svg class="o-cf-icon arrow-up" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#00A04A" d="M34.9 289.5l-22.2-22.2c-9.4-9.4-9.4-24.6 0-33.9L207 39c9.4-9.4 24.6-9.4 33.9 0l194.3 194.3c9.4 9.4 9.4 24.6 0 33.9L413 289.4c-9.5 9.5-25 9.3-34.3-.4L264 168.6V456c0 13.3-10.7 24-24 24h-32c-13.3 0-24-10.7-24-24V168.6L69.2 289.1c-9.3 9.8-24.8 10-34.3.4z"></path></svg>';
27714
+ const ARROW_DOWN = '<svg class="o-cf-icon arrow-down" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#E06666" d="M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z"></path></svg>';
27715
+ const ARROW_UP = '<svg class="o-cf-icon arrow-up" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#6AA84F" d="M34.9 289.5l-22.2-22.2c-9.4-9.4-9.4-24.6 0-33.9L207 39c9.4-9.4 24.6-9.4 33.9 0l194.3 194.3c9.4 9.4 9.4 24.6 0 33.9L413 289.4c-9.5 9.5-25 9.3-34.3-.4L264 168.6V456c0 13.3-10.7 24-24 24h-32c-13.3 0-24-10.7-24-24V168.6L69.2 289.1c-9.3 9.8-24.8 10-34.3.4z"></path></svg>';
27570
27716
  const ARROW_RIGHT = '<svg class="o-cf-icon arrow-right" width="10" height="10" focusable="false" viewBox="0 0 448 512"><path fill="#F0AD4E" d="M190.5 66.9l22.2-22.2c9.4-9.4 24.6-9.4 33.9 0L441 239c9.4 9.4 9.4 24.6 0 33.9L246.6 467.3c-9.4 9.4-24.6 9.4-33.9 0l-22.2-22.2c-9.5-9.5-9.3-25 .4-34.3L311.4 296H24c-13.3 0-24-10.7-24-24v-32c0-13.3 10.7-24 24-24h287.4L190.9 101.2c-9.8-9.3-10-24.8-.4-34.3z"></path></svg>';
27571
- const SMILE = '<svg class="o-cf-icon smile" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#00A04A" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z"></path></svg>';
27717
+ const SMILE = '<svg class="o-cf-icon smile" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#6AA84F" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm4 72.6c-20.8 25-51.5 39.4-84 39.4s-63.2-14.3-84-39.4c-8.5-10.2-23.7-11.5-33.8-3.1-10.2 8.5-11.5 23.6-3.1 33.8 30 36 74.1 56.6 120.9 56.6s90.9-20.6 120.9-56.6c8.5-10.2 7.1-25.3-3.1-33.8-10.1-8.4-25.3-7.1-33.8 3.1z"></path></svg>';
27572
27718
  const MEH = '<svg class="o-cf-icon meh" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#F0AD4E" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm8 144H160c-13.2 0-24 10.8-24 24s10.8 24 24 24h176c13.2 0 24-10.8 24-24s-10.8-24-24-24z"></path></svg>';
27573
- const FROWN = '<svg class="o-cf-icon frown" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#DC6965" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm-80 128c-40.2 0-78 17.7-103.8 48.6-8.5 10.2-7.1 25.3 3.1 33.8 10.2 8.4 25.3 7.1 33.8-3.1 16.6-19.9 41-31.4 66.9-31.4s50.3 11.4 66.9 31.4c8.1 9.7 23.1 11.9 33.8 3.1 10.2-8.5 11.5-23.6 3.1-33.8C326 321.7 288.2 304 248 304z"></path></svg>';
27574
- const GREEN_DOT = '<svg class="o-cf-icon green-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#00A04A" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
27719
+ const FROWN = '<svg class="o-cf-icon frown" width="10" height="10" focusable="false" viewBox="0 0 496 512"><path fill="#E06666" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm0 448c-110.3 0-200-89.7-200-200S137.7 56 248 56s200 89.7 200 200-89.7 200-200 200zm-80-216c17.7 0 32-14.3 32-32s-14.3-32-32-32-32 14.3-32 32 14.3 32 32 32zm160-64c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm-80 128c-40.2 0-78 17.7-103.8 48.6-8.5 10.2-7.1 25.3 3.1 33.8 10.2 8.4 25.3 7.1 33.8-3.1 16.6-19.9 41-31.4 66.9-31.4s50.3 11.4 66.9 31.4c8.1 9.7 23.1 11.9 33.8 3.1 10.2-8.5 11.5-23.6 3.1-33.8C326 321.7 288.2 304 248 304z"></path></svg>';
27720
+ const GREEN_DOT = '<svg class="o-cf-icon green-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#6AA84F" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
27575
27721
  const YELLOW_DOT = '<svg class="o-cf-icon yellow-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#F0AD4E" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
27576
- const RED_DOT = '<svg class="o-cf-icon red-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#DC6965" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
27722
+ const RED_DOT = '<svg class="o-cf-icon red-dot" width="10" height="10" focusable="false" viewBox="0 0 512 512"><path fill="#E06666" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z"></path></svg>';
27577
27723
  function loadIconImage(svg) {
27578
27724
  /** We have to add xmlns, as it's not added by owl in the canvas */
27579
27725
  svg = `<svg xmlns="http://www.w3.org/2000/svg" ${svg.slice(4)}`;
@@ -29439,8 +29585,7 @@ stores.inject(MyMetaStore, storeInstance);
29439
29585
  }
29440
29586
  }
29441
29587
 
29442
- const BORDER_COLOR = "#8B008B";
29443
- const BACKGROUND_COLOR = "#8B008B33";
29588
+ const FIND_AND_REPLACE_HIGHLIGHT_COLOR = "#8B008B";
29444
29589
  var Direction;
29445
29590
  (function (Direction) {
29446
29591
  Direction[Direction["previous"] = -1] = "previous";
@@ -29471,14 +29616,14 @@ stores.inject(MyMetaStore, storeInstance);
29471
29616
  super(get);
29472
29617
  this.initialShowFormulaState = this.model.getters.shouldShowFormulas();
29473
29618
  this.searchOptions.searchFormulas = this.initialShowFormulaState;
29619
+ const highlightStore = get(HighlightStore);
29620
+ highlightStore.register(owl.toRaw(this));
29474
29621
  this.onDispose(() => {
29475
29622
  this.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.initialShowFormulaState });
29476
29623
  this.updateSearchContent.stopDebounce();
29624
+ highlightStore.unRegister(owl.toRaw(this));
29477
29625
  });
29478
29626
  }
29479
- get renderingLayers() {
29480
- return ["Search"];
29481
- }
29482
29627
  get searchMatches() {
29483
29628
  switch (this.searchOptions.searchScope) {
29484
29629
  case "allSheets":
@@ -29705,8 +29850,8 @@ stores.inject(MyMetaStore, storeInstance);
29705
29850
  // ---------------------------------------------------------------------------
29706
29851
  // Grid rendering
29707
29852
  // ---------------------------------------------------------------------------
29708
- draw(renderingContext) {
29709
- const { ctx } = renderingContext;
29853
+ get highlights() {
29854
+ const highlights = [];
29710
29855
  const sheetId = this.getters.getActiveSheetId();
29711
29856
  for (const [index, match] of this.searchMatches.entries()) {
29712
29857
  if (match.sheetId !== sheetId) {
@@ -29714,26 +29859,31 @@ stores.inject(MyMetaStore, storeInstance);
29714
29859
  }
29715
29860
  const zone = positionToZone(match);
29716
29861
  const zoneWithMerge = this.getters.expandZone(sheetId, zone);
29717
- const { x, y, width, height } = this.getters.getVisibleRect(zoneWithMerge);
29862
+ const { width, height } = this.getters.getVisibleRect(zoneWithMerge);
29718
29863
  if (width > 0 && height > 0) {
29719
- ctx.fillStyle = BACKGROUND_COLOR;
29720
- ctx.fillRect(x, y, width, height);
29721
- if (index === this.selectedMatchIndex) {
29722
- ctx.strokeStyle = BORDER_COLOR;
29723
- ctx.strokeRect(x, y, width, height);
29724
- }
29864
+ highlights.push({
29865
+ sheetId,
29866
+ zone: zoneWithMerge,
29867
+ color: FIND_AND_REPLACE_HIGHLIGHT_COLOR,
29868
+ noBorder: index !== this.selectedMatchIndex,
29869
+ thinLine: true,
29870
+ fillAlpha: 0.2,
29871
+ });
29725
29872
  }
29726
29873
  }
29727
- // TODO: use Highlights helpers/stores when the Highlight PR is merged
29728
29874
  if (this.searchOptions.searchScope === "specificRange") {
29729
29875
  const range = this.searchOptions.specificRange;
29730
- if (!range || range.sheetId !== sheetId) {
29731
- return;
29876
+ if (range && range.sheetId === sheetId) {
29877
+ highlights.push({
29878
+ sheetId,
29879
+ zone: range.zone,
29880
+ color: FIND_AND_REPLACE_HIGHLIGHT_COLOR,
29881
+ noFill: true,
29882
+ thinLine: true,
29883
+ });
29732
29884
  }
29733
- const { x, y, width, height } = this.getters.getVisibleRect(range.zone);
29734
- ctx.strokeStyle = BORDER_COLOR;
29735
- ctx.strokeRect(x, y, width, height);
29736
29885
  }
29886
+ return highlights;
29737
29887
  }
29738
29888
  }
29739
29889
 
@@ -30147,6 +30297,12 @@ stores.inject(MyMetaStore, storeInstance);
30147
30297
  "headerRow",
30148
30298
  "totalRow",
30149
30299
  ];
30300
+ /** Return the content zone of the table, ie. the table zone without the headers */
30301
+ function getTableContentZone(tableZone, tableConfig) {
30302
+ const numberOfHeaders = tableConfig.numberOfHeaders;
30303
+ const contentZone = { ...tableZone, top: tableZone.top + numberOfHeaders };
30304
+ return contentZone.top <= contentZone.bottom ? contentZone : undefined;
30305
+ }
30150
30306
  function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
30151
30307
  return {
30152
30308
  borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
@@ -30598,14 +30754,6 @@ stores.inject(MyMetaStore, storeInstance);
30598
30754
  tableXc: this.env.model.getters.getRangeString(this.props.table.range, sheetId),
30599
30755
  filtersEnabledIfPossible: this.props.table.config.hasFilters,
30600
30756
  });
30601
- owl.onWillUpdateProps((nextProps) => {
30602
- if (this.props.table.id !== nextProps.table.id) {
30603
- const sheetId = this.env.model.getters.getActiveSheetId();
30604
- this.state.tableXc = this.env.model.getters.getRangeString(nextProps.table.range, sheetId);
30605
- this.state.tableZoneErrors = [];
30606
- this.state.filtersEnabledIfPossible = nextProps.table.config.hasFilters;
30607
- }
30608
- });
30609
30757
  }
30610
30758
  updateHasFilters(hasFilters) {
30611
30759
  this.state.filtersEnabledIfPossible = hasFilters;
@@ -30754,7 +30902,11 @@ stores.inject(MyMetaStore, storeInstance);
30754
30902
  if (!table) {
30755
30903
  return { isOpen: false };
30756
30904
  }
30757
- return { isOpen: true, props: { table } };
30905
+ return {
30906
+ isOpen: true,
30907
+ props: { table },
30908
+ key: table.id,
30909
+ };
30758
30910
  },
30759
30911
  });
30760
30912
 
@@ -32469,19 +32621,21 @@ stores.inject(MyMetaStore, storeInstance);
32469
32621
  slots: Object,
32470
32622
  };
32471
32623
  get iconStyle() {
32472
- const x = this.getIconHorizontalPosition();
32473
- const y = this.getIconVerticalPosition();
32624
+ const cellPosition = this.props.cellPosition;
32625
+ const merge = this.env.model.getters.getMerge(cellPosition);
32626
+ const zone = merge || positionToZone(cellPosition);
32627
+ const rect = this.env.model.getters.getVisibleRectWithoutHeaders(zone);
32628
+ const x = this.getIconHorizontalPosition(rect, cellPosition);
32629
+ const y = this.getIconVerticalPosition(rect, cellPosition);
32474
32630
  return cssPropertiesToCss({
32475
32631
  top: `${y + (this.props.offset?.y || 0)}px`,
32476
32632
  left: `${x + (this.props.offset?.x || 0)}px`,
32477
32633
  });
32478
32634
  }
32479
- getIconVerticalPosition() {
32480
- const { sheetId, row } = this.props.cellPosition;
32481
- const merge = this.env.model.getters.getMerge(this.props.cellPosition);
32482
- const start = this.env.model.getters.getRowDimensionsInViewport(sheetId, row).start;
32483
- const end = this.env.model.getters.getRowDimensionsInViewport(sheetId, merge ? merge.bottom : row).end;
32484
- const cell = this.env.model.getters.getCell(this.props.cellPosition);
32635
+ getIconVerticalPosition(rect, cellPosition) {
32636
+ const start = rect.y;
32637
+ const end = rect.y + rect.height;
32638
+ const cell = this.env.model.getters.getCell(cellPosition);
32485
32639
  const align = this.props.verticalAlign || cell?.style?.verticalAlign || DEFAULT_VERTICAL_ALIGN;
32486
32640
  switch (align) {
32487
32641
  case "bottom":
@@ -32493,13 +32647,11 @@ stores.inject(MyMetaStore, storeInstance);
32493
32647
  return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
32494
32648
  }
32495
32649
  }
32496
- getIconHorizontalPosition() {
32497
- const { sheetId, col } = this.props.cellPosition;
32498
- const merge = this.env.model.getters.getMerge(this.props.cellPosition);
32499
- const start = this.env.model.getters.getColDimensionsInViewport(sheetId, col).start;
32500
- const end = this.env.model.getters.getColDimensionsInViewport(sheetId, merge ? merge.right : col).end;
32501
- const cell = this.env.model.getters.getCell(this.props.cellPosition);
32502
- const evaluatedCell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
32650
+ getIconHorizontalPosition(rect, cellPosition) {
32651
+ const start = rect.x;
32652
+ const end = rect.x + rect.width;
32653
+ const cell = this.env.model.getters.getCell(cellPosition);
32654
+ const evaluatedCell = this.env.model.getters.getEvaluatedCell(cellPosition);
32503
32655
  const align = this.props.horizontalAlign || cell?.style?.align || evaluatedCell.defaultAlign;
32504
32656
  switch (align) {
32505
32657
  case "right":
@@ -32604,6 +32756,8 @@ stores.inject(MyMetaStore, storeInstance);
32604
32756
  height: ${CHECKBOX_WIDTH}px;
32605
32757
  accent-color: #808080;
32606
32758
  margin: ${MARGIN}px;
32759
+ /** required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
32760
+ position: absolute;
32607
32761
  }
32608
32762
  `;
32609
32763
  class DataValidationCheckbox extends owl.Component {
@@ -32622,7 +32776,7 @@ stores.inject(MyMetaStore, storeInstance);
32622
32776
  }
32623
32777
  get isDisabled() {
32624
32778
  const cell = this.env.model.getters.getCell(this.props.cellPosition);
32625
- return !!cell?.isFormula;
32779
+ return this.env.model.getters.isReadonly() || !!cell?.isFormula;
32626
32780
  }
32627
32781
  }
32628
32782
 
@@ -32662,12 +32816,17 @@ stores.inject(MyMetaStore, storeInstance);
32662
32816
  static props = {};
32663
32817
  static components = { GridCellIcon, DataValidationCheckbox, DataValidationListIcon };
32664
32818
  get checkBoxCellPositions() {
32665
- return this.env.model.getters.getDataValidationCheckBoxCellPositions();
32819
+ return this.env.model.getters
32820
+ .getVisibleCellPositions()
32821
+ .filter(this.env.model.getters.isCellValidCheckbox);
32666
32822
  }
32667
32823
  get listIconsCellPositions() {
32668
- return this.env.model.getters.isReadonly()
32669
- ? []
32670
- : this.env.model.getters.getDataValidationListCellsPositions();
32824
+ if (this.env.model.getters.isReadonly()) {
32825
+ return [];
32826
+ }
32827
+ return this.env.model.getters
32828
+ .getVisibleCellPositions()
32829
+ .filter(this.env.model.getters.cellHasListDataValidationIcon);
32671
32830
  }
32672
32831
  }
32673
32832
 
@@ -32712,7 +32871,7 @@ stores.inject(MyMetaStore, storeInstance);
32712
32871
  const newY = clip(initialFigure.y + deltaY, minY, maxY - initialFigure.height - scrollY);
32713
32872
  return { ...initialFigure, x: newX, y: newY };
32714
32873
  }
32715
- function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize) {
32874
+ function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize, { scrollX, scrollY }) {
32716
32875
  let { x, y, width, height } = initialFigure;
32717
32876
  if (keepRatio && dirX != 0 && dirY != 0) {
32718
32877
  const deltaX = Math.min(dirX * (mouseInitialX - mouseX), initialFigure.width - minFigSize);
@@ -32739,14 +32898,14 @@ stores.inject(MyMetaStore, storeInstance);
32739
32898
  y = initialFigure.y - deltaY;
32740
32899
  }
32741
32900
  }
32742
- // Restrict resizing if x or y reaches header boundaries
32743
- if (x < 0) {
32744
- width += x;
32745
- x = 0;
32901
+ // Adjusts figure dimensions to ensure it remains within header boundaries and viewport during resizing.
32902
+ if (x + scrollX <= 0) {
32903
+ width = width + x + scrollX;
32904
+ x = -scrollX;
32746
32905
  }
32747
- if (y < 0) {
32748
- height += y;
32749
- y = 0;
32906
+ if (y + scrollY <= 0) {
32907
+ height = height + y + scrollY;
32908
+ y = -scrollY;
32750
32909
  }
32751
32910
  return { ...initialFigure, x, y, width, height };
32752
32911
  }
@@ -33166,7 +33325,7 @@ stores.inject(MyMetaStore, storeInstance);
33166
33325
  const minFigSize = figureRegistry.get(figure.tag).minFigSize || MIN_FIG_SIZE;
33167
33326
  const onMouseMove = (ev) => {
33168
33327
  const currentMousePosition = { x: ev.clientX, y: ev.clientY };
33169
- const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize);
33328
+ const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize, this.env.model.getters.getActiveSheetScrollInfo());
33170
33329
  const otherFigures = this.getOtherFigures(figure.id);
33171
33330
  const snapResult = snapForResize(this.env.model.getters, dirX, dirY, draggedFigure, otherFigures);
33172
33331
  this.dnd.draggedFigure = snapResult.snappedFigure;
@@ -33342,29 +33501,6 @@ stores.inject(MyMetaStore, storeInstance);
33342
33501
  }
33343
33502
  }
33344
33503
 
33345
- /**
33346
- * Repeatedly calls a callback function with a time delay between calls.
33347
- */
33348
- function useInterval(callback, delay) {
33349
- let intervalId;
33350
- const { setInterval, clearInterval } = window;
33351
- owl.useEffect(() => {
33352
- intervalId = setInterval(callback, delay);
33353
- return () => clearInterval(intervalId);
33354
- }, () => [delay]);
33355
- return {
33356
- pause: () => {
33357
- clearInterval(intervalId);
33358
- intervalId = undefined;
33359
- },
33360
- resume: () => {
33361
- if (intervalId === undefined) {
33362
- intervalId = setInterval(callback, delay);
33363
- }
33364
- },
33365
- };
33366
- }
33367
-
33368
33504
  const CURSOR_SVG = /*xml*/ `
33369
33505
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="16"><path d="M6.5.4c1.3-.8 2.9-.1 3.8 1.4l2.9 5.1c.2.4.9 1.6-.4 2.3l-1.6.9 1.8 3.1c.2.4.1 1-.2 1.2l-1.6 1c-.3.1-.9 0-1.1-.4l-1.8-3.1-1.6 1c-.6.4-1.7 0-2.2-.8L0 4.3"/><path fill="#fff" d="M9.1 2a1.4 1.1 60 0 0-1.7-.6L5.5 2.5l.9 1.6-1 .6-.9-1.6-.6.4 1.8 3.1-1.3.7-1.8-3.1-1 .6 3.8 6.6 6.8-3.98M3.9 8.8 10.82 5l.795 1.4-6.81 3.96"/></svg>
33370
33506
  `;
@@ -35249,10 +35385,17 @@ stores.inject(MyMetaStore, storeInstance);
35249
35385
  get panelProps() {
35250
35386
  const state = this.computeState(this.componentTag, this.initialPanelProps);
35251
35387
  if (state.isOpen) {
35252
- return state.props;
35388
+ return state.props ?? {};
35253
35389
  }
35254
35390
  return {};
35255
35391
  }
35392
+ get panelKey() {
35393
+ const state = this.computeState(this.componentTag, this.initialPanelProps);
35394
+ if (state.isOpen) {
35395
+ return state.key;
35396
+ }
35397
+ return undefined;
35398
+ }
35256
35399
  open(componentTag, panelProps = {}) {
35257
35400
  const state = this.computeState(componentTag, panelProps);
35258
35401
  if (state.isOpen === false) {
@@ -35262,7 +35405,7 @@ stores.inject(MyMetaStore, storeInstance);
35262
35405
  this.initialPanelProps?.onCloseSidePanel?.();
35263
35406
  }
35264
35407
  this.componentTag = componentTag;
35265
- this.initialPanelProps = state.props;
35408
+ this.initialPanelProps = state.props ?? {};
35266
35409
  }
35267
35410
  toggle(componentTag, panelProps) {
35268
35411
  if (this.isOpen && componentTag === this.componentTag) {
@@ -40739,10 +40882,8 @@ stores.inject(MyMetaStore, storeInstance);
40739
40882
  "ref", // a function to access a certain dependency at a given index
40740
40883
  "range", // same as above, but guarantee that the result is in the form of a range
40741
40884
  "ctx", code.toString());
40742
- functionCache[cacheKey] = {
40743
- // @ts-ignore
40744
- execute: baseFunction,
40745
- };
40885
+ // @ts-ignore
40886
+ functionCache[cacheKey] = baseFunction;
40746
40887
  /**
40747
40888
  * This function compile the function arguments. It is mostly straightforward,
40748
40889
  * except that there is a non trivial transformation in one situation:
@@ -40849,7 +40990,7 @@ stores.inject(MyMetaStore, storeInstance);
40849
40990
  }
40850
40991
  }
40851
40992
  const compiledFormula = {
40852
- execute: functionCache[cacheKey].execute,
40993
+ execute: functionCache[cacheKey],
40853
40994
  dependencies,
40854
40995
  constantValues,
40855
40996
  tokens,
@@ -42334,6 +42475,15 @@ stores.inject(MyMetaStore, storeInstance);
42334
42475
  return "Success" /* CommandResult.Success */;
42335
42476
  }
42336
42477
  }
42478
+ beforeHandle(cmd) {
42479
+ switch (cmd.type) {
42480
+ case "DELETE_SHEET":
42481
+ this.getters.getFigures(cmd.sheetId).forEach((figure) => {
42482
+ this.dispatch("DELETE_FIGURE", { id: figure.id, sheetId: cmd.sheetId });
42483
+ });
42484
+ break;
42485
+ }
42486
+ }
42337
42487
  handle(cmd) {
42338
42488
  switch (cmd.type) {
42339
42489
  case "CREATE_SHEET":
@@ -43820,7 +43970,6 @@ stores.inject(MyMetaStore, storeInstance);
43820
43970
  "getNumberHeaders",
43821
43971
  "getGridLinesVisibility",
43822
43972
  "getNextSheetName",
43823
- "isEmpty",
43824
43973
  "getSheetSize",
43825
43974
  "getSheetZone",
43826
43975
  "getPaneDivisions",
@@ -44192,14 +44341,6 @@ stores.inject(MyMetaStore, storeInstance);
44192
44341
  // ---------------------------------------------------------------------------
44193
44342
  // Row/Col manipulation
44194
44343
  // ---------------------------------------------------------------------------
44195
- /**
44196
- * Check if a zone only contains empty cells
44197
- */
44198
- isEmpty(sheetId, zone) {
44199
- return positions(zone)
44200
- .map(({ col, row }) => this.getCell({ sheetId, col, row }))
44201
- .every((cell) => !cell || cell.content === "");
44202
- }
44203
44344
  getCommandZones(cmd) {
44204
44345
  const zones = [];
44205
44346
  if ("zone" in cmd) {
@@ -45003,13 +45144,15 @@ stores.inject(MyMetaStore, storeInstance);
45003
45144
  if (zone.left !== zone.right) {
45004
45145
  throw new Error("Can only define a filter on a single column");
45005
45146
  }
45006
- const filteredZone = { ...zone, top: zone.top + config.numberOfHeaders };
45007
- const filteredRange = this.getters.getRangeFromZone(range.sheetId, filteredZone);
45147
+ const contentZone = getTableContentZone(zone, config);
45148
+ const filteredRange = contentZone
45149
+ ? this.getters.getRangeFromZone(range.sheetId, contentZone)
45150
+ : undefined;
45008
45151
  return {
45009
45152
  id,
45010
45153
  rangeWithHeaders: range,
45011
45154
  col: zone.left,
45012
- filteredRange: filteredZone.top > filteredZone.bottom ? undefined : filteredRange,
45155
+ filteredRange,
45013
45156
  };
45014
45157
  }
45015
45158
  copyTableForSheet(sheetId, table) {
@@ -45618,7 +45761,11 @@ stores.inject(MyMetaStore, storeInstance);
45618
45761
  });
45619
45762
  }
45620
45763
  getParameters() {
45621
- return [this.refFn.bind(this), this.range.bind(this), this.evalContext];
45764
+ return {
45765
+ referenceDenormalizer: this.refFn.bind(this),
45766
+ ensureRange: this.range.bind(this),
45767
+ evalContext: this.evalContext,
45768
+ };
45622
45769
  }
45623
45770
  /**
45624
45771
  * Returns the value of the cell(s) used in reference
@@ -45699,6 +45846,41 @@ stores.inject(MyMetaStore, storeInstance);
45699
45846
  }
45700
45847
  }
45701
45848
 
45849
+ class PositionMap {
45850
+ map = {};
45851
+ set({ sheetId, col, row }, value) {
45852
+ const map = this.map;
45853
+ if (!map[sheetId]) {
45854
+ map[sheetId] = {};
45855
+ }
45856
+ if (!map[sheetId][col]) {
45857
+ map[sheetId][col] = {};
45858
+ }
45859
+ map[sheetId][col][row] = value;
45860
+ }
45861
+ get({ sheetId, col, row }) {
45862
+ return this.map[sheetId]?.[col]?.[row];
45863
+ }
45864
+ has({ sheetId, col, row }) {
45865
+ return this.map[sheetId]?.[col]?.[row] !== undefined;
45866
+ }
45867
+ delete({ sheetId, col, row }) {
45868
+ delete this.map[sheetId]?.[col]?.[row];
45869
+ }
45870
+ keys() {
45871
+ const map = this.map;
45872
+ const keys = [];
45873
+ for (const sheetId in map) {
45874
+ for (const col in map[sheetId]) {
45875
+ for (const row in map[sheetId][col]) {
45876
+ keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
45877
+ }
45878
+ }
45879
+ }
45880
+ return keys;
45881
+ }
45882
+ }
45883
+
45702
45884
  function quickselect(arr, k, left, right, compare) {
45703
45885
  quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
45704
45886
  }
@@ -46425,26 +46607,26 @@ stores.inject(MyMetaStore, storeInstance);
46425
46607
  * It uses an R-Tree data structure to efficiently find dependent cells.
46426
46608
  */
46427
46609
  class FormulaDependencyGraph {
46428
- encoder;
46429
- dependencies = new Map();
46610
+ createEmptyPositionSet;
46611
+ dependencies = new PositionMap();
46430
46612
  rTree;
46431
- constructor(encoder, data = []) {
46432
- this.encoder = encoder;
46613
+ constructor(createEmptyPositionSet, data = []) {
46614
+ this.createEmptyPositionSet = createEmptyPositionSet;
46433
46615
  this.rTree = new SpreadsheetRTree(data);
46434
46616
  }
46435
- removeAllDependencies(formulaPositionId) {
46436
- const ranges = this.dependencies.get(formulaPositionId);
46617
+ removeAllDependencies(formulaPosition) {
46618
+ const ranges = this.dependencies.get(formulaPosition);
46437
46619
  if (!ranges) {
46438
46620
  return;
46439
46621
  }
46440
46622
  for (const range of ranges) {
46441
46623
  this.rTree.remove(range);
46442
46624
  }
46443
- this.dependencies.delete(formulaPositionId);
46625
+ this.dependencies.delete(formulaPosition);
46444
46626
  }
46445
- addDependencies(formulaPositionId, dependencies) {
46627
+ addDependencies(formulaPosition, dependencies) {
46446
46628
  const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
46447
- data: formulaPositionId,
46629
+ data: formulaPosition,
46448
46630
  boundingBox: {
46449
46631
  zone,
46450
46632
  sheetId,
@@ -46453,12 +46635,12 @@ stores.inject(MyMetaStore, storeInstance);
46453
46635
  for (const item of rTreeItems) {
46454
46636
  this.rTree.insert(item);
46455
46637
  }
46456
- const existingDependencies = this.dependencies.get(formulaPositionId);
46638
+ const existingDependencies = this.dependencies.get(formulaPosition);
46457
46639
  if (existingDependencies) {
46458
46640
  existingDependencies.push(...rTreeItems);
46459
46641
  }
46460
46642
  else {
46461
- this.dependencies.set(formulaPositionId, rTreeItems);
46643
+ this.dependencies.set(formulaPosition, rTreeItems);
46462
46644
  }
46463
46645
  }
46464
46646
  /**
@@ -46467,23 +46649,194 @@ stores.inject(MyMetaStore, storeInstance);
46467
46649
  * This is called a topological ordering (excluding cycles)
46468
46650
  */
46469
46651
  getCellsDependingOn(ranges) {
46470
- const visited = new JetSet();
46652
+ const visited = this.createEmptyPositionSet();
46471
46653
  const queue = Array.from(ranges).reverse();
46472
46654
  while (queue.length > 0) {
46473
46655
  const range = queue.pop();
46474
- visited.addMany(this.encoder.encodeBoundingBox(range));
46475
- const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
46476
- for (const positionId of impactedPositionIds) {
46477
- if (!visited.has(positionId)) {
46478
- queue.push(this.encoder.decodeToBoundingBox(positionId));
46656
+ visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
46657
+ const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
46658
+ for (const position of impactedPositions) {
46659
+ if (!visited.has(position)) {
46660
+ queue.push({ sheetId: position.sheetId, zone: positionToZone(position) });
46479
46661
  }
46480
46662
  }
46481
46663
  }
46482
- visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
46664
+ visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
46483
46665
  return visited;
46484
46666
  }
46485
46667
  }
46486
46668
 
46669
+ /**
46670
+ * Implements a fixed-sized grid or 2D matrix of bits.
46671
+ * based on https://github.com/zandaqo/structurae
46672
+ *
46673
+ * The grid is implemented as a 1D array of 32-bit integers, where each bit represents a cell in the grid.
46674
+ * It follows row-major order, with each row stored consecutively in 32-bit blocks.
46675
+ * Pads the number of columns to the next power of 2 to allow quick lookups with bitwise operations.
46676
+ *
46677
+ * Key terminology:
46678
+ * - bucket: Index of an item in the Uint32Array, a 32-bit integer.
46679
+ * - bitPosition: The position of a bit within the bucket 32-bit integer.
46680
+ */
46681
+ class BinaryGrid extends Uint32Array {
46682
+ columnOffset = 0;
46683
+ cols = 0;
46684
+ rows = 0;
46685
+ /**
46686
+ * Creates a binary grid of specified dimensions.
46687
+ */
46688
+ static create(rows, columns) {
46689
+ const columnOffset = log2Ceil(columns);
46690
+ const length = (rows << columnOffset) >> 5;
46691
+ const grid = new this(length + 1);
46692
+ grid.columnOffset = columnOffset;
46693
+ grid.cols = columns;
46694
+ grid.rows = rows;
46695
+ return grid;
46696
+ }
46697
+ /**
46698
+ * Returns the bit at given coordinates.
46699
+ */
46700
+ getValue(position) {
46701
+ const [bucket, bitPosition] = this.getCoordinates(position);
46702
+ return ((this[bucket] >> bitPosition) & 1);
46703
+ }
46704
+ /**
46705
+ * Sets the bit at given coordinates.
46706
+ */
46707
+ setValue(position, value) {
46708
+ const [bucket, bitPosition] = this.getCoordinates(position);
46709
+ const currentValue = (this[bucket] >> bitPosition) & 1;
46710
+ const hasBeenInserted = currentValue === 0 && value === 1;
46711
+ this[bucket] = (this[bucket] & ~(1 << bitPosition)) | (value << bitPosition);
46712
+ return hasBeenInserted;
46713
+ // Let's breakdown of the above line:
46714
+ // with an example with a 4-bit integer (instead of 32-bit).
46715
+ //
46716
+ // Let's say we want to set the bit at position 2 to 1 and the existing
46717
+ // bit sequence this[bucket] is 1001. The final bit sequence should be 1101.
46718
+ //
46719
+ // First, we clear the bit at position 2 by AND-ing this[bucket] with a
46720
+ // mask having all 1s except a 0 at the bit position (~ (1 << bitPosition)).
46721
+ // 1 << bitPosition is 0100 (shifting 0001 to the left by 2)
46722
+ // Inverting the bits with ~ gives the final mask ~(1 << bitPosition): 1011
46723
+ //
46724
+ // Then, we shift the value by the bit position (value << bitPosition: 0100)
46725
+ // and OR the result with the previous step's result:
46726
+ // (1001 & 1011) | 0100 = 1101
46727
+ }
46728
+ isEmpty() {
46729
+ return !this.some((bucket) => bucket !== 0);
46730
+ }
46731
+ fillAllPositions() {
46732
+ const thirtyTwoOnes = -1 >>> 0; // same as 2 ** 32 - 1, a 32-bit number with all bits set to 1
46733
+ this.fill(thirtyTwoOnes);
46734
+ }
46735
+ clear() {
46736
+ this.fill(0);
46737
+ }
46738
+ getCoordinates(position) {
46739
+ const { row, col } = position;
46740
+ const index = (row << this.columnOffset) + col;
46741
+ const bucket = index >> 5;
46742
+ return [bucket, index - (bucket << 5)];
46743
+ }
46744
+ }
46745
+ function log2Ceil(value) {
46746
+ // A faster version of Math.ceil(Math.log2(value)).
46747
+ if (value === 0) {
46748
+ return -Infinity;
46749
+ }
46750
+ else if (value < 0) {
46751
+ return NaN;
46752
+ }
46753
+ // --value handles the case where value is a power of 2
46754
+ return 32 - Math.clz32(--value);
46755
+ }
46756
+
46757
+ class PositionSet {
46758
+ sheets = {};
46759
+ /**
46760
+ * List of positions in the order they were inserted.
46761
+ */
46762
+ insertions = [];
46763
+ maxSize = 0;
46764
+ constructor(sheetSizes) {
46765
+ for (const sheetId in sheetSizes) {
46766
+ const cols = sheetSizes[sheetId].cols;
46767
+ const rows = sheetSizes[sheetId].rows;
46768
+ this.maxSize += cols * rows;
46769
+ this.sheets[sheetId] = BinaryGrid.create(rows, cols);
46770
+ }
46771
+ }
46772
+ add(position) {
46773
+ const hasBeenInserted = this.sheets[position.sheetId].setValue(position, 1);
46774
+ if (hasBeenInserted) {
46775
+ this.insertions.push(position);
46776
+ }
46777
+ }
46778
+ addMany(positions) {
46779
+ for (const position of positions) {
46780
+ this.add(position);
46781
+ }
46782
+ }
46783
+ delete(position) {
46784
+ this.sheets[position.sheetId].setValue(position, 0);
46785
+ }
46786
+ deleteMany(positions) {
46787
+ for (const position of positions) {
46788
+ this.delete(position);
46789
+ }
46790
+ }
46791
+ has(position) {
46792
+ return this.sheets[position.sheetId].getValue(position) === 1;
46793
+ }
46794
+ clear() {
46795
+ const insertions = this.insertions;
46796
+ this.insertions = [];
46797
+ for (const sheetId in this.sheets) {
46798
+ this.sheets[sheetId].clear();
46799
+ }
46800
+ return insertions;
46801
+ }
46802
+ isEmpty() {
46803
+ if (this.insertions.length === 0) {
46804
+ return true;
46805
+ }
46806
+ for (const sheetId in this.sheets) {
46807
+ if (!this.sheets[sheetId].isEmpty()) {
46808
+ return false;
46809
+ }
46810
+ }
46811
+ return true;
46812
+ }
46813
+ fillAllPositions() {
46814
+ this.insertions = new Array(this.maxSize);
46815
+ let index = 0;
46816
+ for (const sheetId in this.sheets) {
46817
+ const grid = this.sheets[sheetId];
46818
+ grid.fillAllPositions();
46819
+ for (let i = 0; i < grid.rows; i++) {
46820
+ for (let j = 0; j < grid.cols; j++) {
46821
+ this.insertions[index++] = { sheetId, row: i, col: j };
46822
+ }
46823
+ }
46824
+ }
46825
+ }
46826
+ /**
46827
+ * Iterate over the positions in the order of insertion.
46828
+ * Note that the same position may be yielded multiple times if the value was added
46829
+ * to the set then removed and then added again.
46830
+ */
46831
+ *[Symbol.iterator]() {
46832
+ for (const position of this.insertions) {
46833
+ if (this.sheets[position.sheetId].getValue(position) === 1) {
46834
+ yield position;
46835
+ }
46836
+ }
46837
+ }
46838
+ }
46839
+
46487
46840
  /**
46488
46841
  * Contains, for each cell, the array
46489
46842
  * formulas that could potentially spread on it
@@ -46497,6 +46850,7 @@ stores.inject(MyMetaStore, storeInstance);
46497
46850
  *
46498
46851
  */
46499
46852
  class SpreadingRelation {
46853
+ createEmptyPositionSet;
46500
46854
  /**
46501
46855
  * Internal structure:
46502
46856
  * For something like
@@ -46525,39 +46879,42 @@ stores.inject(MyMetaStore, storeInstance);
46525
46879
  * - (B1) --> (B2, B3, B4) meaning B1 spreads on B2, B3 and B4
46526
46880
  *
46527
46881
  */
46528
- resultsToArrayFormulas = new Map();
46529
- arrayFormulasToResults = new Map();
46530
- getFormulaPositionsSpreadingOn(resultPositionId) {
46531
- return this.resultsToArrayFormulas.get(resultPositionId) || EMPTY_ARRAY;
46882
+ resultsToArrayFormulas = new PositionMap();
46883
+ arrayFormulasToResults = new PositionMap();
46884
+ constructor(createEmptyPositionSet) {
46885
+ this.createEmptyPositionSet = createEmptyPositionSet;
46886
+ }
46887
+ getFormulaPositionsSpreadingOn(resultPosition) {
46888
+ return this.resultsToArrayFormulas.get(resultPosition) || EMPTY_ARRAY;
46532
46889
  }
46533
- getArrayResultPositionIds(formulasPositionId) {
46534
- return this.arrayFormulasToResults.get(formulasPositionId) || EMPTY_ARRAY;
46890
+ getArrayResultPositions(formulasPosition) {
46891
+ return this.arrayFormulasToResults.get(formulasPosition) || EMPTY_ARRAY;
46535
46892
  }
46536
46893
  /**
46537
46894
  * Remove a node, also remove it from other nodes adjacency list
46538
46895
  */
46539
- removeNode(positionId) {
46540
- this.resultsToArrayFormulas.delete(positionId);
46541
- this.arrayFormulasToResults.delete(positionId);
46896
+ removeNode(position) {
46897
+ this.resultsToArrayFormulas.delete(position);
46898
+ this.arrayFormulasToResults.delete(position);
46542
46899
  }
46543
46900
  /**
46544
46901
  * Create a spreading relation between two cells
46545
46902
  */
46546
- addRelation({ arrayFormulaPositionId, resultPositionId, }) {
46547
- if (!this.resultsToArrayFormulas.has(resultPositionId)) {
46548
- this.resultsToArrayFormulas.set(resultPositionId, new Set());
46903
+ addRelation({ arrayFormulaPosition, resultPosition, }) {
46904
+ if (!this.resultsToArrayFormulas.has(resultPosition)) {
46905
+ this.resultsToArrayFormulas.set(resultPosition, this.createEmptyPositionSet());
46549
46906
  }
46550
- this.resultsToArrayFormulas.get(resultPositionId)?.add(arrayFormulaPositionId);
46551
- if (!this.arrayFormulasToResults.has(arrayFormulaPositionId)) {
46552
- this.arrayFormulasToResults.set(arrayFormulaPositionId, new Set());
46907
+ this.resultsToArrayFormulas.get(resultPosition)?.add(arrayFormulaPosition);
46908
+ if (!this.arrayFormulasToResults.has(arrayFormulaPosition)) {
46909
+ this.arrayFormulasToResults.set(arrayFormulaPosition, this.createEmptyPositionSet());
46553
46910
  }
46554
- this.arrayFormulasToResults.get(arrayFormulaPositionId)?.add(resultPositionId);
46911
+ this.arrayFormulasToResults.get(arrayFormulaPosition)?.add(resultPosition);
46555
46912
  }
46556
- hasArrayFormulaResult(positionId) {
46557
- return this.resultsToArrayFormulas.has(positionId);
46913
+ hasArrayFormulaResult(position) {
46914
+ return this.resultsToArrayFormulas.has(position);
46558
46915
  }
46559
- isArrayFormula(positionId) {
46560
- return this.arrayFormulasToResults.has(positionId);
46916
+ isArrayFormula(position) {
46917
+ return this.arrayFormulasToResults.has(position);
46561
46918
  }
46562
46919
  }
46563
46920
  const EMPTY_ARRAY = [];
@@ -46565,104 +46922,103 @@ stores.inject(MyMetaStore, storeInstance);
46565
46922
  const MAX_ITERATION = 30;
46566
46923
  const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
46567
46924
  const EMPTY_CELL = createEvaluatedCell({ value: null });
46568
- const EVAL_CONTEXT_INDEX = 2;
46569
46925
  class Evaluator {
46570
46926
  context;
46571
46927
  getters;
46572
46928
  compilationParams;
46573
- encoder = new PositionBitsEncoder();
46574
- evaluatedCells = new Map();
46575
- formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
46576
- blockedArrayFormulas = new Set();
46577
- spreadingRelations = new SpreadingRelation();
46929
+ evaluatedCells = new PositionMap();
46930
+ formulaDependencies = lazy(new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this)));
46931
+ blockedArrayFormulas = new PositionSet({});
46932
+ spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
46578
46933
  constructor(context, getters) {
46579
46934
  this.context = context;
46580
46935
  this.getters = getters;
46581
46936
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
46582
46937
  }
46583
46938
  getEvaluatedCell(position) {
46584
- return this.evaluatedCells.get(this.encoder.encode(position)) || EMPTY_CELL;
46939
+ return this.evaluatedCells.get(position) || EMPTY_CELL;
46585
46940
  }
46586
46941
  getSpreadPositionsOf(position) {
46587
- const positionId = this.encoder.encode(position);
46588
- if (!this.spreadingRelations.isArrayFormula(positionId)) {
46942
+ if (!this.spreadingRelations.isArrayFormula(position)) {
46589
46943
  return [];
46590
46944
  }
46591
- return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
46592
- }
46593
- getArrayFormulaSpreadingOn(position) {
46594
- const positionId = this.encoder.encode(position);
46595
- const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
46596
- return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
46945
+ return Array.from(this.spreadingRelations.getArrayResultPositions(position));
46597
46946
  }
46598
46947
  getEvaluatedPositions() {
46599
- return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
46948
+ return this.evaluatedCells.keys();
46600
46949
  }
46601
- getArrayFormulaSpreadingOnId(positionId) {
46602
- if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
46950
+ getArrayFormulaSpreadingOn(position) {
46951
+ if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
46603
46952
  return undefined;
46604
46953
  }
46605
- const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
46606
- return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
46954
+ const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
46955
+ return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
46607
46956
  }
46608
46957
  updateDependencies(position) {
46609
- const positionId = this.encoder.encode(position);
46610
- this.formulaDependencies().removeAllDependencies(positionId);
46611
- const dependencies = this.getDirectDependencies(positionId);
46612
- this.formulaDependencies().addDependencies(positionId, dependencies);
46958
+ this.formulaDependencies().removeAllDependencies(position);
46959
+ const dependencies = this.getDirectDependencies(position);
46960
+ this.formulaDependencies().addDependencies(position, dependencies);
46613
46961
  }
46614
46962
  addDependencies(position, dependencies) {
46615
- const positionId = this.encoder.encode(position);
46616
- this.formulaDependencies().addDependencies(positionId, dependencies);
46963
+ this.formulaDependencies().addDependencies(position, dependencies);
46617
46964
  }
46618
46965
  updateCompilationParameters() {
46619
46966
  // rebuild the compilation parameters (with a clean cache)
46620
46967
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
46621
- this.compilationParams[EVAL_CONTEXT_INDEX].updateDependencies =
46622
- this.updateDependencies.bind(this);
46623
- this.compilationParams[EVAL_CONTEXT_INDEX].addDependencies = this.addDependencies.bind(this);
46968
+ this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
46969
+ this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
46970
+ }
46971
+ createEmptyPositionSet() {
46972
+ const sheetSizes = {};
46973
+ for (const sheetId of this.getters.getSheetIds()) {
46974
+ sheetSizes[sheetId] = {
46975
+ rows: this.getters.getNumberRows(sheetId),
46976
+ cols: this.getters.getNumberCols(sheetId),
46977
+ };
46978
+ }
46979
+ return new PositionSet(sheetSizes);
46624
46980
  }
46625
46981
  evaluateCells(positions) {
46626
- const cells = positions.map((p) => this.encoder.encode(p));
46627
- const cellsToCompute = new JetSet(cells);
46628
- const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
46629
- cellsToCompute.addMany(this.getCellsDependingOn(cells));
46630
- cellsToCompute.addMany(arrayFormulasPositionIds);
46631
- cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
46982
+ const cellsToCompute = this.createEmptyPositionSet();
46983
+ cellsToCompute.addMany(positions);
46984
+ const arrayFormulasPositions = this.getArrayFormulasImpactedByChangesOf(positions);
46985
+ cellsToCompute.addMany(this.getCellsDependingOn(positions));
46986
+ cellsToCompute.addMany(arrayFormulasPositions);
46987
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
46632
46988
  this.evaluate(cellsToCompute);
46633
46989
  }
46634
- getArrayFormulasImpactedByChangesOf(positionIds) {
46635
- const impactedPositionIds = new JetSet();
46636
- for (const positionId of positionIds) {
46637
- const content = this.getCell(positionId)?.content;
46638
- const arrayFormulaPositionId = this.getArrayFormulaSpreadingOnId(positionId);
46639
- if (arrayFormulaPositionId !== undefined) {
46990
+ getArrayFormulasImpactedByChangesOf(positions) {
46991
+ const impactedPositions = this.createEmptyPositionSet();
46992
+ for (const position of positions) {
46993
+ const content = this.getters.getCell(position)?.content;
46994
+ const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
46995
+ if (arrayFormulaPosition !== undefined) {
46640
46996
  // take into account new collisions.
46641
- impactedPositionIds.add(arrayFormulaPositionId);
46997
+ impactedPositions.add(arrayFormulaPosition);
46642
46998
  }
46643
46999
  if (!content) {
46644
47000
  // The previous content could have blocked some array formulas
46645
- impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
47001
+ impactedPositions.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(position));
46646
47002
  }
46647
47003
  }
46648
- return impactedPositionIds;
47004
+ return impactedPositions;
46649
47005
  }
46650
47006
  buildDependencyGraph() {
46651
- this.blockedArrayFormulas = new Set();
46652
- this.spreadingRelations = new SpreadingRelation();
47007
+ this.blockedArrayFormulas = this.createEmptyPositionSet();
47008
+ this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
46653
47009
  this.formulaDependencies = lazy(() => {
46654
- const dependencies = [...this.getAllCells()].flatMap((positionId) => this.getDirectDependencies(positionId).map((range) => ({
46655
- data: positionId,
47010
+ const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position).map((range) => ({
47011
+ data: position,
46656
47012
  boundingBox: {
46657
47013
  zone: range.zone,
46658
47014
  sheetId: range.sheetId,
46659
47015
  },
46660
47016
  })));
46661
- return new FormulaDependencyGraph(this.encoder, dependencies);
47017
+ return new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this), dependencies);
46662
47018
  });
46663
47019
  }
46664
47020
  evaluateAllCells() {
46665
- this.evaluatedCells = new Map();
47021
+ this.evaluatedCells = new PositionMap();
46666
47022
  this.evaluate(this.getAllCells());
46667
47023
  }
46668
47024
  evaluateFormula(sheetId, formulaString) {
@@ -46679,57 +47035,50 @@ stores.inject(MyMetaStore, storeInstance);
46679
47035
  return result.value;
46680
47036
  }
46681
47037
  getAllCells() {
46682
- const positionIds = new JetSet();
46683
- for (const sheetId of this.getters.getSheetIds()) {
46684
- const cellIds = this.getters.getCells(sheetId);
46685
- for (const cellId in cellIds) {
46686
- positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
46687
- }
46688
- }
46689
- return positionIds;
47038
+ const positions = this.createEmptyPositionSet();
47039
+ positions.fillAllPositions();
47040
+ return positions;
46690
47041
  }
46691
- getArrayFormulasBlockedByOrSpreadingOn(positionId) {
46692
- if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
47042
+ getArrayFormulasBlockedByOrSpreadingOn(position) {
47043
+ if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
46693
47044
  return [];
46694
47045
  }
46695
- const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
46696
- const cells = new JetSet(arrayFormulas);
46697
- cells.addMany(this.getCellsDependingOn(arrayFormulas));
46698
- return cells;
47046
+ const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
47047
+ const positions = this.createEmptyPositionSet();
47048
+ positions.addMany(arrayFormulas);
47049
+ positions.addMany(this.getCellsDependingOn(arrayFormulas));
47050
+ return positions;
46699
47051
  }
46700
- nextPositionsToUpdate = new JetSet();
47052
+ nextPositionsToUpdate = new PositionSet({});
46701
47053
  cellsBeingComputed = new Set();
46702
- evaluate(cells) {
47054
+ evaluate(positions) {
46703
47055
  this.cellsBeingComputed = new Set();
46704
- this.nextPositionsToUpdate = cells;
47056
+ this.nextPositionsToUpdate = positions;
46705
47057
  let currentIteration = 0;
46706
- while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
47058
+ while (!this.nextPositionsToUpdate.isEmpty() && currentIteration++ < MAX_ITERATION) {
46707
47059
  this.updateCompilationParameters();
46708
- const positionIds = Array.from(this.nextPositionsToUpdate);
46709
- this.nextPositionsToUpdate.clear();
46710
- for (let i = 0; i < positionIds.length; ++i) {
46711
- const cell = positionIds[i];
46712
- this.evaluatedCells.delete(cell);
47060
+ const positions = this.nextPositionsToUpdate.clear();
47061
+ for (let i = 0; i < positions.length; ++i) {
47062
+ this.evaluatedCells.delete(positions[i]);
46713
47063
  }
46714
- for (let i = 0; i < positionIds.length; ++i) {
46715
- const cell = positionIds[i];
46716
- this.setEvaluatedCell(cell, this.computeCell(cell));
47064
+ for (let i = 0; i < positions.length; ++i) {
47065
+ const position = positions[i];
47066
+ const evaluatedCell = this.computeCell(position);
47067
+ if (evaluatedCell !== EMPTY_CELL) {
47068
+ this.evaluatedCells.set(position, evaluatedCell);
47069
+ }
46717
47070
  }
46718
47071
  }
46719
47072
  }
46720
- setEvaluatedCell(positionId, evaluatedCell) {
46721
- this.evaluatedCells.set(positionId, evaluatedCell);
46722
- }
46723
- computeCell(positionId) {
46724
- const evaluation = this.evaluatedCells.get(positionId);
47073
+ computeCell(position) {
47074
+ const evaluation = this.evaluatedCells.get(position);
46725
47075
  if (evaluation) {
46726
47076
  return evaluation; // already computed
46727
47077
  }
46728
- if (!this.blockedArrayFormulas.has(positionId)) {
46729
- this.invalidateSpreading(positionId);
47078
+ if (!this.blockedArrayFormulas.has(position)) {
47079
+ this.invalidateSpreading(position);
46730
47080
  }
46731
- const cellPosition = this.encoder.decode(positionId);
46732
- const cell = this.getters.getCell(cellPosition);
47081
+ const cell = this.getters.getCell(position);
46733
47082
  if (cell === undefined) {
46734
47083
  return EMPTY_CELL;
46735
47084
  }
@@ -46741,10 +47090,12 @@ stores.inject(MyMetaStore, storeInstance);
46741
47090
  }
46742
47091
  this.cellsBeingComputed.add(cellId);
46743
47092
  return cell.isFormula
46744
- ? this.computeFormulaCell(cellPosition.sheetId, cell)
47093
+ ? this.computeFormulaCell(position.sheetId, cell)
46745
47094
  : evaluateLiteral(cell.content, localeFormat);
46746
47095
  }
46747
47096
  catch (e) {
47097
+ e.value = e?.value || CellErrorType.GenericError;
47098
+ e.message = e?.message || implementationErrorMessage;
46748
47099
  return createEvaluatedCell(e);
46749
47100
  }
46750
47101
  finally {
@@ -46752,10 +47103,9 @@ stores.inject(MyMetaStore, storeInstance);
46752
47103
  }
46753
47104
  }
46754
47105
  computeAndSave(position) {
46755
- const positionId = this.encoder.encode(position);
46756
- const evaluatedCell = this.computeCell(positionId);
46757
- if (!this.evaluatedCells.has(positionId)) {
46758
- this.setEvaluatedCell(positionId, evaluatedCell);
47106
+ const evaluatedCell = this.computeCell(position);
47107
+ if (!this.evaluatedCells.has(position)) {
47108
+ this.evaluatedCells.set(position, evaluatedCell);
46759
47109
  }
46760
47110
  return evaluatedCell;
46761
47111
  }
@@ -46793,24 +47143,23 @@ stores.inject(MyMetaStore, storeInstance);
46793
47143
  throw new EvaluationError(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
46794
47144
  }
46795
47145
  updateSpreadRelation({ sheetId, col, row, }) {
46796
- const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
47146
+ const arrayFormulaPosition = { sheetId, col, row };
46797
47147
  return (i, j) => {
46798
47148
  const position = { sheetId, col: i + col, row: j + row };
46799
- const resultPositionId = this.encoder.encode(position);
46800
- this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
47149
+ this.spreadingRelations.addRelation({ resultPosition: position, arrayFormulaPosition });
46801
47150
  };
46802
47151
  }
46803
- checkCollision({ sheetId, col, row }) {
46804
- const formulaPositionId = this.encoder.encode({ sheetId, col, row });
47152
+ checkCollision(formulaPosition) {
47153
+ const { sheetId, col, row } = formulaPosition;
46805
47154
  return (i, j) => {
46806
47155
  const position = { sheetId: sheetId, col: i + col, row: j + row };
46807
47156
  const rawCell = this.getters.getCell(position);
46808
47157
  if (rawCell?.content ||
46809
47158
  this.getters.getEvaluatedCell(position).type !== CellValueType.empty) {
46810
- this.blockedArrayFormulas.add(formulaPositionId);
47159
+ this.blockedArrayFormulas.add(formulaPosition);
46811
47160
  throw new EvaluationError(_t("Array result was not expanded because it would overwrite data in %s.", toXC(position.col, position.row)));
46812
47161
  }
46813
- this.blockedArrayFormulas.delete(formulaPositionId);
47162
+ this.blockedArrayFormulas.delete(formulaPosition);
46814
47163
  };
46815
47164
  }
46816
47165
  spreadValues({ sheetId, col, row }, matrixResult) {
@@ -46818,19 +47167,18 @@ stores.inject(MyMetaStore, storeInstance);
46818
47167
  const position = { sheetId, col: i + col, row: j + row };
46819
47168
  const cell = this.getters.getCell(position);
46820
47169
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
46821
- const positionId = this.encoder.encode(position);
46822
- this.setEvaluatedCell(positionId, evaluatedCell);
47170
+ this.evaluatedCells.set(position, evaluatedCell);
46823
47171
  // check if formula dependencies present in the spread zone
46824
47172
  // if so, they need to be recomputed
46825
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
47173
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
46826
47174
  };
46827
47175
  }
46828
- invalidateSpreading(positionId) {
46829
- if (!this.spreadingRelations.isArrayFormula(positionId)) {
47176
+ invalidateSpreading(position) {
47177
+ if (!this.spreadingRelations.isArrayFormula(position)) {
46830
47178
  return;
46831
47179
  }
46832
- for (const child of this.spreadingRelations.getArrayResultPositionIds(positionId)) {
46833
- const content = this.getCell(child)?.content;
47180
+ for (const child of this.spreadingRelations.getArrayResultPositions(position)) {
47181
+ const content = this.getters.getCell(child)?.content;
46834
47182
  if (content) {
46835
47183
  // there's no point at re-evaluating overlapping array formulas,
46836
47184
  // there's still a collision
@@ -46840,28 +47188,25 @@ stores.inject(MyMetaStore, storeInstance);
46840
47188
  this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
46841
47189
  this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
46842
47190
  }
46843
- this.spreadingRelations.removeNode(positionId);
47191
+ this.spreadingRelations.removeNode(position);
46844
47192
  }
46845
47193
  // ----------------------------------------------------------
46846
47194
  // COMMON FUNCTIONALITY
46847
47195
  // ----------------------------------------------------------
46848
- getDirectDependencies(positionId) {
46849
- const cell = this.getCell(positionId);
47196
+ getDirectDependencies(position) {
47197
+ const cell = this.getters.getCell(position);
46850
47198
  if (!cell?.isFormula) {
46851
47199
  return [];
46852
47200
  }
46853
47201
  return cell.compiledFormula.dependencies;
46854
47202
  }
46855
- getCellsDependingOn(positionIds) {
47203
+ getCellsDependingOn(positions) {
46856
47204
  const ranges = [];
46857
- for (const positionId of positionIds) {
46858
- ranges.push(this.encoder.decodeToBoundingBox(positionId));
47205
+ for (const position of positions) {
47206
+ ranges.push({ sheetId: position.sheetId, zone: positionToZone(position) });
46859
47207
  }
46860
47208
  return this.formulaDependencies().getCellsDependingOn(ranges);
46861
47209
  }
46862
- getCell(positionId) {
46863
- return this.getters.getCell(this.encoder.decode(positionId));
46864
- }
46865
47210
  }
46866
47211
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
46867
47212
  for (let i = 0; i < nbColumns; ++i) {
@@ -46887,96 +47232,17 @@ stores.inject(MyMetaStore, storeInstance);
46887
47232
  }
46888
47233
  return fPayload;
46889
47234
  }
46890
- /**
46891
- * Encode (and decode) cell positions { sheetId, col, row }
46892
- * to a single integer.
46893
- *
46894
- * `col` and `row` values are encoded on 21 bits each (max 2^21 = 2_097_152),
46895
- * An incremental integer id is assigned to each different sheet id, starting at 0.
46896
- *
46897
- * e.g.
46898
- * Given { col: 10, row: 4, sheetId: "abcde" }
46899
- * we have:
46900
- * - row "4" encoded on 21 bits: 000000000000000000100
46901
- * - col "10" encoded on 21 bits: 000000000000000001010
46902
- * - sheetId: let's say it's the 4th sheetId met, encoded to: 11
46903
- *
46904
- * The final encoded value is found by concatenating the 3 bit sequences:
46905
- *
46906
- * sheetId: 11
46907
- * col: 000000000000000001010
46908
- * row: 000000000000000000100
46909
- * => 11000000000000000001010000000000000000000100
46910
- *
46911
- * this binary sequence is the integer 13194160504836
46912
- */
46913
- class PositionBitsEncoder {
46914
- sheetMapping = {};
46915
- inverseSheetMapping = new Map();
46916
- constructor() {
46917
- try {
46918
- // @ts-ignore
46919
- o_spreadsheet.__DEBUG__ = o_spreadsheet.__DEBUG__ || {};
46920
- // @ts-ignore
46921
- o_spreadsheet.__DEBUG__.decodePosition = this.decode.bind(this);
46922
- // @ts-ignore
46923
- o_spreadsheet.__DEBUG__.encodePosition = this.encode.bind(this);
46924
- }
46925
- catch (error) { }
46926
- }
46927
- /**
46928
- * Encode a cell position to a single integer.
46929
- */
46930
- encode({ sheetId, col, row }) {
46931
- return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
46932
- }
46933
- encodeBoundingBox({ sheetId, zone }) {
46934
- const positions = [];
46935
- forEachPositionsInZone(zone, (col, row) => {
46936
- positions.push(this.encode({ sheetId, col, row }));
46937
- });
46938
- return positions;
46939
- }
46940
- decode(id) {
46941
- // keep only the last 21 bits by AND-ing the bit sequence with 21 ones
46942
- const row = Number(id & 2097151n);
46943
- const col = Number((id >> 21n) & 2097151n);
46944
- const sheetId = this.decodeSheet(id >> 42n);
46945
- return { sheetId, col, row };
46946
- }
46947
- decodeToBoundingBox(id) {
46948
- const { sheetId, col, row } = this.decode(id);
46949
- return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
46950
- }
46951
- encodeSheet(sheetId) {
46952
- const sheetKey = this.sheetMapping[sheetId];
46953
- if (sheetKey === undefined) {
46954
- const newSheetKey = BigInt(Object.keys(this.sheetMapping).length);
46955
- this.sheetMapping[sheetId] = newSheetKey;
46956
- this.inverseSheetMapping.set(newSheetKey, sheetId);
46957
- return newSheetKey;
46958
- }
46959
- return sheetKey;
46960
- }
46961
- decodeSheet(sheetKey) {
46962
- const sheetId = this.inverseSheetMapping.get(sheetKey);
46963
- if (sheetId === undefined) {
46964
- throw new Error("Sheet id not found");
46965
- }
46966
- return sheetId;
46967
- }
46968
- }
46969
47235
  function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
46970
- compilationParams[EVAL_CONTEXT_INDEX].__originCellXC = lazy(() => {
47236
+ compilationParams.evalContext.__originCellXC = lazy(() => {
46971
47237
  if (!cellId) {
46972
47238
  return undefined;
46973
47239
  }
46974
47240
  // compute the value lazily for performance reasons
46975
- const position = compilationParams[EVAL_CONTEXT_INDEX].getters.getCellPosition(cellId);
47241
+ const position = compilationParams.evalContext.getters.getCellPosition(cellId);
46976
47242
  return toXC(position.col, position.row);
46977
47243
  });
46978
- compilationParams[EVAL_CONTEXT_INDEX].__originSheetId = sheetId;
46979
- return compiledFormula.execute(compiledFormula.dependencies, ...compilationParams);
47244
+ compilationParams.evalContext.__originSheetId = sheetId;
47245
+ return compiledFormula.execute(compiledFormula.dependencies, compilationParams.referenceDenormalizer, compilationParams.ensureRange, compilationParams.evalContext);
46980
47246
  }
46981
47247
 
46982
47248
  //#region
@@ -47083,6 +47349,7 @@ stores.inject(MyMetaStore, storeInstance);
47083
47349
  "getEvaluatedCellsInZone",
47084
47350
  "getSpreadPositionsOf",
47085
47351
  "getArrayFormulaSpreadingOn",
47352
+ "isEmpty",
47086
47353
  ];
47087
47354
  shouldRebuildDependenciesGraph = true;
47088
47355
  evaluator;
@@ -47111,6 +47378,10 @@ stores.inject(MyMetaStore, storeInstance);
47111
47378
  this.evaluator.updateDependencies(cmd);
47112
47379
  }
47113
47380
  break;
47381
+ case "DUPLICATE_SHEET":
47382
+ case "CREATE_SHEET":
47383
+ this.shouldRebuildDependenciesGraph = true;
47384
+ break;
47114
47385
  case "EVALUATE_CELLS":
47115
47386
  this.evaluator.evaluateAllCells();
47116
47387
  break;
@@ -47188,6 +47459,14 @@ stores.inject(MyMetaStore, storeInstance);
47188
47459
  getArrayFormulaSpreadingOn(position) {
47189
47460
  return this.evaluator.getArrayFormulaSpreadingOn(position);
47190
47461
  }
47462
+ /**
47463
+ * Check if a zone only contains empty cells
47464
+ */
47465
+ isEmpty(sheetId, zone) {
47466
+ return positions(zone)
47467
+ .map(({ col, row }) => this.getEvaluatedCell({ sheetId, col, row }))
47468
+ .every((cell) => cell.type === CellValueType.empty);
47469
+ }
47191
47470
  // ---------------------------------------------------------------------------
47192
47471
  // Export
47193
47472
  // ---------------------------------------------------------------------------
@@ -47305,12 +47584,13 @@ stores.inject(MyMetaStore, storeInstance);
47305
47584
  */
47306
47585
  class CustomColorsPlugin extends UIPlugin {
47307
47586
  customColors = {};
47308
- configCustomColors;
47309
- shouldUpdateColors = false;
47587
+ shouldUpdateColors = true;
47310
47588
  static getters = ["getCustomColors"];
47311
47589
  constructor(config) {
47312
47590
  super(config);
47313
- this.configCustomColors = config.customColors;
47591
+ for (const color of config.customColors ?? []) {
47592
+ this.tryToAddColor(color);
47593
+ }
47314
47594
  }
47315
47595
  handle(cmd) {
47316
47596
  switch (cmd.type) {
@@ -47321,31 +47601,29 @@ stores.inject(MyMetaStore, storeInstance);
47321
47601
  case "SET_BORDER":
47322
47602
  case "SET_ZONE_BORDERS":
47323
47603
  case "SET_FORMATTING":
47604
+ case "CREATE_TABLE":
47605
+ case "UPDATE_TABLE":
47324
47606
  this.history.update("shouldUpdateColors", true);
47607
+ break;
47325
47608
  }
47326
47609
  }
47327
47610
  finalize() {
47328
47611
  if (this.shouldUpdateColors) {
47329
47612
  this.history.update("shouldUpdateColors", false);
47330
- for (const color of this.getCustomColors()) {
47613
+ for (const color of this.computeCustomColors()) {
47331
47614
  this.tryToAddColor(color);
47332
47615
  }
47333
47616
  }
47334
47617
  }
47335
47618
  getCustomColors() {
47336
- let usedColors = this.configCustomColors;
47619
+ return sortWithClusters(Object.keys(this.customColors));
47620
+ }
47621
+ computeCustomColors() {
47622
+ let usedColors = [];
47337
47623
  for (const sheetId of this.getters.getSheetIds()) {
47338
- usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId));
47339
- }
47340
- return sortWithClusters([
47341
- ...new Set(
47342
- // remove duplicates first to check validity on a reduced
47343
- // set of colors, then normalize to HEX and remove duplicates
47344
- // again
47345
- [...new Set([...usedColors, ...Object.keys(this.customColors)])]
47346
- .filter(isColorValid)
47347
- .map(toHex)),
47348
- ]).filter((color) => !COLOR_PICKER_DEFAULTS.includes(color));
47624
+ usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId), this.getTableColors(sheetId));
47625
+ }
47626
+ return [...new Set([...usedColors])];
47349
47627
  }
47350
47628
  getColorsFromCells(sheetId) {
47351
47629
  const cells = Object.values(this.getters.getCells(sheetId));
@@ -47407,7 +47685,43 @@ stores.inject(MyMetaStore, storeInstance);
47407
47685
  }
47408
47686
  return [...chartsColors];
47409
47687
  }
47688
+ getTableColors(sheetId) {
47689
+ const tables = this.getters.getTables(sheetId);
47690
+ return tables.flatMap((table) => {
47691
+ const config = table.config;
47692
+ const style = TABLE_PRESETS[config.styleId];
47693
+ return [
47694
+ this.getTableStyleElementColors(style.wholeTable),
47695
+ config.numberOfHeaders > 0 ? this.getTableStyleElementColors(style.headerRow) : [],
47696
+ config.totalRow ? this.getTableStyleElementColors(style.totalRow) : [],
47697
+ config.bandedColumns ? this.getTableStyleElementColors(style.firstColumnStripe) : [],
47698
+ config.bandedColumns ? this.getTableStyleElementColors(style.secondColumnStripe) : [],
47699
+ config.bandedRows ? this.getTableStyleElementColors(style.firstRowStripe) : [],
47700
+ config.bandedRows ? this.getTableStyleElementColors(style.secondRowStripe) : [],
47701
+ config.firstColumn ? this.getTableStyleElementColors(style.firstColumn) : [],
47702
+ config.lastColumn ? this.getTableStyleElementColors(style.lastColumn) : [],
47703
+ ].flat();
47704
+ });
47705
+ }
47706
+ getTableStyleElementColors(element) {
47707
+ if (!element) {
47708
+ return [];
47709
+ }
47710
+ return [
47711
+ element.style?.fillColor,
47712
+ element.style?.textColor,
47713
+ element.border?.bottom?.color,
47714
+ element.border?.top?.color,
47715
+ element.border?.left?.color,
47716
+ element.border?.right?.color,
47717
+ element.border?.horizontal?.color,
47718
+ element.border?.vertical?.color,
47719
+ ].filter(isDefined$1);
47720
+ }
47410
47721
  tryToAddColor(color) {
47722
+ if (!isColorValid(color)) {
47723
+ return;
47724
+ }
47411
47725
  const formattedColor = toHex(color);
47412
47726
  if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
47413
47727
  this.history.update("customColors", formattedColor, true);
@@ -47831,8 +48145,6 @@ stores.inject(MyMetaStore, storeInstance);
47831
48145
  class EvaluationDataValidationPlugin extends UIPlugin {
47832
48146
  static getters = [
47833
48147
  "getDataValidationInvalidCriterionValueMessage",
47834
- "getDataValidationCheckBoxCellPositions",
47835
- "getDataValidationListCellsPositions",
47836
48148
  "getInvalidDataValidationMessage",
47837
48149
  "getValidationResultForCellValue",
47838
48150
  "isCellValidCheckbox",
@@ -47910,19 +48222,6 @@ stores.inject(MyMetaStore, storeInstance);
47910
48222
  const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
47911
48223
  return error ? { error, rule, isValid: false } : VALID_RESULT;
47912
48224
  }
47913
- getDataValidationCheckBoxCellPositions() {
47914
- const rules = this.getters
47915
- .getDataValidationRules(this.getters.getActiveSheetId())
47916
- .filter((rule) => rule.criterion.type === "isBoolean");
47917
- return getCellPositionsInRanges(rules.map((rule) => rule.ranges).flat()).filter((position) => this.isCellValidCheckbox(position));
47918
- }
47919
- getDataValidationListCellsPositions() {
47920
- const rules = this.getters
47921
- .getDataValidationRules(this.getters.getActiveSheetId())
47922
- .filter((rule) => (rule.criterion.type === "isValueInList" || rule.criterion.type === "isValueInRange") &&
47923
- rule.criterion.displayStyle === "arrow");
47924
- return getCellPositionsInRanges(rules.map((rule) => rule.ranges).flat());
47925
- }
47926
48225
  getValidationResultForCell(cellPosition) {
47927
48226
  const { col, row, sheetId } = cellPosition;
47928
48227
  if (!this.validationResults[sheetId]) {
@@ -48379,6 +48678,23 @@ stores.inject(MyMetaStore, storeInstance);
48379
48678
  * autofiller
48380
48679
  */
48381
48680
  autofillAuto() {
48681
+ const activePosition = this.getters.getActivePosition();
48682
+ const table = this.getters.getTable(activePosition);
48683
+ let autofillRow = table ? table.range.zone.bottom : this.getAutofillAutoLastRow();
48684
+ // Stop autofill at the next non-empty cell
48685
+ const selection = this.getters.getSelectedZone();
48686
+ for (let row = selection.bottom + 1; row <= autofillRow; row++) {
48687
+ if (this.getters.getEvaluatedCell({ ...activePosition, row }).type !== CellValueType.empty) {
48688
+ autofillRow = row - 1;
48689
+ break;
48690
+ }
48691
+ }
48692
+ if (autofillRow > selection.bottom) {
48693
+ this.select(activePosition.col, autofillRow);
48694
+ this.autofill(true);
48695
+ }
48696
+ }
48697
+ getAutofillAutoLastRow() {
48382
48698
  const zone = this.getters.getSelectedZone();
48383
48699
  const sheetId = this.getters.getActiveSheetId();
48384
48700
  let col = zone.left;
@@ -48402,10 +48718,7 @@ stores.inject(MyMetaStore, storeInstance);
48402
48718
  }
48403
48719
  }
48404
48720
  }
48405
- if (row !== zone.bottom) {
48406
- this.select(zone.left, row - 1);
48407
- this.autofill(true);
48408
- }
48721
+ return row - 1;
48409
48722
  }
48410
48723
  /**
48411
48724
  * Generate the next cell
@@ -49947,7 +50260,6 @@ stores.inject(MyMetaStore, storeInstance);
49947
50260
  * the search with a new value.
49948
50261
  */
49949
50262
  class FindAndReplacePlugin extends UIPlugin {
49950
- static layers = ["Search"];
49951
50263
  static getters = [];
49952
50264
  handle(cmd) {
49953
50265
  switch (cmd.type) {
@@ -51017,6 +51329,51 @@ stores.inject(MyMetaStore, storeInstance);
51017
51329
  }
51018
51330
  }
51019
51331
 
51332
+ class TableAutofillPlugin extends UIPlugin {
51333
+ handle(cmd) {
51334
+ switch (cmd.type) {
51335
+ case "AUTOFILL_TABLE_COLUMN":
51336
+ const table = this.getters.getTable(cmd);
51337
+ const cell = this.getters.getCell(cmd);
51338
+ if (!table || !table.config.automaticAutofill || !cell?.isFormula)
51339
+ return;
51340
+ const { col, row } = cmd;
51341
+ const tableContentZone = getTableContentZone(table.range.zone, table.config);
51342
+ if (tableContentZone && isInside(col, row, tableContentZone)) {
51343
+ this.autofillTableZone(cmd, tableContentZone);
51344
+ }
51345
+ break;
51346
+ }
51347
+ }
51348
+ autofillTableZone(autofillSource, zone) {
51349
+ if (zone.top === zone.bottom) {
51350
+ return;
51351
+ }
51352
+ const { col, row, sheetId } = autofillSource;
51353
+ for (let r = zone.top; r <= zone.bottom; r++) {
51354
+ if (r === row) {
51355
+ continue;
51356
+ }
51357
+ if (this.getters.getEvaluatedCell({ col, row: r, sheetId }).type !== CellValueType.empty) {
51358
+ return;
51359
+ }
51360
+ }
51361
+ // TODO: seems odd that autofill a table column have side effects on the selection. Autofill commands should be
51362
+ // refactored to take the selection as a parameter
51363
+ const oldSelection = {
51364
+ zone: this.getters.getSelectedZone(),
51365
+ cell: this.getters.getActivePosition(),
51366
+ };
51367
+ this.selection.selectCell(col, row);
51368
+ this.dispatch("AUTOFILL_SELECT", { col, row: zone.bottom });
51369
+ this.dispatch("AUTOFILL");
51370
+ this.selection.selectCell(col, row);
51371
+ this.dispatch("AUTOFILL_SELECT", { col, row: zone.top });
51372
+ this.dispatch("AUTOFILL");
51373
+ this.selection.selectZone(oldSelection);
51374
+ }
51375
+ }
51376
+
51020
51377
  class TableStylePlugin extends UIPlugin {
51021
51378
  static getters = ["getCellTableStyle", "getCellTableBorder"];
51022
51379
  tableStyles = {};
@@ -52884,6 +53241,8 @@ stores.inject(MyMetaStore, storeInstance);
52884
53241
  "getEdgeScrollRow",
52885
53242
  "getVisibleFigures",
52886
53243
  "getVisibleRect",
53244
+ "getVisibleRectWithoutHeaders",
53245
+ "getVisibleCellPositions",
52887
53246
  "getColRowOffsetInViewport",
52888
53247
  "getMainViewportCoordinates",
52889
53248
  "getActiveSheetScrollInfo",
@@ -53129,6 +53488,26 @@ stores.inject(MyMetaStore, storeInstance);
53129
53488
  const viewports = this.getSubViewports(sheetId);
53130
53489
  return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
53131
53490
  }
53491
+ /**
53492
+ * Get the positions of all the cells that are visible in the viewport, taking merges into account.
53493
+ */
53494
+ getVisibleCellPositions() {
53495
+ const visibleCols = this.getSheetViewVisibleCols();
53496
+ const visibleRows = this.getSheetViewVisibleRows();
53497
+ const sheetId = this.getters.getActiveSheetId();
53498
+ const positions = [];
53499
+ for (const col of visibleCols) {
53500
+ for (const row of visibleRows) {
53501
+ const position = { sheetId, col, row };
53502
+ const mainPosition = this.getters.getMainCellPosition(position);
53503
+ if (mainPosition.row !== row || mainPosition.col !== col) {
53504
+ continue;
53505
+ }
53506
+ positions.push(position);
53507
+ }
53508
+ }
53509
+ return positions;
53510
+ }
53132
53511
  /**
53133
53512
  * Return the main viewport maximum size relative to the client size.
53134
53513
  */
@@ -53248,6 +53627,13 @@ stores.inject(MyMetaStore, storeInstance);
53248
53627
  * Computes the coordinates and size to draw the zone on the canvas
53249
53628
  */
53250
53629
  getVisibleRect(zone) {
53630
+ const rect = this.getVisibleRectWithoutHeaders(zone);
53631
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
53632
+ }
53633
+ /**
53634
+ * Computes the coordinates and size to draw the zone without taking the grid offset into account
53635
+ */
53636
+ getVisibleRectWithoutHeaders(zone) {
53251
53637
  const sheetId = this.getters.getActiveSheetId();
53252
53638
  const viewportRects = this.getSubViewports(sheetId)
53253
53639
  .map((viewport) => viewport.getRect(zone))
@@ -53259,12 +53645,7 @@ stores.inject(MyMetaStore, storeInstance);
53259
53645
  const y = Math.min(...viewportRects.map((rect) => rect.y));
53260
53646
  const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
53261
53647
  const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
53262
- return {
53263
- x: x + this.gridOffsetX,
53264
- y: y + this.gridOffsetY,
53265
- width,
53266
- height,
53267
- };
53648
+ return { x, y, width, height };
53268
53649
  }
53269
53650
  /**
53270
53651
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -53518,13 +53899,8 @@ stores.inject(MyMetaStore, storeInstance);
53518
53899
  }
53519
53900
  break;
53520
53901
  case "UPDATE_CELL":
53521
- if ("content" in cmd || "format" in cmd) {
53522
- this.headerPositions = {};
53523
- this.isDirty = true;
53524
- }
53525
- else {
53526
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
53527
- }
53902
+ this.headerPositions = {};
53903
+ this.isDirty = true;
53528
53904
  break;
53529
53905
  case "UPDATE_FILTER":
53530
53906
  case "UPDATE_TABLE":
@@ -53647,7 +54023,8 @@ stores.inject(MyMetaStore, storeInstance);
53647
54023
  .add("split_to_columns", SplitToColumnsPlugin)
53648
54024
  .add("collaborative", CollaborativePlugin)
53649
54025
  .add("history", HistoryPlugin)
53650
- .add("data_cleanup", DataCleanupPlugin);
54026
+ .add("data_cleanup", DataCleanupPlugin)
54027
+ .add("table_autofill", TableAutofillPlugin);
53651
54028
  // Plugins which have a state, but which should not be shared in collaborative
53652
54029
  const statefulUIPluginRegistry = new Registry()
53653
54030
  .add("selection", GridSelectionPlugin)
@@ -53990,6 +54367,9 @@ stores.inject(MyMetaStore, storeInstance);
53990
54367
  this.scrollToSheet();
53991
54368
  }
53992
54369
  onDblClick() {
54370
+ if (this.env.model.getters.isReadonly()) {
54371
+ return;
54372
+ }
53993
54373
  this.startEdition();
53994
54374
  }
53995
54375
  onKeyDown(ev) {
@@ -55946,7 +56326,6 @@ stores.inject(MyMetaStore, storeInstance);
55946
56326
  }
55947
56327
  }, () => [this.env.model.getters.getActiveSheetId()]);
55948
56328
  owl.useExternalListener(window, "resize", () => this.render(true));
55949
- owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
55950
56329
  this.bindModelEvents();
55951
56330
  owl.onWillUpdateProps((nextProps) => {
55952
56331
  if (nextProps.model !== this.props.model) {
@@ -59787,9 +60166,9 @@ stores.inject(MyMetaStore, storeInstance);
59787
60166
  exports.tokenize = tokenize;
59788
60167
 
59789
60168
 
59790
- __info__.version = "17.2.0-alpha.6";
59791
- __info__.date = "2024-02-28T12:28:44.114Z";
59792
- __info__.hash = "318a04e";
60169
+ __info__.version = "17.2.0";
60170
+ __info__.date = "2024-03-20T08:12:44.398Z";
60171
+ __info__.hash = "1869c24";
59793
60172
 
59794
60173
 
59795
60174
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);