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

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-alpha.8
7
+ * @date 2024-03-15T11:01:01.388Z
8
+ * @hash 11cf381
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;
@@ -2145,7 +2145,6 @@
2145
2145
  Background: 0,
2146
2146
  Highlights: 1,
2147
2147
  Clipboard: 2,
2148
- Search: 3,
2149
2148
  Chart: 4,
2150
2149
  Autofill: 5,
2151
2150
  Selection: 6,
@@ -6317,6 +6316,11 @@
6317
6316
  this.pasteTableCell(sheetId, tableCell, position, clipboardOptions);
6318
6317
  }
6319
6318
  }
6319
+ if (tableCells.length === 1) {
6320
+ for (let c = 0; c < tableCells[0].length; c++) {
6321
+ this.dispatch("AUTOFILL_TABLE_COLUMN", { col: col + c, row, sheetId });
6322
+ }
6323
+ }
6320
6324
  }
6321
6325
  pasteTableCell(sheetId, tableCell, position, options) {
6322
6326
  if (tableCell.table && !options?.pasteOption) {
@@ -7443,6 +7447,7 @@
7443
7447
  firstColumn: _t("First column"),
7444
7448
  lastColumn: _t("Last column"),
7445
7449
  bandedColumns: _t("Banded columns"),
7450
+ automaticAutofill: _t("Automatically autofill formulas"),
7446
7451
  totalRow: _t("Total row"),
7447
7452
  },
7448
7453
  Tooltips: {
@@ -8189,11 +8194,18 @@ stores.inject(MyMetaStore, storeInstance);
8189
8194
  }
8190
8195
  const color = highlight.color || HIGHLIGHT_COLOR;
8191
8196
  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";
8197
+ if (!highlight.noBorder) {
8198
+ ctx.strokeStyle = color;
8199
+ if (highlight.thinLine) {
8200
+ ctx.lineWidth = 1;
8201
+ ctx.strokeRect(x, y, width, height);
8202
+ }
8203
+ else {
8204
+ ctx.lineWidth = 2;
8205
+ /** + 0.5 offset to have sharp lines. See comment in {@link RendererPlugin#drawBorder} for more details */
8206
+ ctx.strokeRect(x + 0.5, y + 0.5, width, height);
8207
+ }
8208
+ }
8197
8209
  if (!highlight.noFill) {
8198
8210
  ctx.fillStyle = setColorAlpha(color, highlight.fillAlpha ?? 0.12);
8199
8211
  ctx.fillRect(x, y, width, height);
@@ -8567,6 +8579,7 @@ stores.inject(MyMetaStore, storeInstance);
8567
8579
  else if (cell.link) {
8568
8580
  content = markdownLink(content, cell.link.url);
8569
8581
  }
8582
+ this.addHeadersForSpreadingFormula(content);
8570
8583
  this.model.dispatch("UPDATE_CELL", {
8571
8584
  sheetId: this.sheetId,
8572
8585
  col,
@@ -8582,6 +8595,7 @@ stores.inject(MyMetaStore, storeInstance);
8582
8595
  row,
8583
8596
  });
8584
8597
  }
8598
+ this.model.dispatch("AUTOFILL_TABLE_COLUMN", { col, row, sheetId: this.sheetId });
8585
8599
  this.setContent("");
8586
8600
  }
8587
8601
  }
@@ -8784,6 +8798,38 @@ stores.inject(MyMetaStore, storeInstance);
8784
8798
  }
8785
8799
  this.colorIndexByRange = colorsToKeep;
8786
8800
  }
8801
+ /** Add headers at the end of the sheet so the formula in the composer has enough space to spread */
8802
+ addHeadersForSpreadingFormula(content) {
8803
+ if (!content.startsWith("=")) {
8804
+ return;
8805
+ }
8806
+ const evaluated = this.getters.evaluateFormula(this.sheetId, content);
8807
+ if (!isMatrix(evaluated)) {
8808
+ return;
8809
+ }
8810
+ const numberOfRows = this.getters.getNumberRows(this.sheetId);
8811
+ const numberOfCols = this.getters.getNumberCols(this.sheetId);
8812
+ const missingRows = this.row + evaluated[0].length - numberOfRows;
8813
+ const missingCols = this.col + evaluated.length - numberOfCols;
8814
+ if (missingCols > 0) {
8815
+ this.model.dispatch("ADD_COLUMNS_ROWS", {
8816
+ sheetId: this.sheetId,
8817
+ dimension: "COL",
8818
+ base: numberOfCols - 1,
8819
+ position: "after",
8820
+ quantity: missingCols + 20,
8821
+ });
8822
+ }
8823
+ if (missingRows > 0) {
8824
+ this.model.dispatch("ADD_COLUMNS_ROWS", {
8825
+ sheetId: this.sheetId,
8826
+ dimension: "ROW",
8827
+ base: numberOfRows - 1,
8828
+ position: "after",
8829
+ quantity: missingRows + 50,
8830
+ });
8831
+ }
8832
+ }
8787
8833
  /**
8788
8834
  * Highlight all ranges that can be found in the composer content.
8789
8835
  */
@@ -17417,10 +17463,11 @@ stores.inject(MyMetaStore, storeInstance);
17417
17463
  const DEFAULT_MATCH_MODE = 0;
17418
17464
  const DEFAULT_SEARCH_MODE = 1;
17419
17465
  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
- }
17466
+ function valueNotAvailable(searchKey) {
17467
+ return {
17468
+ value: CellErrorType.NotAvailable,
17469
+ message: _t("Did not find value '%s' in [[FUNCTION_NAME]] evaluation.", toString(searchKey)),
17470
+ };
17424
17471
  }
17425
17472
  // -----------------------------------------------------------------------------
17426
17473
  // ADDRESS
@@ -17517,7 +17564,9 @@ stores.inject(MyMetaStore, storeInstance);
17517
17564
  ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
17518
17565
  : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
17519
17566
  const col = range[colIndex];
17520
- assertAvailable(col, searchKey?.value);
17567
+ if (col === undefined) {
17568
+ return valueNotAvailable(searchKey);
17569
+ }
17521
17570
  return col[_index - 1];
17522
17571
  },
17523
17572
  isExported: true,
@@ -17636,11 +17685,11 @@ stores.inject(MyMetaStore, storeInstance);
17636
17685
  : (range, index) => range[index][0].value;
17637
17686
  const rangeLength = verticalSearch ? nbRow : nbCol;
17638
17687
  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);
17688
+ if (index === -1 ||
17689
+ (verticalSearch && searchArray[0][index] === undefined) ||
17690
+ (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
17691
+ return valueNotAvailable(searchKey);
17692
+ }
17644
17693
  if (resultRange === undefined) {
17645
17694
  return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
17646
17695
  }
@@ -17690,7 +17739,10 @@ stores.inject(MyMetaStore, storeInstance);
17690
17739
  index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
17691
17740
  break;
17692
17741
  }
17693
- assertAvailable(nbCol === 1 ? range[0][index] : range[index], searchKey);
17742
+ if ((nbCol === 1 && range[0][index] === undefined) ||
17743
+ (nbCol !== 1 && range[index] === undefined)) {
17744
+ return valueNotAvailable(searchKey);
17745
+ }
17694
17746
  return index + 1;
17695
17747
  },
17696
17748
  isExported: true,
@@ -17749,7 +17801,9 @@ stores.inject(MyMetaStore, storeInstance);
17749
17801
  ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
17750
17802
  : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
17751
17803
  const value = range[_index - 1][rowIndex];
17752
- assertAvailable(value, searchKey);
17804
+ if (value === undefined) {
17805
+ return valueNotAvailable(searchKey);
17806
+ }
17753
17807
  return value;
