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