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