17754
17808
  },
17755
17809
  isExported: true,
@@ -17809,7 +17863,9 @@ stores.inject(MyMetaStore, storeInstance);
17809
17863
  ? returnRange.map((col) => [col[index]])
17810
17864
  : [returnRange[index]];
17811
17865
  }
17812
- assertAvailable(defaultValue, searchKey);
17866
+ if (defaultValue === undefined) {
17867
+ return valueNotAvailable(searchKey);
17868
+ }
17813
17869
  return [[defaultValue]];
17814
17870
  },
17815
17871
  isExported: true,
@@ -18576,8 +18632,7 @@ stores.inject(MyMetaStore, storeInstance);
18576
18632
  function addErrorHandling(compute, functionName) {
18577
18633
  return function (...args) {
18578
18634
  try {
18579
- const computeFormula = compute.bind(this);
18580
- return computeFormula(...args);
18635
+ return compute.apply(this, args);
18581
18636
  }
18582
18637
  catch (e) {
18583
18638
  return handleError(e, functionName);
@@ -18611,8 +18666,7 @@ stores.inject(MyMetaStore, storeInstance);
18611
18666
  }
18612
18667
  function addResultHandling(compute) {
18613
18668
  return function (...args) {
18614
- const computeResult = compute.bind(this);
18615
- const result = computeResult(...args);
18669
+ const result = compute.apply(this, args);
18616
18670
  if (!isMatrix(result)) {
18617
18671
  if (typeof result === "object" && result !== null && "value" in result) {
18618
18672
  return result;
@@ -18827,8 +18881,10 @@ stores.inject(MyMetaStore, storeInstance);
18827
18881
  if (x === cell) {
18828
18882
  found = true;
18829
18883
  }
18830
- const cellValue = evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
18831
- if (filter(cellValue)) {
18884
+ const cellValue = x?.isFormula
18885
+ ? undefined
18886
+ : evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
18887
+ if (cellValue && filter(cellValue)) {
18832
18888
  group.push(cellValue);
18833
18889
  }
18834
18890
  else {
@@ -19477,9 +19533,15 @@ stores.inject(MyMetaStore, storeInstance);
19477
19533
  : (label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`);
19478
19534
  }
19479
19535
  else {
19480
- label = label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`;
19536
+ label = `${ChartTerms.Series} ${parseInt(dsIndex) + 1}`;
19481
19537
  }
19482
19538
  let data = ds.dataRange ? getData(getters, ds) : [];
19539
+ if (data.every((e) => typeof e === "string" && !isEvaluationError(e))) {
19540
+ // In this case, we want a chart based on the string occurrences count
19541
+ // This will be done by associating each string with a value of 1 and
19542
+ // the using the classical aggregation method to sum the values.
19543
+ data.fill(1);
19544
+ }
19483
19545
  datasetValues.push({ data, label });
19484
19546
  }
19485
19547
  return datasetValues;
@@ -19578,7 +19640,7 @@ stores.inject(MyMetaStore, storeInstance);
19578
19640
  dataSets: context.range ? context.range : [],
19579
19641
  dataSetsHaveTitle: false,
19580
19642
  stacked: false,
19581
- aggregated: false,
19643
+ aggregated: context.aggregated ?? false,
19582
19644
  legendPosition: "top",
19583
19645
  title: context.title || "",
19584
19646
  type: "bar",
@@ -19594,6 +19656,7 @@ stores.inject(MyMetaStore, storeInstance);
19594
19656
  auxiliaryRange: this.labelRange
19595
19657
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
19596
19658
  : undefined,
19659
+ aggregated: this.aggregated,
19597
19660
  };
19598
19661
  }
19599
19662
  copyForSheetId(sheetId) {
@@ -20343,7 +20406,7 @@ stores.inject(MyMetaStore, storeInstance);
20343
20406
  verticalAxisPosition: "left",
20344
20407
  labelRange: context.auxiliaryRange || undefined,
20345
20408
  stacked: false,
20346
- aggregated: false,
20409
+ aggregated: context.aggregated ?? false,
20347
20410
  cumulative: false,
20348
20411
  };
20349
20412
  }
@@ -20376,6 +20439,7 @@ stores.inject(MyMetaStore, storeInstance);
20376
20439
  auxiliaryRange: this.labelRange
20377
20440
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
20378
20441
  : undefined,
20442
+ aggregated: this.aggregated,
20379
20443
  };
20380
20444
  }
20381
20445
  updateRanges(applyChange) {
@@ -20450,7 +20514,7 @@ stores.inject(MyMetaStore, storeInstance);
20450
20514
  title: context.title || "",
20451
20515
  type: "pie",
20452
20516
  labelRange: context.auxiliaryRange || undefined,
20453
- aggregated: false,
20517
+ aggregated: context.aggregated ?? false,
20454
20518
  };
20455
20519
  }
20456
20520
  getDefinition() {
@@ -20464,6 +20528,7 @@ stores.inject(MyMetaStore, storeInstance);
20464
20528
  auxiliaryRange: this.labelRange
20465
20529
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
20466
20530
  : undefined,
20531
+ aggregated: this.aggregated,
20467
20532
  };
20468
20533
  }
20469
20534
  getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
@@ -20655,7 +20720,7 @@ stores.inject(MyMetaStore, storeInstance);
20655
20720
  type: "scatter",
20656
20721
  verticalAxisPosition: "left",
20657
20722
  labelRange: context.auxiliaryRange || undefined,
20658
- aggregated: false,
20723
+ aggregated: context.aggregated ?? false,
20659
20724
  };
20660
20725
  }
20661
20726
  getDefinition() {
@@ -20685,6 +20750,7 @@ stores.inject(MyMetaStore, storeInstance);
20685
20750
  auxiliaryRange: this.labelRange
20686
20751
  ? this.getters.getRangeString(this.labelRange, this.sheetId)
20687
20752
  : undefined,
20753
+ aggregated: this.aggregated,
20688
20754
  };
20689
20755
  }
20690
20756
  updateRanges(applyChange) {
@@ -22352,6 +22418,50 @@ stores.inject(MyMetaStore, storeInstance);
22352
22418
  return isMacOS() ? ev.metaKey : ev.ctrlKey;
22353
22419
  }
22354
22420
 
22421
+ /**
22422
+ * Repeatedly calls a callback function with a time delay between calls.
22423
+ */
22424
+ function useInterval(callback, delay) {
22425
+ let intervalId;
22426
+ const { setInterval, clearInterval } = window;
22427
+ owl.useEffect(() => {
22428
+ intervalId = setInterval(callback, delay);
22429
+ return () => clearInterval(intervalId);
22430
+ }, () => [delay]);
22431
+ return {
22432
+ pause: () => {
22433
+ clearInterval(intervalId);
22434
+ intervalId = undefined;
22435
+ },
22436
+ resume: () => {
22437
+ if (intervalId === undefined) {
22438
+ intervalId = setInterval(callback, delay);
22439
+ }
22440
+ },
22441
+ };
22442
+ }
22443
+ /**
22444
+ * Calls a callback function with a time delay
22445
+ */
22446
+ function useTimeOut() {
22447
+ let timeOutId;
22448
+ function clear() {
22449
+ if (timeOutId !== undefined) {
22450
+ clearTimeout(timeOutId);
22451
+ timeOutId = undefined;
22452
+ }
22453
+ }
22454
+ function schedule(callback, delay) {
22455
+ clear();
22456
+ timeOutId = setTimeout(callback, delay);
22457
+ }
22458
+ owl.onWillUnmount(clear);
22459
+ return {
22460
+ clear,
22461
+ schedule,
22462
+ };
22463
+ }
22464
+
22355
22465
  //------------------------------------------------------------------------------
22356
22466
  // Context Menu Component
22357
22467
  //------------------------------------------------------------------------------
@@ -22402,6 +22512,7 @@ stores.inject(MyMetaStore, storeInstance);
22402
22512
  }
22403
22513
  }
22404
22514
  `;
22515
+ const TIMEOUT_DELAY = 250;
22405
22516
  class Menu extends owl.Component {
22406
22517
  static template = "o-spreadsheet-Menu";
22407
22518
  static props = {
@@ -22412,6 +22523,7 @@ stores.inject(MyMetaStore, storeInstance);
22412
22523
  onClose: Function,
22413
22524
  onMenuClicked: { type: Function, optional: true },
22414
22525
  menuId: { type: String, optional: true },
22526
+ onMouseOver: { type: Function, optional: true },
22415
22527
  };
22416
22528
  static components = { Menu, Popover };
22417
22529
  static defaultProps = {
@@ -22422,10 +22534,12 @@ stores.inject(MyMetaStore, storeInstance);
22422
22534
  position: null,
22423
22535
  scrollOffset: 0,
22424
22536
  menuItems: [],
22537
+ isHoveringChild: false,
22425
22538
  });
22426
22539
  menuRef = owl.useRef("menu");
22427
22540
  hoveredMenu = undefined;
22428
22541
  position = useAbsoluteBoundingRect(this.menuRef);
22542
+ openingTimeOut = useTimeOut();
22429
22543
  setup() {
22430
22544
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
22431
22545
  owl.useExternalListener(window, "contextmenu", this.onExternalClick, { capture: true });
@@ -22525,6 +22639,9 @@ stores.inject(MyMetaStore, storeInstance);
22525
22639
  }
22526
22640
  return false;
22527
22641
  }
22642
+ isActive(menuItem) {
22643
+ return (this.subMenu?.isHoveringChild || false) && this.isParentMenu(this.subMenu, menuItem);
22644
+ }
22528
22645
  onScroll(ev) {
22529
22646
  this.subMenu.scrollOffset = ev.target.scrollTop;
22530
22647
  }
@@ -22532,10 +22649,10 @@ stores.inject(MyMetaStore, storeInstance);
22532
22649
  * If the given menu is not disabled, open it's submenu at the
22533
22650
  * correct position according to available surrounding space.
22534
22651
  */
22535
- openSubMenu(menu, ev) {
22536
- const parentMenuEl = ev.currentTarget;
22537
- if (!parentMenuEl)
22652
+ openSubMenu(menu, parentMenuEl) {
22653
+ if (!parentMenuEl) {
22538
22654
  return;
22655
+ }
22539
22656
  const y = parentMenuEl.getBoundingClientRect().top;
22540
22657
  this.subMenu.position = {
22541
22658
  x: this.position.x + this.props.depth * MENU_WIDTH,
@@ -22549,32 +22666,50 @@ stores.inject(MyMetaStore, storeInstance);
22549
22666
  return subMenu.parentMenu?.id === menuItem.id;
22550
22667
  }
22551
22668
  closeSubMenu() {
22669
+ if (this.subMenu.isHoveringChild) {
22670
+ return;
22671
+ }
22552
22672
  this.subMenu.isOpen = false;
22553
22673
  this.subMenu.parentMenu = undefined;
22554
22674
  }
22555
22675
  onClickMenu(menu, ev) {
22556
22676
  if (this.isEnabled(menu)) {
22557
22677
  if (this.isRoot(menu)) {
22558
- this.openSubMenu(menu, ev);
22678
+ this.openSubMenu(menu, ev.currentTarget);
22559
22679
  }
22560
22680
  else {
22561
22681
  this.activateMenu(menu);
22562
22682
  }
22563
22683
  }
22564
22684
  }
22565
- onMouseEnter(menu, ev) {
22566
- this.hoveredMenu = menu;
22567
- menu.onStartHover?.(this.env);
22685
+ onMouseOver(menu, ev) {
22568
22686
  if (this.isEnabled(menu)) {
22569
- if (this.isRoot(menu)) {
22570
- this.openSubMenu(menu, ev);
22687
+ if (this.isParentMenu(this.subMenu, menu)) {
22688
+ this.openingTimeOut.clear();
22689
+ return;
22571
22690
  }
22572
- else {
22573
- this.closeSubMenu();
22691
+ const currentTarget = ev.currentTarget;
22692
+ if (this.isRoot(menu)) {
22693
+ this.openingTimeOut.schedule(() => {
22694
+ this.openSubMenu(menu, currentTarget);
22695
+ }, TIMEOUT_DELAY);
22574
22696
  }
22575
22697
  }
22576
22698
  }
22699
+ onMouseOverMainMenu() {
22700
+ this.props.onMouseOver?.();
22701
+ this.subMenu.isHoveringChild = false;
22702
+ }
22703
+ onMouseOverChildMenu() {
22704
+ this.subMenu.isHoveringChild = true;
22705
+ this.openingTimeOut.clear();
22706
+ }
22707
+ onMouseEnter(menu, ev) {
22708
+ this.hoveredMenu = menu;
22709
+ menu.onStartHover?.(this.env);
22710
+ }
22577
22711
  onMouseLeave(menu) {
22712
+ this.openingTimeOut.schedule(this.closeSubMenu.bind(this), TIMEOUT_DELAY);
22578
22713
  this.hoveredMenu = undefined;
22579
22714
  menu.onStopHover?.(this.env);
22580
22715
  }
@@ -22826,7 +22961,8 @@ stores.inject(MyMetaStore, storeInstance);
22826
22961
  */
22827
22962
  function getSmartChartDefinition(zone, getters) {
22828
22963
  let dataSetZone = zone;
22829
- if (zone.left !== zone.right) {
22964
+ const singleColumn = zoneToDimension(zone).numberOfCols === 1;
22965
+ if (!singleColumn) {
22830
22966
  dataSetZone = { ...zone, left: zone.left + 1 };
22831
22967
  }
22832
22968
  const dataSets = [zoneToXc(dataSetZone)];
@@ -22860,7 +22996,7 @@ stores.inject(MyMetaStore, storeInstance);
22860
22996
  }
22861
22997
  }
22862
22998
  let labelRangeXc;
22863
- if (zone.left !== zone.right) {
22999
+ if (!singleColumn) {
22864
23000
  labelRangeXc = zoneToXc({
22865
23001
  ...zone,
22866
23002
  right: zone.left,
@@ -22884,6 +23020,19 @@ stores.inject(MyMetaStore, storeInstance);
22884
23020
  legendPosition: newLegendPos,
22885
23021
  };
22886
23022
  }
23023
+ const _dataSets = createDataSets(getters, dataSets, sheetId, dataSetsHaveTitle);
23024
+ if (singleColumn &&
23025
+ getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
23026
+ return {
23027
+ title: "",
23028
+ dataSets,
23029
+ aggregated: true,
23030
+ labelRange: dataSets[0],
23031
+ type: "pie",
23032
+ legendPosition: "top",
23033
+ dataSetsHaveTitle: false,
23034
+ };
23035
+ }
22887
23036
  return {
22888
23037
  title,
22889
23038
  dataSets,
@@ -22911,6 +23060,7 @@ stores.inject(MyMetaStore, storeInstance);
22911
23060
  numberOfHeaders: 1,
22912
23061
  bandedRows: true,
22913
23062
  bandedColumns: false,
23063
+ automaticAutofill: true,
22914
23064
  styleId: "TableStyleMedium2",
22915
23065
  };
22916
23066
  function generateColorSet(name, highlightColor) {
@@ -26394,7 +26544,7 @@ stores.inject(MyMetaStore, storeInstance);
26394
26544
  {
26395
26545
  name: "aggregated",
26396
26546
  label: _t("Aggregate"),
26397
- value: this.props.definition.aggregated,
26547
+ value: this.props.definition.aggregated ?? false,
26398
26548
  onChange: this.onUpdateAggregated.bind(this),
26399
26549
  },
26400
26550
  ];
@@ -27565,15 +27715,15 @@ stores.inject(MyMetaStore, storeInstance);
27565
27715
  // -----------------------------------------------------------------------------
27566
27716
  // We need here the svg of the icons that we need to convert to images for the renderer
27567
27717
  // -----------------------------------------------------------------------------
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>';
27718
+ 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>';
27719
+ 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
27720
  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>';
27721
+ 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
27722
  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>';
27723
+ 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>';
27724
+ 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
27725
  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>';
27726
+ 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
27727
  function loadIconImage(svg) {
27578
27728
  /** We have to add xmlns, as it's not added by owl in the canvas */
27579
27729
  svg = `<svg xmlns="http://www.w3.org/2000/svg" ${svg.slice(4)}`;
@@ -29439,8 +29589,7 @@ stores.inject(MyMetaStore, storeInstance);
29439
29589
  }
29440
29590
  }
29441
29591
 
29442
- const BORDER_COLOR = "#8B008B";
29443
- const BACKGROUND_COLOR = "#8B008B33";
29592
+ const FIND_AND_REPLACE_HIGHLIGHT_COLOR = "#8B008B";
29444
29593
  var Direction;
29445
29594
  (function (Direction) {
29446
29595
  Direction[Direction["previous"] = -1] = "previous";
@@ -29471,14 +29620,14 @@ stores.inject(MyMetaStore, storeInstance);
29471
29620
  super(get);
29472
29621
  this.initialShowFormulaState = this.model.getters.shouldShowFormulas();
29473
29622
  this.searchOptions.searchFormulas = this.initialShowFormulaState;
29623
+ const highlightStore = get(HighlightStore);
29624
+ highlightStore.register(owl.toRaw(this));
29474
29625
  this.onDispose(() => {
29475
29626
  this.model.dispatch("SET_FORMULA_VISIBILITY", { show: this.initialShowFormulaState });
29476
29627
  this.updateSearchContent.stopDebounce();
29628
+ highlightStore.unRegister(owl.toRaw(this));
29477
29629
  });
29478
29630
  }
29479
- get renderingLayers() {
29480
- return ["Search"];
29481
- }
29482
29631
  get searchMatches() {
29483
29632
  switch (this.searchOptions.searchScope) {
29484
29633
  case "allSheets":
@@ -29705,8 +29854,8 @@ stores.inject(MyMetaStore, storeInstance);
29705
29854
  // ---------------------------------------------------------------------------
29706
29855
  // Grid rendering
29707
29856
  // ---------------------------------------------------------------------------
29708
- draw(renderingContext) {
29709
- const { ctx } = renderingContext;
29857
+ get highlights() {
29858
+ const highlights = [];
29710
29859
  const sheetId = this.getters.getActiveSheetId();
29711
29860
  for (const [index, match] of this.searchMatches.entries()) {
29712
29861
  if (match.sheetId !== sheetId) {
@@ -29714,26 +29863,31 @@ stores.inject(MyMetaStore, storeInstance);
29714
29863
  }
29715
29864
  const zone = positionToZone(match);
29716
29865
  const zoneWithMerge = this.getters.expandZone(sheetId, zone);
29717
- const { x, y, width, height } = this.getters.getVisibleRect(zoneWithMerge);
29866
+ const { width, height } = this.getters.getVisibleRect(zoneWithMerge);
29718
29867
  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
- }
29868
+ highlights.push({
29869
+ sheetId,
29870
+ zone: zoneWithMerge,
29871
+ color: FIND_AND_REPLACE_HIGHLIGHT_COLOR,
29872
+ noBorder: index !== this.selectedMatchIndex,
29873
+ thinLine: true,
29874
+ fillAlpha: 0.2,
29875
+ });
29725
29876
  }
29726
29877
  }
29727
- // TODO: use Highlights helpers/stores when the Highlight PR is merged
29728
29878
  if (this.searchOptions.searchScope === "specificRange") {
29729
29879
  const range = this.searchOptions.specificRange;
29730
- if (!range || range.sheetId !== sheetId) {
29731
- return;
29880
+ if (range && range.sheetId === sheetId) {
29881
+ highlights.push({
29882
+ sheetId,
29883
+ zone: range.zone,
29884
+ color: FIND_AND_REPLACE_HIGHLIGHT_COLOR,
29885
+ noFill: true,
29886
+ thinLine: true,
29887
+ });
29732
29888
  }
29733
- const { x, y, width, height } = this.getters.getVisibleRect(range.zone);
29734
- ctx.strokeStyle = BORDER_COLOR;
29735
- ctx.strokeRect(x, y, width, height);
29736
29889
  }
29890
+ return highlights;
29737
29891
  }
29738
29892
  }
29739
29893
 
@@ -30147,6 +30301,12 @@ stores.inject(MyMetaStore, storeInstance);
30147
30301
  "headerRow",
30148
30302
  "totalRow",
30149
30303
  ];
30304
+ /** Return the content zone of the table, ie. the table zone without the headers */
30305
+ function getTableContentZone(tableZone, tableConfig) {
30306
+ const numberOfHeaders = tableConfig.numberOfHeaders;
30307
+ const contentZone = { ...tableZone, top: tableZone.top + numberOfHeaders };
30308
+ return contentZone.top <= contentZone.bottom ? contentZone : undefined;
30309
+ }
30150
30310
  function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
30151
30311
  return {
30152
30312
  borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
@@ -30598,14 +30758,6 @@ stores.inject(MyMetaStore, storeInstance);
30598
30758
  tableXc: this.env.model.getters.getRangeString(this.props.table.range, sheetId),
30599
30759
  filtersEnabledIfPossible: this.props.table.config.hasFilters,
30600
30760
  });
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
30761
  }
30610
30762
  updateHasFilters(hasFilters) {
30611
30763
  this.state.filtersEnabledIfPossible = hasFilters;
@@ -30754,7 +30906,11 @@ stores.inject(MyMetaStore, storeInstance);
30754
30906
  if (!table) {
30755
30907
  return { isOpen: false };
30756
30908
  }
30757
- return { isOpen: true, props: { table } };
30909
+ return {
30910
+ isOpen: true,
30911
+ props: { table },
30912
+ key: table.id,
30913
+ };
30758
30914
  },
30759
30915
  });
30760
30916
 
@@ -32469,19 +32625,21 @@ stores.inject(MyMetaStore, storeInstance);
32469
32625
  slots: Object,
32470
32626
  };
32471
32627
  get iconStyle() {
32472
- const x = this.getIconHorizontalPosition();
32473
- const y = this.getIconVerticalPosition();
32628
+ const cellPosition = this.props.cellPosition;
32629
+ const merge = this.env.model.getters.getMerge(cellPosition);
32630
+ const zone = merge || positionToZone(cellPosition);
32631
+ const rect = this.env.model.getters.getVisibleRectWithoutHeaders(zone);
32632
+ const x = this.getIconHorizontalPosition(rect, cellPosition);
32633
+ const y = this.getIconVerticalPosition(rect, cellPosition);
32474
32634
  return cssPropertiesToCss({
32475
32635
  top: `${y + (this.props.offset?.y || 0)}px`,
32476
32636
  left: `${x + (this.props.offset?.x || 0)}px`,
32477
32637
  });
32478
32638
  }
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);
32639
+ getIconVerticalPosition(rect, cellPosition) {
32640
+ const start = rect.y;
32641
+ const end = rect.y + rect.height;
32642
+ const cell = this.env.model.getters.getCell(cellPosition);
32485
32643
  const align = this.props.verticalAlign || cell?.style?.verticalAlign || DEFAULT_VERTICAL_ALIGN;
32486
32644
  switch (align) {
32487
32645
  case "bottom":
@@ -32493,13 +32651,11 @@ stores.inject(MyMetaStore, storeInstance);
32493
32651
  return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
32494
32652
  }
32495
32653
  }
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);
32654
+ getIconHorizontalPosition(rect, cellPosition) {
32655
+ const start = rect.x;
32656
+ const end = rect.x + rect.width;
32657
+ const cell = this.env.model.getters.getCell(cellPosition);
32658
+ const evaluatedCell = this.env.model.getters.getEvaluatedCell(cellPosition);
32503
32659
  const align = this.props.horizontalAlign || cell?.style?.align || evaluatedCell.defaultAlign;
32504
32660
  switch (align) {
32505
32661
  case "right":
@@ -32604,6 +32760,8 @@ stores.inject(MyMetaStore, storeInstance);
32604
32760
  height: ${CHECKBOX_WIDTH}px;
32605
32761
  accent-color: #808080;
32606
32762
  margin: ${MARGIN}px;
32763
+ /** required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
32764
+ position: absolute;
32607
32765
  }
32608
32766
  `;
32609
32767
  class DataValidationCheckbox extends owl.Component {
@@ -32622,7 +32780,7 @@ stores.inject(MyMetaStore, storeInstance);
32622
32780
  }
32623
32781
  get isDisabled() {
32624
32782
  const cell = this.env.model.getters.getCell(this.props.cellPosition);
32625
- return !!cell?.isFormula;
32783
+ return this.env.model.getters.isReadonly() || !!cell?.isFormula;
32626
32784
  }
32627
32785
  }
32628
32786
 
@@ -32662,12 +32820,17 @@ stores.inject(MyMetaStore, storeInstance);
32662
32820
  static props = {};
32663
32821
  static components = { GridCellIcon, DataValidationCheckbox, DataValidationListIcon };
32664
32822
  get checkBoxCellPositions() {
32665
- return this.env.model.getters.getDataValidationCheckBoxCellPositions();
32823
+ return this.env.model.getters
32824
+ .getVisibleCellPositions()
32825
+ .filter(this.env.model.getters.isCellValidCheckbox);
32666
32826
  }
32667
32827
  get listIconsCellPositions() {
32668
- return this.env.model.getters.isReadonly()
32669
- ? []
32670
- : this.env.model.getters.getDataValidationListCellsPositions();
32828
+ if (this.env.model.getters.isReadonly()) {
32829
+ return [];
32830
+ }
32831
+ return this.env.model.getters
32832
+ .getVisibleCellPositions()
32833
+ .filter(this.env.model.getters.cellHasListDataValidationIcon);
32671
32834
  }
32672
32835
  }
32673
32836
 
@@ -32712,7 +32875,7 @@ stores.inject(MyMetaStore, storeInstance);
32712
32875
  const newY = clip(initialFigure.y + deltaY, minY, maxY - initialFigure.height - scrollY);
32713
32876
  return { ...initialFigure, x: newX, y: newY };
32714
32877
  }
32715
- function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize) {
32878
+ function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }, { x: mouseInitialX, y: mouseInitialY }, keepRatio, minFigSize, { scrollX, scrollY }) {
32716
32879
  let { x, y, width, height } = initialFigure;
32717
32880
  if (keepRatio && dirX != 0 && dirY != 0) {
32718
32881
  const deltaX = Math.min(dirX * (mouseInitialX - mouseX), initialFigure.width - minFigSize);
@@ -32739,14 +32902,14 @@ stores.inject(MyMetaStore, storeInstance);
32739
32902
  y = initialFigure.y - deltaY;
32740
32903
  }
32741
32904
  }
32742
- // Restrict resizing if x or y reaches header boundaries
32743
- if (x < 0) {
32744
- width += x;
32745
- x = 0;
32905
+ // Adjusts figure dimensions to ensure it remains within header boundaries and viewport during resizing.
32906
+ if (x + scrollX <= 0) {
32907
+ width = width + x + scrollX;
32908
+ x = -scrollX;
32746
32909
  }
32747
- if (y < 0) {
32748
- height += y;
32749
- y = 0;
32910
+ if (y + scrollY <= 0) {
32911
+ height = height + y + scrollY;
32912
+ y = -scrollY;
32750
32913
  }
32751
32914
  return { ...initialFigure, x, y, width, height };
32752
32915
  }
@@ -33166,7 +33329,7 @@ stores.inject(MyMetaStore, storeInstance);
33166
33329
  const minFigSize = figureRegistry.get(figure.tag).minFigSize || MIN_FIG_SIZE;
33167
33330
  const onMouseMove = (ev) => {
33168
33331
  const currentMousePosition = { x: ev.clientX, y: ev.clientY };
33169
- const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize);
33332
+ const draggedFigure = dragFigureForResize(initialFig, dirX, dirY, currentMousePosition, initialMousePosition, keepRatio, minFigSize, this.env.model.getters.getActiveSheetScrollInfo());
33170
33333
  const otherFigures = this.getOtherFigures(figure.id);
33171
33334
  const snapResult = snapForResize(this.env.model.getters, dirX, dirY, draggedFigure, otherFigures);
33172
33335
  this.dnd.draggedFigure = snapResult.snappedFigure;
@@ -33342,29 +33505,6 @@ stores.inject(MyMetaStore, storeInstance);
33342
33505
  }
33343
33506
  }
33344
33507
 
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
33508
  const CURSOR_SVG = /*xml*/ `
33369
33509
  <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
33510
  `;
@@ -35249,10 +35389,17 @@ stores.inject(MyMetaStore, storeInstance);
35249
35389
  get panelProps() {
35250
35390
  const state = this.computeState(this.componentTag, this.initialPanelProps);
35251
35391
  if (state.isOpen) {
35252
- return state.props;
35392
+ return state.props ?? {};
35253
35393
  }
35254
35394
  return {};
35255
35395
  }
35396
+ get panelKey() {
35397
+ const state = this.computeState(this.componentTag, this.initialPanelProps);
35398
+ if (state.isOpen) {
35399
+ return state.key;
35400
+ }
35401
+ return undefined;
35402
+ }
35256
35403
  open(componentTag, panelProps = {}) {
35257
35404
  const state = this.computeState(componentTag, panelProps);
35258
35405
  if (state.isOpen === false) {
@@ -35262,7 +35409,7 @@ stores.inject(MyMetaStore, storeInstance);
35262
35409
  this.initialPanelProps?.onCloseSidePanel?.();
35263
35410
  }
35264
35411
  this.componentTag = componentTag;
35265
- this.initialPanelProps = state.props;
35412
+ this.initialPanelProps = state.props ?? {};
35266
35413
  }
35267
35414
  toggle(componentTag, panelProps) {
35268
35415
  if (this.isOpen && componentTag === this.componentTag) {
@@ -40739,10 +40886,8 @@ stores.inject(MyMetaStore, storeInstance);
40739
40886
  "ref", // a function to access a certain dependency at a given index
40740
40887
  "range", // same as above, but guarantee that the result is in the form of a range
40741
40888
  "ctx", code.toString());
40742
- functionCache[cacheKey] = {
40743
- // @ts-ignore
40744
- execute: baseFunction,
40745
- };
40889
+ // @ts-ignore
40890
+ functionCache[cacheKey] = baseFunction;
40746
40891
  /**
40747
40892
  * This function compile the function arguments. It is mostly straightforward,
40748
40893
  * except that there is a non trivial transformation in one situation:
@@ -40849,7 +40994,7 @@ stores.inject(MyMetaStore, storeInstance);
40849
40994
  }
40850
40995
  }
40851
40996
  const compiledFormula = {
40852
- execute: functionCache[cacheKey].execute,
40997
+ execute: functionCache[cacheKey],
40853
40998
  dependencies,
40854
40999
  constantValues,
40855
41000
  tokens,
@@ -42334,6 +42479,15 @@ stores.inject(MyMetaStore, storeInstance);
42334
42479
  return "Success" /* CommandResult.Success */;
42335
42480
  }
42336
42481
  }
42482
+ beforeHandle(cmd) {
42483
+ switch (cmd.type) {
42484
+ case "DELETE_SHEET":
42485
+ this.getters.getFigures(cmd.sheetId).forEach((figure) => {
42486
+ this.dispatch("DELETE_FIGURE", { id: figure.id, sheetId: cmd.sheetId });
42487
+ });
42488
+ break;
42489
+ }
42490
+ }
42337
42491
  handle(cmd) {
42338
42492
  switch (cmd.type) {
42339
42493
  case "CREATE_SHEET":
@@ -43820,7 +43974,6 @@ stores.inject(MyMetaStore, storeInstance);
43820
43974
  "getNumberHeaders",
43821
43975
  "getGridLinesVisibility",
43822
43976
  "getNextSheetName",
43823
- "isEmpty",
43824
43977
  "getSheetSize",
43825
43978
  "getSheetZone",
43826
43979
  "getPaneDivisions",
@@ -44192,14 +44345,6 @@ stores.inject(MyMetaStore, storeInstance);
44192
44345
  // ---------------------------------------------------------------------------
44193
44346
  // Row/Col manipulation
44194
44347
  // ---------------------------------------------------------------------------
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
44348
  getCommandZones(cmd) {
44204
44349
  const zones = [];
44205
44350
  if ("zone" in cmd) {
@@ -45003,13 +45148,15 @@ stores.inject(MyMetaStore, storeInstance);
45003
45148
  if (zone.left !== zone.right) {
45004
45149
  throw new Error("Can only define a filter on a single column");
45005
45150
  }
45006
- const filteredZone = { ...zone, top: zone.top + config.numberOfHeaders };
45007
- const filteredRange = this.getters.getRangeFromZone(range.sheetId, filteredZone);
45151
+ const contentZone = getTableContentZone(zone, config);
45152
+ const filteredRange = contentZone
45153
+ ? this.getters.getRangeFromZone(range.sheetId, contentZone)
45154
+ : undefined;
45008
45155
  return {
45009
45156
  id,
45010
45157
  rangeWithHeaders: range,
45011
45158
  col: zone.left,
45012
- filteredRange: filteredZone.top > filteredZone.bottom ? undefined : filteredRange,
45159
+ filteredRange,
45013
45160
  };
45014
45161
  }
45015
45162
  copyTableForSheet(sheetId, table) {
@@ -45618,7 +45765,11 @@ stores.inject(MyMetaStore, storeInstance);
45618
45765
  });
45619
45766
  }
45620
45767
  getParameters() {
45621
- return [this.refFn.bind(this), this.range.bind(this), this.evalContext];
45768
+ return {
45769
+ referenceDenormalizer: this.refFn.bind(this),
45770
+ ensureRange: this.range.bind(this),
45771
+ evalContext: this.evalContext,
45772
+ };
45622
45773
  }
45623
45774
  /**
45624
45775
  * Returns the value of the cell(s) used in reference
@@ -46565,7 +46716,6 @@ stores.inject(MyMetaStore, storeInstance);
46565
46716
  const MAX_ITERATION = 30;
46566
46717
  const ERROR_CYCLE_CELL = createEvaluatedCell(new CircularDependencyError());
46567
46718
  const EMPTY_CELL = createEvaluatedCell({ value: null });
46568
- const EVAL_CONTEXT_INDEX = 2;
46569
46719
  class Evaluator {
46570
46720
  context;
46571
46721
  getters;
@@ -46618,9 +46768,8 @@ stores.inject(MyMetaStore, storeInstance);
46618
46768
  updateCompilationParameters() {
46619
46769
  // rebuild the compilation parameters (with a clean cache)
46620
46770
  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);
46771
+ this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
46772
+ this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
46624
46773
  }
46625
46774
  evaluateCells(positions) {
46626
46775
  const cells = positions.map((p) => this.encoder.encode(p));
@@ -46745,6 +46894,8 @@ stores.inject(MyMetaStore, storeInstance);
46745
46894
  : evaluateLiteral(cell.content, localeFormat);
46746
46895
  }
46747
46896
  catch (e) {
46897
+ e.value = e?.value || CellErrorType.GenericError;
46898
+ e.message = e?.message || implementationErrorMessage;
46748
46899
  return createEvaluatedCell(e);
46749
46900
  }
46750
46901
  finally {
@@ -46967,16 +47118,16 @@ stores.inject(MyMetaStore, storeInstance);
46967
47118
  }
46968
47119
  }
46969
47120
  function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
46970
- compilationParams[EVAL_CONTEXT_INDEX].__originCellXC = lazy(() => {
47121
+ compilationParams.evalContext.__originCellXC = lazy(() => {
46971
47122
  if (!cellId) {
46972
47123
  return undefined;
46973
47124
  }
46974
47125
  // compute the value lazily for performance reasons
46975
- const position = compilationParams[EVAL_CONTEXT_INDEX].getters.getCellPosition(cellId);
47126
+ const position = compilationParams.evalContext.getters.getCellPosition(cellId);
46976
47127
  return toXC(position.col, position.row);
46977
47128
  });
46978
- compilationParams[EVAL_CONTEXT_INDEX].__originSheetId = sheetId;
46979
- return compiledFormula.execute(compiledFormula.dependencies, ...compilationParams);
47129
+ compilationParams.evalContext.__originSheetId = sheetId;
47130
+ return compiledFormula.execute(compiledFormula.dependencies, compilationParams.referenceDenormalizer, compilationParams.ensureRange, compilationParams.evalContext);
46980
47131
  }
46981
47132
 
46982
47133
  //#region
@@ -47083,6 +47234,7 @@ stores.inject(MyMetaStore, storeInstance);
47083
47234
  "getEvaluatedCellsInZone",
47084
47235
  "getSpreadPositionsOf",
47085
47236
  "getArrayFormulaSpreadingOn",
47237
+ "isEmpty",
47086
47238
  ];
47087
47239
  shouldRebuildDependenciesGraph = true;
47088
47240
  evaluator;
@@ -47188,6 +47340,14 @@ stores.inject(MyMetaStore, storeInstance);
47188
47340
  getArrayFormulaSpreadingOn(position) {
47189
47341
  return this.evaluator.getArrayFormulaSpreadingOn(position);
47190
47342
  }
47343
+ /**
47344
+ * Check if a zone only contains empty cells
47345
+ */
47346
+ isEmpty(sheetId, zone) {
47347
+ return positions(zone)
47348
+ .map(({ col, row }) => this.getEvaluatedCell({ sheetId, col, row }))
47349
+ .every((cell) => cell.type === CellValueType.empty);
47350
+ }
47191
47351
  // ---------------------------------------------------------------------------
47192
47352
  // Export
47193
47353
  // ---------------------------------------------------------------------------
@@ -47305,12 +47465,13 @@ stores.inject(MyMetaStore, storeInstance);
47305
47465
  */
47306
47466
  class CustomColorsPlugin extends UIPlugin {
47307
47467
  customColors = {};
47308
- configCustomColors;
47309
- shouldUpdateColors = false;
47468
+ shouldUpdateColors = true;
47310
47469
  static getters = ["getCustomColors"];
47311
47470
  constructor(config) {
47312
47471
  super(config);
47313
- this.configCustomColors = config.customColors;
47472
+ for (const color of config.customColors ?? []) {
47473
+ this.tryToAddColor(color);
47474
+ }
47314
47475
  }
47315
47476
  handle(cmd) {
47316
47477
  switch (cmd.type) {
@@ -47321,31 +47482,29 @@ stores.inject(MyMetaStore, storeInstance);
47321
47482
  case "SET_BORDER":
47322
47483
  case "SET_ZONE_BORDERS":
47323
47484
  case "SET_FORMATTING":
47485
+ case "CREATE_TABLE":
47486
+ case "UPDATE_TABLE":
47324
47487
  this.history.update("shouldUpdateColors", true);
47488
+ break;
47325
47489
  }
47326
47490
  }
47327
47491
  finalize() {
47328
47492
  if (this.shouldUpdateColors) {
47329
47493
  this.history.update("shouldUpdateColors", false);
47330
- for (const color of this.getCustomColors()) {
47494
+ for (const color of this.computeCustomColors()) {
47331
47495
  this.tryToAddColor(color);
47332
47496
  }
47333
47497
  }
47334
47498
  }
47335
47499
  getCustomColors() {
47336
- let usedColors = this.configCustomColors;
47500
+ return sortWithClusters(Object.keys(this.customColors));
47501
+ }
47502
+ computeCustomColors() {
47503
+ let usedColors = [];
47337
47504
  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));
47505
+ usedColors = usedColors.concat(this.getColorsFromCells(sheetId), this.getFormattingColors(sheetId), this.getChartColors(sheetId), this.getTableColors(sheetId));
47506
+ }
47507
+ return [...new Set([...usedColors])];
47349
47508
  }
47350
47509
  getColorsFromCells(sheetId) {
47351
47510
  const cells = Object.values(this.getters.getCells(sheetId));
@@ -47407,7 +47566,43 @@ stores.inject(MyMetaStore, storeInstance);
47407
47566
  }
47408
47567
  return [...chartsColors];
47409
47568
  }
47569
+ getTableColors(sheetId) {
47570
+ const tables = this.getters.getTables(sheetId);
47571
+ return tables.flatMap((table) => {
47572
+ const config = table.config;
47573
+ const style = TABLE_PRESETS[config.styleId];
47574
+ return [
47575
+ this.getTableStyleElementColors(style.wholeTable),
47576
+ config.numberOfHeaders > 0 ? this.getTableStyleElementColors(style.headerRow) : [],
47577
+ config.totalRow ? this.getTableStyleElementColors(style.totalRow) : [],
47578
+ config.bandedColumns ? this.getTableStyleElementColors(style.firstColumnStripe) : [],
47579
+ config.bandedColumns ? this.getTableStyleElementColors(style.secondColumnStripe) : [],
47580
+ config.bandedRows ? this.getTableStyleElementColors(style.firstRowStripe) : [],
47581
+ config.bandedRows ? this.getTableStyleElementColors(style.secondRowStripe) : [],
47582
+ config.firstColumn ? this.getTableStyleElementColors(style.firstColumn) : [],
47583
+ config.lastColumn ? this.getTableStyleElementColors(style.lastColumn) : [],
47584
+ ].flat();
47585
+ });
47586
+ }
47587
+ getTableStyleElementColors(element) {
47588
+ if (!element) {
47589
+ return [];
47590
+ }
47591
+ return [
47592
+ element.style?.fillColor,
47593
+ element.style?.textColor,
47594
+ element.border?.bottom?.color,
47595
+ element.border?.top?.color,
47596
+ element.border?.left?.color,
47597
+ element.border?.right?.color,
47598
+ element.border?.horizontal?.color,
47599
+ element.border?.vertical?.color,
47600
+ ].filter(isDefined$1);
47601
+ }
47410
47602
  tryToAddColor(color) {
47603
+ if (!isColorValid(color)) {
47604
+ return;
47605
+ }
47411
47606
  const formattedColor = toHex(color);
47412
47607
  if (color && !COLOR_PICKER_DEFAULTS.includes(formattedColor)) {
47413
47608
  this.history.update("customColors", formattedColor, true);
@@ -47831,8 +48026,6 @@ stores.inject(MyMetaStore, storeInstance);
47831
48026
  class EvaluationDataValidationPlugin extends UIPlugin {
47832
48027
  static getters = [
47833
48028
  "getDataValidationInvalidCriterionValueMessage",
47834
- "getDataValidationCheckBoxCellPositions",
47835
- "getDataValidationListCellsPositions",
47836
48029
  "getInvalidDataValidationMessage",
47837
48030
  "getValidationResultForCellValue",
47838
48031
  "isCellValidCheckbox",
@@ -47910,19 +48103,6 @@ stores.inject(MyMetaStore, storeInstance);
47910
48103
  const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
47911
48104
  return error ? { error, rule, isValid: false } : VALID_RESULT;
47912
48105
  }
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
48106
  getValidationResultForCell(cellPosition) {
47927
48107
  const { col, row, sheetId } = cellPosition;
47928
48108
  if (!this.validationResults[sheetId]) {
@@ -48379,6 +48559,23 @@ stores.inject(MyMetaStore, storeInstance);
48379
48559
  * autofiller
48380
48560
  */
48381
48561
  autofillAuto() {
48562
+ const activePosition = this.getters.getActivePosition();
48563
+ const table = this.getters.getTable(activePosition);
48564
+ let autofillRow = table ? table.range.zone.bottom : this.getAutofillAutoLastRow();
48565
+ // Stop autofill at the next non-empty cell
48566
+ const selection = this.getters.getSelectedZone();
48567
+ for (let row = selection.bottom + 1; row <= autofillRow; row++) {
48568
+ if (this.getters.getEvaluatedCell({ ...activePosition, row }).type !== CellValueType.empty) {
48569
+ autofillRow = row - 1;
48570
+ break;
48571
+ }
48572
+ }
48573
+ if (autofillRow > selection.bottom) {
48574
+ this.select(activePosition.col, autofillRow);
48575
+ this.autofill(true);
48576
+ }
48577
+ }
48578
+ getAutofillAutoLastRow() {
48382
48579
  const zone = this.getters.getSelectedZone();
48383
48580
  const sheetId = this.getters.getActiveSheetId();
48384
48581
  let col = zone.left;
@@ -48402,10 +48599,7 @@ stores.inject(MyMetaStore, storeInstance);
48402
48599
  }
48403
48600
  }
48404
48601
  }
48405
- if (row !== zone.bottom) {
48406
- this.select(zone.left, row - 1);
48407
- this.autofill(true);
48408
- }
48602
+ return row - 1;
48409
48603
  }
48410
48604
  /**
48411
48605
  * Generate the next cell
@@ -49947,7 +50141,6 @@ stores.inject(MyMetaStore, storeInstance);
49947
50141
  * the search with a new value.
49948
50142
  */
49949
50143
  class FindAndReplacePlugin extends UIPlugin {
49950
- static layers = ["Search"];
49951
50144
  static getters = [];
49952
50145
  handle(cmd) {
49953
50146
  switch (cmd.type) {
@@ -51017,6 +51210,51 @@ stores.inject(MyMetaStore, storeInstance);
51017
51210
  }
51018
51211
  }
51019
51212
 
51213
+ class TableAutofillPlugin extends UIPlugin {
51214
+ handle(cmd) {
51215
+ switch (cmd.type) {
51216
+ case "AUTOFILL_TABLE_COLUMN":
51217
+ const table = this.getters.getTable(cmd);
51218
+ const cell = this.getters.getCell(cmd);
51219
+ if (!table || !table.config.automaticAutofill || !cell?.isFormula)
51220
+ return;
51221
+ const { col, row } = cmd;
51222
+ const tableContentZone = getTableContentZone(table.range.zone, table.config);
51223
+ if (tableContentZone && isInside(col, row, tableContentZone)) {
51224
+ this.autofillTableZone(cmd, tableContentZone);
51225
+ }
51226
+ break;
51227
+ }
51228
+ }
51229
+ autofillTableZone(autofillSource, zone) {
51230
+ if (zone.top === zone.bottom) {
51231
+ return;
51232
+ }
51233
+ const { col, row, sheetId } = autofillSource;
51234
+ for (let r = zone.top; r <= zone.bottom; r++) {
51235
+ if (r === row) {
51236
+ continue;
51237
+ }
51238
+ if (this.getters.getEvaluatedCell({ col, row: r, sheetId }).type !== CellValueType.empty) {
51239
+ return;
51240
+ }
51241
+ }
51242
+ // TODO: seems odd that autofill a table column have side effects on the selection. Autofill commands should be
51243
+ // refactored to take the selection as a parameter
51244
+ const oldSelection = {
51245
+ zone: this.getters.getSelectedZone(),
51246
+ cell: this.getters.getActivePosition(),
51247
+ };
51248
+ this.selection.selectCell(col, row);
51249
+ this.dispatch("AUTOFILL_SELECT", { col, row: zone.bottom });
51250
+ this.dispatch("AUTOFILL");
51251
+ this.selection.selectCell(col, row);
51252
+ this.dispatch("AUTOFILL_SELECT", { col, row: zone.top });
51253
+ this.dispatch("AUTOFILL");
51254
+ this.selection.selectZone(oldSelection);
51255
+ }
51256
+ }
51257
+
51020
51258
  class TableStylePlugin extends UIPlugin {
51021
51259
  static getters = ["getCellTableStyle", "getCellTableBorder"];
51022
51260
  tableStyles = {};
@@ -52884,6 +53122,8 @@ stores.inject(MyMetaStore, storeInstance);
52884
53122
  "getEdgeScrollRow",
52885
53123
  "getVisibleFigures",
52886
53124
  "getVisibleRect",
53125
+ "getVisibleRectWithoutHeaders",
53126
+ "getVisibleCellPositions",
52887
53127
  "getColRowOffsetInViewport",
52888
53128
  "getMainViewportCoordinates",
52889
53129
  "getActiveSheetScrollInfo",
@@ -53129,6 +53369,26 @@ stores.inject(MyMetaStore, storeInstance);
53129
53369
  const viewports = this.getSubViewports(sheetId);
53130
53370
  return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
53131
53371
  }
53372
+ /**
53373
+ * Get the positions of all the cells that are visible in the viewport, taking merges into account.
53374
+ */
53375
+ getVisibleCellPositions() {
53376
+ const visibleCols = this.getSheetViewVisibleCols();
53377
+ const visibleRows = this.getSheetViewVisibleRows();
53378
+ const sheetId = this.getters.getActiveSheetId();
53379
+ const positions = [];
53380
+ for (const col of visibleCols) {
53381
+ for (const row of visibleRows) {
53382
+ const position = { sheetId, col, row };
53383
+ const mainPosition = this.getters.getMainCellPosition(position);
53384
+ if (mainPosition.row !== row || mainPosition.col !== col) {
53385
+ continue;
53386
+ }
53387
+ positions.push(position);
53388
+ }
53389
+ }
53390
+ return positions;
53391
+ }
53132
53392
  /**
53133
53393
  * Return the main viewport maximum size relative to the client size.
53134
53394
  */
@@ -53248,6 +53508,13 @@ stores.inject(MyMetaStore, storeInstance);
53248
53508
  * Computes the coordinates and size to draw the zone on the canvas
53249
53509
  */
53250
53510
  getVisibleRect(zone) {
53511
+ const rect = this.getVisibleRectWithoutHeaders(zone);
53512
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
53513
+ }
53514
+ /**
53515
+ * Computes the coordinates and size to draw the zone without taking the grid offset into account
53516
+ */
53517
+ getVisibleRectWithoutHeaders(zone) {
53251
53518
  const sheetId = this.getters.getActiveSheetId();
53252
53519
  const viewportRects = this.getSubViewports(sheetId)
53253
53520
  .map((viewport) => viewport.getRect(zone))
@@ -53259,12 +53526,7 @@ stores.inject(MyMetaStore, storeInstance);
53259
53526
  const y = Math.min(...viewportRects.map((rect) => rect.y));
53260
53527
  const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
53261
53528
  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
- };
53529
+ return { x, y, width, height };
53268
53530
  }
53269
53531
  /**
53270
53532
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -53518,13 +53780,8 @@ stores.inject(MyMetaStore, storeInstance);
53518
53780
  }
53519
53781
  break;
53520
53782
  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
- }
53783
+ this.headerPositions = {};
53784
+ this.isDirty = true;
53528
53785
  break;
53529
53786
  case "UPDATE_FILTER":
53530
53787
  case "UPDATE_TABLE":
@@ -53647,7 +53904,8 @@ stores.inject(MyMetaStore, storeInstance);
53647
53904
  .add("split_to_columns", SplitToColumnsPlugin)
53648
53905
  .add("collaborative", CollaborativePlugin)
53649
53906
  .add("history", HistoryPlugin)
53650
- .add("data_cleanup", DataCleanupPlugin);
53907
+ .add("data_cleanup", DataCleanupPlugin)
53908
+ .add("table_autofill", TableAutofillPlugin);
53651
53909
  // Plugins which have a state, but which should not be shared in collaborative
53652
53910
  const statefulUIPluginRegistry = new Registry()
53653
53911
  .add("selection", GridSelectionPlugin)
@@ -53990,6 +54248,9 @@ stores.inject(MyMetaStore, storeInstance);
53990
54248
  this.scrollToSheet();
53991
54249
  }
53992
54250
  onDblClick() {
54251
+ if (this.env.model.getters.isReadonly()) {
54252
+ return;
54253
+ }
53993
54254
  this.startEdition();
53994
54255
  }
53995
54256
  onKeyDown(ev) {
@@ -55946,7 +56207,6 @@ stores.inject(MyMetaStore, storeInstance);
55946
56207
  }
55947
56208
  }, () => [this.env.model.getters.getActiveSheetId()]);
55948
56209
  owl.useExternalListener(window, "resize", () => this.render(true));
55949
- owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
55950
56210
  this.bindModelEvents();
55951
56211
  owl.onWillUpdateProps((nextProps) => {
55952
56212
  if (nextProps.model !== this.props.model) {
@@ -59787,9 +60047,9 @@ stores.inject(MyMetaStore, storeInstance);
59787
60047
  exports.tokenize = tokenize;
59788
60048
 
59789
60049
 
59790
- __info__.version = "17.2.0-alpha.6";
59791
- __info__.date = "2024-02-28T12:28:44.114Z";
59792
- __info__.hash = "318a04e";
60050
+ __info__.version = "17.2.0-alpha.8";
60051
+ __info__.date = "2024-03-15T11:01:01.388Z";
60052
+ __info__.hash = "11cf381";
59793
60053
 
59794
60054
 
59795
60055
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);