@odoo/o-spreadsheet 17.2.1 → 17.3.0-alpha.1

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.1
7
- * @date 2024-03-25T09:41:58.737Z
8
- * @hash f97284c
6
+ * @version 17.3.0-alpha.1
7
+ * @date 2024-03-25T09:43:36.072Z
8
+ * @hash 4095c41
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';
@@ -4201,6 +4201,10 @@ function organizeZone(zone) {
4201
4201
  function positionToZone(position) {
4202
4202
  return { left: position.col, right: position.col, top: position.row, bottom: position.row };
4203
4203
  }
4204
+ /** Transform a zone into a zone with only its top-left position */
4205
+ function zoneToTopLeft(zone) {
4206
+ return { ...zone, right: zone.left, bottom: zone.top };
4207
+ }
4204
4208
  function isFullRow(zone) {
4205
4209
  return zone.right === undefined;
4206
4210
  }
@@ -4240,6 +4244,16 @@ function getZonesRows(zones) {
4240
4244
  }
4241
4245
  return set;
4242
4246
  }
4247
+ function unionPositionsToZone(positions) {
4248
+ const zone = { top: Infinity, left: Infinity, bottom: -Infinity, right: -Infinity };
4249
+ for (const { col, row } of positions) {
4250
+ zone.top = Math.min(zone.top, row);
4251
+ zone.left = Math.min(zone.left, col);
4252
+ zone.bottom = Math.max(zone.bottom, row);
4253
+ zone.right = Math.max(zone.right, col);
4254
+ }
4255
+ return zone;
4256
+ }
4243
4257
 
4244
4258
  class RangeImpl {
4245
4259
  getSheetSize;
@@ -5569,7 +5583,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
5569
5583
  const spreader = this.getters.getArrayFormulaSpreadingOn(position);
5570
5584
  let cell = this.getters.getCell(position);
5571
5585
  const evaluatedCell = this.getters.getEvaluatedCell(position);
5572
- if (spreader) {
5586
+ if (spreader && !deepEquals(spreader, position)) {
5573
5587
  const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
5574
5588
  const content = isSpreaderCopied
5575
5589
  ? ""
@@ -6216,19 +6230,25 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
6216
6230
  tableCellsInRow.push({});
6217
6231
  continue;
6218
6232
  }
6233
+ const coreTable = this.getters.getCoreTable(position);
6234
+ const tableZone = coreTable?.range.zone;
6219
6235
  // Copy whole table
6220
- if (zones.some((z) => isZoneInside(table.range.zone, z))) {
6221
- copiedTablesIds.add(table.id);
6236
+ if (coreTable && tableZone && zones.some((z) => isZoneInside(tableZone, z))) {
6237
+ copiedTablesIds.add(coreTable.id);
6222
6238
  const values = [];
6223
- for (const col of range(table.range.zone.left, table.range.zone.right + 1)) {
6224
- values.push(this.getters.getFilterHiddenValues({ sheetId, col, row: table.range.zone.top }));
6239
+ for (const col of range(tableZone.left, tableZone.right + 1)) {
6240
+ values.push(this.getters.getFilterHiddenValues({ sheetId, col, row: tableZone.top }));
6225
6241
  }
6226
6242
  tableCellsInRow.push({
6227
- table: { filtersValues: values, range: table.range, config: table.config },
6243
+ table: {
6244
+ range: coreTable.range,
6245
+ config: coreTable.config,
6246
+ type: coreTable.type,
6247
+ },
6228
6248
  });
6229
6249
  }
6230
6250
  // Copy only style of cell
6231
- else {
6251
+ else if (table) {
6232
6252
  tableCellsInRow.push({ style: this.getTableStyleToCopy(position) });
6233
6253
  }
6234
6254
  }
@@ -6299,7 +6319,7 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
6299
6319
  }
6300
6320
  pasteTableCell(sheetId, tableCell, position, options) {
6301
6321
  if (tableCell.table && !options?.pasteOption) {
6302
- const { range: tableRange, filtersValues } = tableCell.table;
6322
+ const { range: tableRange } = tableCell.table;
6303
6323
  const zoneDims = zoneToDimension(tableRange.zone);
6304
6324
  const newTableZone = {
6305
6325
  left: position.col,
@@ -6311,18 +6331,13 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
6311
6331
  sheetId: position.sheetId,
6312
6332
  ranges: [this.getters.getRangeDataFromZone(sheetId, newTableZone)],
6313
6333
  config: tableCell.table.config,
6334
+ tableType: tableCell.table.type,
6314
6335
  });
6315
- for (const i of range(0, filtersValues.length)) {
6316
- this.dispatch("UPDATE_FILTER", {
6317
- sheetId: position.sheetId,
6318
- col: newTableZone.left + i,
6319
- row: newTableZone.top,
6320
- hiddenValues: filtersValues[i],
6321
- });
6322
- }
6323
6336
  }
6324
6337
  // Do not paste table style if we're inside another table
6325
- if (!this.getters.getTable(position)) {
6338
+ // We cannot check for dynamic tables, because at this point the paste can have changed the evaluation, and the
6339
+ // dynamic tables are not yet computed
6340
+ if (!this.getters.getCoreTable(position)) {
6326
6341
  if (tableCell.style?.style && options?.pasteOption !== "asValue") {
6327
6342
  this.dispatch("UPDATE_CELL", { ...position, style: tableCell.style.style });
6328
6343
  }
@@ -7424,9 +7439,11 @@ const TableTerms = {
7424
7439
  bandedColumns: _t("Banded columns"),
7425
7440
  automaticAutofill: _t("Automatically autofill formulas"),
7426
7441
  totalRow: _t("Total row"),
7442
+ isDynamic: _t("Auto-adjust to formula result"),
7427
7443
  },
7428
7444
  Tooltips: {
7429
7445
  filterWithoutHeader: _t("Cannot have filters without a header row"),
7446
+ isDynamic: _t("For tables based on array formulas only"),
7430
7447
  },
7431
7448
  };
7432
7449
 
@@ -22227,6 +22244,7 @@ class LinkDisplay extends Component {
22227
22244
  }
22228
22245
  edit() {
22229
22246
  const { col, row } = this.props.cellPosition;
22247
+ this.env.model.selection.selectCell(col, row);
22230
22248
  this.cellPopovers.open({ col, row }, "LinkEditor");
22231
22249
  }
22232
22250
  unlink() {
@@ -23343,13 +23361,20 @@ const TABLE_PRESETS = {
23343
23361
  * If a single cell is selected, expand the selection to non-empty adjacent cells to create a table.
23344
23362
  */
23345
23363
  function interactiveCreateTable(env, sheetId, tableConfig) {
23346
- const selection = env.model.getters.getSelectedZones();
23347
- if (selection.length === 1 && getZoneArea(selection[0]) === 1) {
23364
+ let target = env.model.getters.getSelectedZones();
23365
+ let isDynamic = env.model.getters.canCreateDynamicTableOnZones(sheetId, target);
23366
+ if (target.length === 1 && !isDynamic && getZoneArea(target[0]) === 1) {
23348
23367
  env.model.selection.selectTableAroundSelection();
23368
+ target = env.model.getters.getSelectedZones();
23369
+ isDynamic = env.model.getters.canCreateDynamicTableOnZones(sheetId, target);
23349
23370
  }
23350
- const target = env.model.getters.getSelectedZones();
23351
23371
  const ranges = target.map((zone) => env.model.getters.getRangeDataFromZone(sheetId, zone));
23352
- const result = env.model.dispatch("CREATE_TABLE", { ranges, sheetId, config: tableConfig });
23372
+ const result = env.model.dispatch("CREATE_TABLE", {
23373
+ ranges,
23374
+ sheetId,
23375
+ config: tableConfig,
23376
+ tableType: isDynamic ? "dynamic" : "static",
23377
+ });
23353
23378
  if (result.isCancelledBecause("TableOverlap" /* CommandResult.TableOverlap */)) {
23354
23379
  env.raiseError(TableTerms.Errors.TableOverlap);
23355
23380
  }
@@ -23797,9 +23822,13 @@ const INSERT_TABLE = (env) => {
23797
23822
  };
23798
23823
  const DELETE_SELECTED_TABLE = (env) => {
23799
23824
  const position = env.model.getters.getActivePosition();
23825
+ const table = env.model.getters.getTable(position);
23826
+ if (!table) {
23827
+ return;
23828
+ }
23800
23829
  env.model.dispatch("REMOVE_TABLE", {
23801
23830
  sheetId: position.sheetId,
23802
- target: [positionToZone(position)],
23831
+ target: [table.range.zone],
23803
23832
  });
23804
23833
  };
23805
23834
  //------------------------------------------------------------------------------
@@ -30307,6 +30336,24 @@ function getTableContentZone(tableZone, tableConfig) {
30307
30336
  const contentZone = { ...tableZone, top: tableZone.top + numberOfHeaders };
30308
30337
  return contentZone.top <= contentZone.bottom ? contentZone : undefined;
30309
30338
  }
30339
+ function getTableTopLeft(table) {
30340
+ const range = table.range;
30341
+ return { row: range.zone.top, col: range.zone.left, sheetId: range.sheetId };
30342
+ }
30343
+ function createFilter(id, range, config, createRange) {
30344
+ const zone = range.zone;
30345
+ if (zone.left !== zone.right) {
30346
+ throw new Error("Can only define a filter on a single column");
30347
+ }
30348
+ const filteredZone = { ...zone, top: zone.top + config.numberOfHeaders };
30349
+ const filteredRange = createRange(range.sheetId, filteredZone);
30350
+ return {
30351
+ id,
30352
+ rangeWithHeaders: range,
30353
+ col: zone.left,
30354
+ filteredRange: filteredZone.top > filteredZone.bottom ? undefined : filteredRange,
30355
+ };
30356
+ }
30310
30357
  function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
30311
30358
  return {
30312
30359
  borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
@@ -30744,6 +30791,11 @@ css /* scss */ `
30744
30791
  color: #ffffff;
30745
30792
  background: #d94b4b;
30746
30793
  }
30794
+
30795
+ .o-info-icon {
30796
+ width: 14px;
30797
+ height: 14px;
30798
+ }
30747
30799
  }
30748
30800
  `;
30749
30801
  class TablePanel extends Component {
@@ -30775,6 +30827,29 @@ class TablePanel extends Component {
30775
30827
  const numberOfHeaders = hasHeaders ? 1 : 0;
30776
30828
  this.updateNumberOfHeaders(numberOfHeaders);
30777
30829
  }
30830
+ updateTableIsDynamic(isDynamic) {
30831
+ const newTableType = isDynamic ? "dynamic" : "forceStatic";
30832
+ if (newTableType === this.props.table.type) {
30833
+ return;
30834
+ }
30835
+ const uiTable = this.env.model.getters.getTable(getTableTopLeft(this.props.table));
30836
+ if (!uiTable) {
30837
+ return;
30838
+ }
30839
+ const sheetId = this.env.model.getters.getActiveSheetId();
30840
+ const result = this.env.model.dispatch("UPDATE_TABLE", {
30841
+ sheetId,
30842
+ zone: this.props.table.range.zone,
30843
+ newTableRange: uiTable.range.rangeData,
30844
+ tableType: newTableType,
30845
+ });
30846
+ const updatedTable = this.env.model.getters.getCoreTable(getTableTopLeft(this.props.table));
30847
+ if (result.isSuccessful && updatedTable) {
30848
+ const newTableRange = updatedTable.range;
30849
+ this.state.tableXc = this.env.model.getters.getRangeString(newTableRange, sheetId);
30850
+ this.state.tableZoneErrors = [];
30851
+ }
30852
+ }
30778
30853
  onChangeNumberOfHeaders(ev) {
30779
30854
  const input = ev.target;
30780
30855
  const numberOfHeaders = parseInt(input.value);
@@ -30798,35 +30873,59 @@ class TablePanel extends Component {
30798
30873
  }
30799
30874
  const sheetId = this.env.model.getters.getActiveSheetId();
30800
30875
  this.state.tableXc = ranges[0];
30876
+ const newTableRange = this.env.model.getters.getRangeFromSheetXC(sheetId, this.state.tableXc);
30801
30877
  this.state.tableZoneErrors = this.env.model.canDispatch("UPDATE_TABLE", {
30802
30878
  sheetId,
30803
30879
  zone: this.props.table.range.zone,
30804
30880
  newTableRange: this.env.model.getters.getRangeDataFromXc(sheetId, this.state.tableXc),
30881
+ tableType: this.getNewTableType(newTableRange.zone),
30805
30882
  }).reasons;
30806
30883
  }
30807
30884
  onRangeConfirmed() {
30808
30885
  const sheetId = this.env.model.getters.getActiveSheetId();
30809
- const newRange = this.env.model.getters.getRangeFromSheetXC(sheetId, this.state.tableXc);
30886
+ let newRange = this.env.model.getters.getRangeFromSheetXC(sheetId, this.state.tableXc);
30887
+ if (getZoneArea(newRange.zone) === 1) {
30888
+ const extendedZone = this.env.model.getters.getContiguousZone(sheetId, newRange.zone);
30889
+ newRange = this.env.model.getters.getRangeFromZone(sheetId, extendedZone);
30890
+ }
30810
30891
  const result = this.env.model.dispatch("UPDATE_TABLE", {
30811
30892
  sheetId,
30812
30893
  zone: this.props.table.range.zone,
30813
30894
  newTableRange: newRange.rangeData,
30895
+ tableType: this.getNewTableType(newRange.zone),
30814
30896
  });
30815
- if (result.isSuccessful) {
30816
- const position = { col: newRange.zone.left, row: newRange.zone.top };
30897
+ const position = { sheetId, col: newRange.zone.left, row: newRange.zone.top };
30898
+ const updatedTable = this.env.model.getters.getCoreTable(position);
30899
+ if (result.isSuccessful && updatedTable) {
30900
+ const newTopLeft = getTableTopLeft(updatedTable);
30817
30901
  this.env.model.selection.selectZone({
30818
- zone: positionToZone(position),
30819
- cell: position,
30902
+ zone: positionToZone(newTopLeft),
30903
+ cell: newTopLeft,
30820
30904
  });
30905
+ const newTableRange = updatedTable.range;
30906
+ this.state.tableXc = this.env.model.getters.getRangeString(newTableRange, sheetId);
30907
+ }
30908
+ else {
30909
+ const oldTableRange = this.props.table.range;
30910
+ this.state.tableXc = this.env.model.getters.getRangeString(oldTableRange, sheetId);
30821
30911
  }
30822
30912
  this.state.tableZoneErrors = [];
30823
- this.state.tableXc = result.isSuccessful
30824
- ? this.state.tableXc
30825
- : this.env.model.getters.getRangeString(this.props.table.range, sheetId);
30826
30913
  }
30827
30914
  deleteTable() {
30828
30915
  const sheetId = this.env.model.getters.getActiveSheetId();
30829
- this.env.model.dispatch("REMOVE_TABLE", { sheetId, target: [this.props.table.range.zone] });
30916
+ this.env.model.dispatch("REMOVE_TABLE", {
30917
+ sheetId,
30918
+ target: [this.props.table.range.zone],
30919
+ });
30920
+ }
30921
+ getNewTableType(newTableZone) {
30922
+ if (this.props.table.type === "forceStatic") {
30923
+ return "forceStatic";
30924
+ }
30925
+ const sheetId = this.env.model.getters.getActiveSheetId();
30926
+ return this.env.model.getters.canCreateDynamicTableOnZones(sheetId, [newTableZone])
30927
+ ? "dynamic"
30928
+ : "static";
30830
30929
  }
30831
30930
  get tableConfig() {
30832
30931
  return this.props.table.config;
@@ -30844,6 +30943,14 @@ class TablePanel extends Component {
30844
30943
  get hasFilterCheckboxTooltip() {
30845
30944
  return this.canHaveFilters ? undefined : TableTerms.Tooltips.filterWithoutHeader;
30846
30945
  }
30946
+ get canBeDynamic() {
30947
+ const sheetId = this.env.model.getters.getActiveSheetId();
30948
+ return (this.props.table.type === "dynamic" ||
30949
+ this.env.model.getters.canCreateDynamicTableOnZones(sheetId, [this.props.table.range.zone]));
30950
+ }
30951
+ get dynamicTableTooltip() {
30952
+ return TableTerms.Tooltips.isDynamic;
30953
+ }
30847
30954
  }
30848
30955
 
30849
30956
  const sidePanelRegistry = new Registry();
@@ -30906,11 +31013,8 @@ sidePanelRegistry.add("TableSidePanel", {
30906
31013
  if (!table) {
30907
31014
  return { isOpen: false };
30908
31015
  }
30909
- return {
30910
- isOpen: true,
30911
- props: { table },
30912
- key: table.id,
30913
- };
31016
+ const coreTable = getters.getCoreTable(getTableTopLeft(table));
31017
+ return { isOpen: true, props: { table: coreTable }, key: table.id };
30914
31018
  },
30915
31019
  });
30916
31020
 
@@ -31855,9 +31959,6 @@ class FunctionDescriptionProvider extends Component {
31855
31959
  this.assistantState.allowCellSelectionBehind = false;
31856
31960
  }, 2000);
31857
31961
  }
31858
- get formulaArgSeparator() {
31859
- return this.env.model.getters.getLocale().formulaArgSeparator + " ";
31860
- }
31861
31962
  }
31862
31963
 
31863
31964
  const functions$2 = functionRegistry.content;
@@ -32755,8 +32856,7 @@ class FilterIconsOverlay extends Component {
32755
32856
  };
32756
32857
  getFilterHeadersPositions() {
32757
32858
  const sheetId = this.env.model.getters.getActiveSheetId();
32758
- const headerPositions = this.env.model.getters.getFilterHeaders(sheetId);
32759
- return headerPositions.map((position) => ({ sheetId, ...position }));
32859
+ return this.env.model.getters.getFilterHeaders(sheetId);
32760
32860
  }
32761
32861
  }
32762
32862
 
@@ -44825,22 +44925,12 @@ class SheetPlugin extends CorePlugin {
44825
44925
  }
44826
44926
 
44827
44927
  class TablePlugin extends CorePlugin {
44828
- static getters = [
44829
- "getFilter",
44830
- "getFilters",
44831
- "getTable",
44832
- "getTables",
44833
- "getTablesInZone",
44834
- "getTablesOverlappingZones",
44835
- "getFilterId",
44836
- "getFilterHeaders",
44837
- "isFilterHeader",
44838
- ];
44928
+ static getters = ["getCoreTable", "getCoreTables"];
44839
44929
  tables = {};
44840
44930
  adaptRanges(applyChange, sheetId) {
44841
44931
  const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
44842
44932
  for (const sheetId of sheetIds) {
44843
- for (const table of this.getTables(sheetId)) {
44933
+ for (const table of this.getCoreTables(sheetId)) {
44844
44934
  this.applyRangeChangeOnTable(sheetId, table, applyChange);
44845
44935
  }
44846
44936
  }
@@ -44856,15 +44946,16 @@ class TablePlugin extends CorePlugin {
44856
44946
  ? "TableOverlap" /* CommandResult.TableOverlap */
44857
44947
  : "Success" /* CommandResult.Success */, (cmd) => this.checkTableConfigUpdateIsValid(cmd.config));
44858
44948
  case "UPDATE_TABLE":
44859
- const updatedTable = this.getTables(cmd.sheetId).find((table) => deepEquals(table.range.zone, cmd.zone));
44949
+ const updatedTable = this.getTableFromZone(cmd.sheetId, cmd.zone);
44860
44950
  if (!updatedTable) {
44861
44951
  return "TableNotFound" /* CommandResult.TableNotFound */;
44862
44952
  }
44863
44953
  return this.checkValidations(cmd, this.checkUpdatedTableZoneIsValid, (cmd) => this.checkTableConfigUpdateIsValid(cmd.config));
44864
44954
  case "ADD_MERGE":
44865
- for (const merge of cmd.target) {
44866
- for (const table of this.getTables(cmd.sheetId)) {
44867
- if (overlap(table.range.zone, merge)) {
44955
+ for (const table of this.getCoreTables(cmd.sheetId)) {
44956
+ const tableZone = table.range.zone;
44957
+ for (const merge of cmd.target) {
44958
+ if (overlap(tableZone, merge)) {
44868
44959
  return "MergeInTable" /* CommandResult.MergeInTable */;
44869
44960
  }
44870
44961
  }
@@ -44886,8 +44977,11 @@ class TablePlugin extends CorePlugin {
44886
44977
  }
44887
44978
  case "DUPLICATE_SHEET": {
44888
44979
  const newTables = {};
44889
- for (const table of this.getTables(cmd.sheetId)) {
44890
- newTables[table.id] = this.copyTableForSheet(cmd.sheetIdTo, table);
44980
+ for (const table of this.getCoreTables(cmd.sheetId)) {
44981
+ newTables[table.id] =
44982
+ table.type === "dynamic"
44983
+ ? this.copyDynamicTableForSheet(cmd.sheetIdTo, table)
44984
+ : this.copyStaticTableForSheet(cmd.sheetIdTo, table);
44891
44985
  }
44892
44986
  this.history.update("tables", cmd.sheetIdTo, newTables);
44893
44987
  break;
@@ -44898,14 +44992,17 @@ class TablePlugin extends CorePlugin {
44898
44992
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
44899
44993
  this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
44900
44994
  const id = this.uuidGenerator.uuidv4();
44901
- const newTable = this.createTable(id, union, cmd.config || DEFAULT_TABLE_CONFIG);
44995
+ const config = cmd.config || DEFAULT_TABLE_CONFIG;
44996
+ const newTable = cmd.tableType === "dynamic"
44997
+ ? this.createDynamicTable(id, union, config)
44998
+ : this.createStaticTable(id, cmd.tableType, union, config);
44902
44999
  this.history.update("tables", cmd.sheetId, newTable.id, newTable);
44903
45000
  break;
44904
45001
  }
44905
45002
  case "REMOVE_TABLE": {
44906
45003
  const tables = {};
44907
- for (const table of this.getTables(cmd.sheetId)) {
44908
- if (cmd.target.every((zone) => !intersection(zone, table.range.zone))) {
45004
+ for (const table of this.getCoreTables(cmd.sheetId)) {
45005
+ if (cmd.target.every((zone) => !intersection(table.range.zone, zone))) {
44909
45006
  tables[table.id] = table;
44910
45007
  }
44911
45008
  }
@@ -44913,23 +45010,15 @@ class TablePlugin extends CorePlugin {
44913
45010
  break;
44914
45011
  }
44915
45012
  case "UPDATE_TABLE": {
44916
- const table = this.getTables(cmd.sheetId).find((table) => deepEquals(table.range.zone, cmd.zone));
44917
- if (table) {
44918
- const newTableRange = cmd.newTableRange
44919
- ? this.getters.getRangeFromRangeData(cmd.newTableRange)
44920
- : undefined;
44921
- if (newTableRange) {
44922
- const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, newTableRange.zone);
44923
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
44924
- }
44925
- const newTable = this.updateTable(table, newTableRange, cmd.config);
44926
- this.history.update("tables", cmd.sheetId, table.id, newTable);
44927
- }
45013
+ this.updateTable(cmd);
44928
45014
  break;
44929
45015
  }
44930
45016
  case "UPDATE_CELL": {
44931
45017
  const sheetId = cmd.sheetId;
44932
- for (const table of this.getTables(sheetId)) {
45018
+ for (const table of this.getCoreTables(sheetId)) {
45019
+ if (table.type === "dynamic") {
45020
+ continue;
45021
+ }
44933
45022
  const direction = this.canUpdateCellCmdExtendTable(cmd, table);
44934
45023
  if (direction === "down") {
44935
45024
  this.extendTableDown(sheetId, table);
@@ -44953,62 +45042,24 @@ class TablePlugin extends CorePlugin {
44953
45042
  }
44954
45043
  }
44955
45044
  }
44956
- getFilters(sheetId) {
44957
- return this.getTables(sheetId)
44958
- .filter((table) => table.config.hasFilters)
44959
- .map((table) => table.filters)
44960
- .flat();
44961
- }
44962
- getTables(sheetId) {
45045
+ getCoreTables(sheetId) {
44963
45046
  return this.tables[sheetId] ? Object.values(this.tables[sheetId]).filter(isDefined$1) : [];
44964
45047
  }
44965
- getFilter(position) {
44966
- const table = this.getTable(position);
44967
- if (!table || !table.config.hasFilters) {
44968
- return undefined;
44969
- }
44970
- return table.filters.find((filter) => filter.col === position.col);
44971
- }
44972
- getFilterId(position) {
44973
- return this.getFilter(position)?.id;
44974
- }
44975
- getTable({ sheetId, col, row }) {
44976
- return this.getTables(sheetId).find((table) => isInside(col, row, table.range.zone));
44977
- }
44978
- /** Get the filter tables that are fully inside the given zone */
44979
- getTablesInZone(sheetId, zone) {
44980
- return this.getTables(sheetId).filter((table) => isZoneInside(table.range.zone, zone));
45048
+ getCoreTable({ sheetId, col, row }) {
45049
+ return this.getCoreTables(sheetId).find((table) => isInside(col, row, table.range.zone));
44981
45050
  }
44982
45051
  getTablesOverlappingZones(sheetId, zones) {
44983
- return this.getTables(sheetId).filter((table) => zones.some((zone) => overlap(table.range.zone, zone)));
44984
- }
44985
- getFilterHeaders(sheetId) {
44986
- const headers = [];
44987
- for (const table of this.getTables(sheetId)) {
44988
- if (!table.config.hasFilters) {
44989
- continue;
44990
- }
44991
- const zone = table.range.zone;
44992
- const row = zone.top;
44993
- for (let col = zone.left; col <= zone.right; col++) {
44994
- headers.push({ col, row });
44995
- }
44996
- }
44997
- return headers;
44998
- }
44999
- isFilterHeader({ sheetId, col, row }) {
45000
- const headers = this.getFilterHeaders(sheetId);
45001
- return headers.some((header) => header.col === col && header.row === row);
45052
+ return this.getCoreTables(sheetId).filter((table) => zones.some((zone) => overlap(table.range.zone, zone)));
45002
45053
  }
45003
45054
  /** Extend a table down one row */
45004
45055
  extendTableDown(sheetId, table) {
45005
45056
  const newRange = this.getters.extendRange(table.range, "ROW", 1);
45006
- this.history.update("tables", sheetId, table.id, this.updateTable(table, newRange));
45057
+ this.history.update("tables", sheetId, table.id, this.updateStaticTable(table, newRange));
45007
45058
  }
45008
45059
  /** Extend a table right one col */
45009
45060
  extendTableRight(sheetId, table) {
45010
45061
  const newRange = this.getters.extendRange(table.range, "COL", 1);
45011
- this.history.update("tables", sheetId, table.id, this.updateTable(table, newRange));
45062
+ this.history.update("tables", sheetId, table.id, this.updateStaticTable(table, newRange));
45012
45063
  }
45013
45064
  /**
45014
45065
  * Check if an UpdateCell command should cause the given table to be extended by one row or col.
@@ -45045,12 +45096,22 @@ class TablePlugin extends CorePlugin {
45045
45096
  const cellContent = this.getters.getCell(cellPosition)?.content;
45046
45097
  if (cellContent ||
45047
45098
  this.getters.isInMerge(cellPosition) ||
45048
- this.getters.getTable(cellPosition)) {
45099
+ this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
45049
45100
  return "none";
45050
45101
  }
45051
45102
  }
45052
45103
  return direction;
45053
45104
  }
45105
+ getTableFromZone(sheetId, zone) {
45106
+ for (const table of this.getCoreTables(sheetId)) {
45107
+ const tableZone = table.range.zone;
45108
+ // Only check top left to match dynamic tables
45109
+ if (tableZone.left === zone.left && tableZone.top === zone.top) {
45110
+ return table;
45111
+ }
45112
+ }
45113
+ return undefined;
45114
+ }
45054
45115
  checkUpdatedTableZoneIsValid(cmd) {
45055
45116
  if (!cmd.newTableRange) {
45056
45117
  return "Success" /* CommandResult.Success */;
@@ -45060,7 +45121,11 @@ class TablePlugin extends CorePlugin {
45060
45121
  if (zoneIsInSheet !== "Success" /* CommandResult.Success */) {
45061
45122
  return zoneIsInSheet;
45062
45123
  }
45063
- const overlappingTables = this.getTablesOverlappingZones(cmd.sheetId, [newTableZone]).filter((table) => !deepEquals(table.range.zone, cmd.zone));
45124
+ const updatedTable = this.getTableFromZone(cmd.sheetId, cmd.zone);
45125
+ if (!updatedTable) {
45126
+ return "TableNotFound" /* CommandResult.TableNotFound */;
45127
+ }
45128
+ const overlappingTables = this.getTablesOverlappingZones(cmd.sheetId, [newTableZone]).filter((table) => table.id !== updatedTable.id);
45064
45129
  return overlappingTables.length ? "TableOverlap" /* CommandResult.TableOverlap */ : "Success" /* CommandResult.Success */;
45065
45130
  }
45066
45131
  checkTableConfigUpdateIsValid(config) {
@@ -45078,7 +45143,7 @@ class TablePlugin extends CorePlugin {
45078
45143
  }
45079
45144
  return "Success" /* CommandResult.Success */;
45080
45145
  }
45081
- createTable(id, tableRange, config, filters) {
45146
+ createStaticTable(id, type, tableRange, config, filters) {
45082
45147
  const zone = tableRange.zone;
45083
45148
  if (!filters) {
45084
45149
  filters = [];
@@ -45093,9 +45158,51 @@ class TablePlugin extends CorePlugin {
45093
45158
  range: tableRange,
45094
45159
  filters,
45095
45160
  config,
45161
+ type,
45162
+ };
45163
+ }
45164
+ createDynamicTable(id, tableRange, config) {
45165
+ const zone = zoneToTopLeft(tableRange.zone);
45166
+ return {
45167
+ id,
45168
+ range: this.getters.getRangeFromZone(tableRange.sheetId, zone),
45169
+ config,
45170
+ type: "dynamic",
45096
45171
  };
45097
45172
  }
45098
- updateTable(table, newRange, configUpdate) {
45173
+ updateTable(cmd) {
45174
+ const table = this.getTableFromZone(cmd.sheetId, cmd.zone);
45175
+ if (!table) {
45176
+ return;
45177
+ }
45178
+ const newTableRange = cmd.newTableRange
45179
+ ? this.getters.getRangeFromRangeData(cmd.newTableRange)
45180
+ : undefined;
45181
+ if (newTableRange) {
45182
+ const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, newTableRange.zone);
45183
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
45184
+ }
45185
+ const range = newTableRange || table.range;
45186
+ const newConfig = this.updateTableConfig(cmd.config, table.config);
45187
+ const newTableType = cmd.tableType ?? table.type;
45188
+ if ((newTableType === "dynamic" && table.type !== "dynamic") ||
45189
+ (newTableType !== "dynamic" && table.type === "dynamic")) {
45190
+ const newTable = newTableType === "dynamic"
45191
+ ? this.createDynamicTable(table.id, range, newConfig)
45192
+ : this.createStaticTable(table.id, newTableType, range, newConfig);
45193
+ this.history.update("tables", cmd.sheetId, table.id, newTable);
45194
+ }
45195
+ else {
45196
+ const updatedTable = table.type === "dynamic"
45197
+ ? this.updateDynamicTable(table, range, newConfig)
45198
+ : this.updateStaticTable(table, range, newConfig, newTableType);
45199
+ this.history.update("tables", cmd.sheetId, table.id, updatedTable);
45200
+ }
45201
+ }
45202
+ updateStaticTable(table, newRange, configUpdate, newTableType = table.type) {
45203
+ if (newTableType === "dynamic") {
45204
+ throw new Error("Cannot use updateStaticTable to update a dynamic table");
45205
+ }
45099
45206
  const tableRange = newRange ? newRange : table.range;
45100
45207
  const tableZone = tableRange.zone;
45101
45208
  const newConfig = this.updateTableConfig(configUpdate, table.config);
@@ -45116,8 +45223,16 @@ class TablePlugin extends CorePlugin {
45116
45223
  range: tableRange,
45117
45224
  config,
45118
45225
  filters: filters.length ? filters : table.filters,
45226
+ type: newTableType,
45119
45227
  };
45120
45228
  }
45229
+ updateDynamicTable(table, newRange, newConfig) {
45230
+ const range = newRange
45231
+ ? this.getters.getRangeFromZone(newRange.sheetId, zoneToTopLeft(newRange.zone))
45232
+ : table.range;
45233
+ const config = newConfig ? newConfig : table.config;
45234
+ return { ...table, range, config };
45235
+ }
45121
45236
  /**
45122
45237
  * Update the old config of a table with the new partial config from an UpdateTable command.
45123
45238
  *
@@ -45139,35 +45254,29 @@ class TablePlugin extends CorePlugin {
45139
45254
  }
45140
45255
  createFilterFromZone(id, sheetId, zone, config) {
45141
45256
  const range = this.getters.getRangeFromZone(sheetId, zone);
45142
- return this.createFilter(id, range, config);
45143
- }
45144
- createFilter(id, range, config) {
45145
- const zone = range.zone;
45146
- if (zone.left !== zone.right) {
45147
- throw new Error("Can only define a filter on a single column");
45148
- }
45149
- const contentZone = getTableContentZone(zone, config);
45150
- const filteredRange = contentZone
45151
- ? this.getters.getRangeFromZone(range.sheetId, contentZone)
45152
- : undefined;
45153
- return {
45154
- id,
45155
- rangeWithHeaders: range,
45156
- col: zone.left,
45157
- filteredRange,
45158
- };
45257
+ return createFilter(id, range, config, this.getters.getRangeFromZone);
45159
45258
  }
45160
- copyTableForSheet(sheetId, table) {
45259
+ copyStaticTableForSheet(sheetId, table) {
45161
45260
  const newRange = this.getters.getRangeFromZone(sheetId, table.range.zone);
45162
45261
  const newFilters = table.filters.map((filter) => {
45163
45262
  const newFilterRange = this.getters.getRangeFromZone(sheetId, filter.rangeWithHeaders.zone);
45164
- return this.createFilter(filter.id, newFilterRange, table.config);
45263
+ return createFilter(filter.id, newFilterRange, table.config, this.getters.getRangeFromZone);
45165
45264
  });
45166
45265
  return {
45167
45266
  id: table.id,
45168
45267
  range: newRange,
45169
45268
  filters: newFilters,
45170
45269
  config: deepCopy(table.config),
45270
+ type: table.type,
45271
+ };
45272
+ }
45273
+ copyDynamicTableForSheet(sheetId, table) {
45274
+ const newRange = this.getters.getRangeFromZone(sheetId, table.range.zone);
45275
+ return {
45276
+ id: table.id,
45277
+ range: newRange,
45278
+ config: deepCopy(table.config),
45279
+ type: "dynamic",
45171
45280
  };
45172
45281
  }
45173
45282
  applyRangeChangeOnTable(sheetId, table, applyChange) {
@@ -45182,6 +45291,11 @@ class TablePlugin extends CorePlugin {
45182
45291
  default:
45183
45292
  newTableRange = tableRangeChange.range;
45184
45293
  }
45294
+ if (table.type === "dynamic") {
45295
+ const newTable = this.updateDynamicTable(table, newTableRange);
45296
+ this.history.update("tables", sheetId, table.id, newTable);
45297
+ return;
45298
+ }
45185
45299
  const filters = [];
45186
45300
  for (const filter of table.filters) {
45187
45301
  const filterRangeChange = applyChange(filter.rangeWithHeaders);
@@ -45193,7 +45307,7 @@ class TablePlugin extends CorePlugin {
45193
45307
  break;
45194
45308
  default:
45195
45309
  const newFilterRange = filterRangeChange.range;
45196
- const newFilter = this.createFilter(filter.id, newFilterRange, table.config);
45310
+ const newFilter = createFilter(filter.id, newFilterRange, table.config, this.getters.getRangeFromZone);
45197
45311
  filters.push(newFilter);
45198
45312
  }
45199
45313
  }
@@ -45208,7 +45322,7 @@ class TablePlugin extends CorePlugin {
45208
45322
  }
45209
45323
  filters.sort((f1, f2) => f1.col - f2.col);
45210
45324
  }
45211
- const newTable = this.createTable(table.id, newTableRange, table.config, filters);
45325
+ const newTable = this.createStaticTable(table.id, table.type, newTableRange, table.config, filters);
45212
45326
  this.history.update("tables", sheetId, table.id, newTable);
45213
45327
  }
45214
45328
  // ---------------------------------------------------------------------------
@@ -45219,16 +45333,20 @@ class TablePlugin extends CorePlugin {
45219
45333
  for (const tableData of sheet.tables || []) {
45220
45334
  const uuid = this.uuidGenerator.uuidv4();
45221
45335
  const tableConfig = tableData.config || DEFAULT_TABLE_CONFIG;
45222
- const tableRange = this.getters.getRangeFromSheetXC(sheet.id, tableData.range);
45223
- const table = this.createTable(uuid, tableRange, tableConfig);
45336
+ const range = this.getters.getRangeFromSheetXC(sheet.id, tableData.range);
45337
+ const tableType = tableData.type || "static";
45338
+ const table = tableType === "dynamic"
45339
+ ? this.createDynamicTable(uuid, range, tableConfig)
45340
+ : this.createStaticTable(uuid, tableType, range, tableConfig);
45224
45341
  this.history.update("tables", sheet.id, table.id, table);
45225
45342
  }
45226
45343
  }
45227
45344
  }
45228
45345
  export(data) {
45229
45346
  for (const sheet of data.sheets) {
45230
- for (const table of this.getTables(sheet.id)) {
45231
- const tableData = { range: zoneToXc(table.range.zone) };
45347
+ for (const table of this.getCoreTables(sheet.id)) {
45348
+ const range = zoneToXc(table.range.zone);
45349
+ const tableData = { range, type: table.type };
45232
45350
  if (!deepEquals(table.config, DEFAULT_TABLE_CONFIG)) {
45233
45351
  tableData.config = table.config;
45234
45352
  }
@@ -46944,14 +47062,18 @@ class Evaluator {
46944
47062
  if (!this.spreadingRelations.isArrayFormula(position)) {
46945
47063
  return [];
46946
47064
  }
46947
- return Array.from(this.spreadingRelations.getArrayResultPositions(position));
47065
+ if (this.evaluatedCells.get(position)?.type === CellValueType.error) {
47066
+ return [position];
47067
+ }
47068
+ const spreadPositions = Array.from(this.spreadingRelations.getArrayResultPositions(position));
47069
+ return [position, ...spreadPositions];
46948
47070
  }
46949
47071
  getEvaluatedPositions() {
46950
47072
  return this.evaluatedCells.keys();
46951
47073
  }
46952
47074
  getArrayFormulaSpreadingOn(position) {
46953
47075
  if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
46954
- return undefined;
47076
+ return this.spreadingRelations.isArrayFormula(position) ? position : undefined;
46955
47077
  }
46956
47078
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
46957
47079
  return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
@@ -47080,6 +47202,7 @@ class Evaluator {
47080
47202
  if (!this.blockedArrayFormulas.has(position)) {
47081
47203
  this.invalidateSpreading(position);
47082
47204
  }
47205
+ this.spreadingRelations.removeNode(position);
47083
47206
  const cell = this.getters.getCell(position);
47084
47207
  if (cell === undefined) {
47085
47208
  return EMPTY_CELL;
@@ -47190,7 +47313,6 @@ class Evaluator {
47190
47313
  this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
47191
47314
  this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
47192
47315
  }
47193
- this.spreadingRelations.removeNode(position);
47194
47316
  }
47195
47317
  // ----------------------------------------------------------
47196
47318
  // COMMON FUNCTIONALITY
@@ -48288,6 +48410,181 @@ class EvaluationDataValidationPlugin extends UIPlugin {
48288
48410
  }
48289
48411
  }
48290
48412
 
48413
+ class DynamicTablesPlugin extends UIPlugin {
48414
+ static getters = [
48415
+ "canCreateDynamicTableOnZones",
48416
+ "doesZonesContainFilter",
48417
+ "getFilter",
48418
+ "getFilters",
48419
+ "getTable",
48420
+ "getTables",
48421
+ "getTablesOverlappingZones",
48422
+ "getFilterId",
48423
+ "getFilterHeaders",
48424
+ "isFilterHeader",
48425
+ ];
48426
+ tables = {};
48427
+ handle(cmd) {
48428
+ if (invalidateEvaluationCommands.has(cmd.type) ||
48429
+ (cmd.type === "UPDATE_CELL" && "content" in cmd) ||
48430
+ cmd.type === "EVALUATE_CELLS") {
48431
+ this.tables = {};
48432
+ return;
48433
+ }
48434
+ switch (cmd.type) {
48435
+ case "CREATE_TABLE":
48436
+ case "REMOVE_TABLE":
48437
+ case "UPDATE_TABLE":
48438
+ case "DELETE_CONTENT":
48439
+ this.tables = {};
48440
+ break;
48441
+ }
48442
+ }
48443
+ finalize() {
48444
+ for (const sheetId of this.getters.getSheetIds()) {
48445
+ if (!this.tables[sheetId]) {
48446
+ this.tables[sheetId] = this.computeTables(sheetId);
48447
+ }
48448
+ }
48449
+ }
48450
+ computeTables(sheetId) {
48451
+ const tables = [];
48452
+ const coreTables = this.getters.getCoreTables(sheetId);
48453
+ // First we create the static tables, so we can use them to compute collision with dynamic tables
48454
+ for (const table of coreTables) {
48455
+ if (table.type === "dynamic")
48456
+ continue;
48457
+ tables.push(table);
48458
+ }
48459
+ const staticTables = [...tables];
48460
+ // Then we create the dynamic tables
48461
+ for (const coreTable of coreTables) {
48462
+ if (coreTable.type !== "dynamic")
48463
+ continue;
48464
+ const table = this.coreTableToTable(sheetId, coreTable);
48465
+ let tableZone = table.range.zone;
48466
+ // Reduce the zone to avoid collision with static tables. Per design, dynamic tables can't overlap with other
48467
+ // dynamic tables, because formulas cannot spread on the same area, so we don't need to check for that.
48468
+ for (const staticTable of staticTables) {
48469
+ if (overlap(tableZone, staticTable.range.zone)) {
48470
+ tableZone = { ...tableZone, right: staticTable.range.zone.left - 1 };
48471
+ }
48472
+ }
48473
+ tables.push({ ...table, range: this.getters.getRangeFromZone(sheetId, tableZone) });
48474
+ }
48475
+ return tables;
48476
+ }
48477
+ getFilters(sheetId) {
48478
+ return this.getTables(sheetId)
48479
+ .filter((table) => table.config.hasFilters)
48480
+ .map((table) => table.filters)
48481
+ .flat();
48482
+ }
48483
+ getTables(sheetId) {
48484
+ return this.tables[sheetId] || [];
48485
+ }
48486
+ getFilter(position) {
48487
+ const table = this.getTable(position);
48488
+ if (!table || !table.config.hasFilters) {
48489
+ return undefined;
48490
+ }
48491
+ return table.filters.find((filter) => filter.col === position.col);
48492
+ }
48493
+ getFilterId(position) {
48494
+ return this.getFilter(position)?.id;
48495
+ }
48496
+ getTable({ sheetId, col, row }) {
48497
+ return this.getTables(sheetId).find((table) => isInside(col, row, table.range.zone));
48498
+ }
48499
+ getTablesOverlappingZones(sheetId, zones) {
48500
+ return this.getTables(sheetId).filter((table) => zones.some((zone) => overlap(table.range.zone, zone)));
48501
+ }
48502
+ doesZonesContainFilter(sheetId, zones) {
48503
+ return this.getTablesOverlappingZones(sheetId, zones).some((table) => table.config.hasFilters);
48504
+ }
48505
+ getFilterHeaders(sheetId) {
48506
+ const headers = [];
48507
+ for (const table of this.getTables(sheetId)) {
48508
+ if (!table.config.hasFilters) {
48509
+ continue;
48510
+ }
48511
+ const zone = table.range.zone;
48512
+ const row = zone.top;
48513
+ for (let col = zone.left; col <= zone.right; col++) {
48514
+ headers.push({ sheetId, col, row });
48515
+ }
48516
+ }
48517
+ return headers;
48518
+ }
48519
+ isFilterHeader({ sheetId, col, row }) {
48520
+ const headers = this.getFilterHeaders(sheetId);
48521
+ return headers.some((header) => header.col === col && header.row === row);
48522
+ }
48523
+ /**
48524
+ * Check if we can create a dynamic table on the given zones.
48525
+ * - The zones must be continuous
48526
+ * - The union of the zones must be either:
48527
+ * - A single cell that contains an array formula
48528
+ * - All the spread cells of a single array formula
48529
+ */
48530
+ canCreateDynamicTableOnZones(sheetId, zones) {
48531
+ if (!areZonesContinuous(zones)) {
48532
+ return false;
48533
+ }
48534
+ const unionZone = union(...zones);
48535
+ const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
48536
+ const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
48537
+ if (!parentSpreadingCell) {
48538
+ return false;
48539
+ }
48540
+ else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
48541
+ return true;
48542
+ }
48543
+ const spreadPositions = this.getters.getSpreadPositionsOf(parentSpreadingCell);
48544
+ return deepEquals(unionZone, unionPositionsToZone(spreadPositions));
48545
+ }
48546
+ coreTableToTable(sheetId, table) {
48547
+ if (table.type !== "dynamic") {
48548
+ return table;
48549
+ }
48550
+ const tableZone = table.range.zone;
48551
+ const tablePosition = { sheetId, col: tableZone.left, row: tableZone.top };
48552
+ const spreadPositions = this.getters.getSpreadPositionsOf(tablePosition);
48553
+ const zone = spreadPositions.length ? unionPositionsToZone(spreadPositions) : table.range.zone;
48554
+ const range = this.getters.getRangeFromZone(sheetId, zone);
48555
+ const filters = this.getDynamicTableFilters(sheetId, table, zone);
48556
+ return { id: table.id, range, filters, config: table.config };
48557
+ }
48558
+ getDynamicTableFilters(sheetId, table, tableZone) {
48559
+ const filters = [];
48560
+ const { top, bottom, left, right } = tableZone;
48561
+ for (let col = left; col <= right; col++) {
48562
+ const tableColIndex = col - left;
48563
+ const zone = { left: col, right: col, top, bottom };
48564
+ const filter = createFilter(this.getDynamicTableFilterId(table.id, tableColIndex), this.getters.getRangeFromZone(sheetId, zone), table.config, this.getters.getRangeFromZone);
48565
+ filters.push(filter);
48566
+ }
48567
+ return filters;
48568
+ }
48569
+ getDynamicTableFilterId(tableId, tableCol) {
48570
+ return tableId + "_" + tableCol;
48571
+ }
48572
+ exportForExcel(data) {
48573
+ for (const sheet of data.sheets) {
48574
+ for (const tableData of sheet.tables) {
48575
+ const zone = toZone(tableData.range);
48576
+ const topLeft = { sheetId: sheet.id, col: zone.left, row: zone.top };
48577
+ const coreTable = this.getters.getCoreTable(topLeft);
48578
+ const table = this.getTable(topLeft);
48579
+ if (coreTable?.type !== "dynamic" || !table) {
48580
+ continue;
48581
+ }
48582
+ tableData.range = zoneToXc(table.range.zone);
48583
+ }
48584
+ }
48585
+ }
48586
+ }
48587
+
48291
48588
  class HeaderSizeUIPlugin extends UIPlugin {
48292
48589
  static getters = ["getRowSize", "getHeaderSize"];
48293
48590
  tallestCellInRow = {};
@@ -51335,10 +51632,11 @@ class TableAutofillPlugin extends UIPlugin {
51335
51632
  handle(cmd) {
51336
51633
  switch (cmd.type) {
51337
51634
  case "AUTOFILL_TABLE_COLUMN":
51338
- const table = this.getters.getTable(cmd);
51635
+ const table = this.getters.getCoreTable(cmd);
51339
51636
  const cell = this.getters.getCell(cmd);
51340
- if (!table || !table.config.automaticAutofill || !cell?.isFormula)
51637
+ if (!table?.config.automaticAutofill || table.type === "dynamic" || !cell?.isFormula) {
51341
51638
  return;
51639
+ }
51342
51640
  const { col, row } = cmd;
51343
51641
  const tableContentZone = getTableContentZone(table.range.zone, table.config);
51344
51642
  if (tableContentZone && isInside(col, row, tableContentZone)) {
@@ -52067,9 +52365,6 @@ class FilterEvaluationPlugin extends UIPlugin {
52067
52365
  case "START":
52068
52366
  for (const sheetId of this.getters.getSheetIds()) {
52069
52367
  this.filterValues[sheetId] = {};
52070
- for (const filter of this.getters.getFilters(sheetId)) {
52071
- this.filterValues[sheetId][filter.id] = [];
52072
- }
52073
52368
  }
52074
52369
  break;
52075
52370
  case "CREATE_SHEET":
@@ -52090,16 +52385,7 @@ class FilterEvaluationPlugin extends UIPlugin {
52090
52385
  this.updateHiddenRows();
52091
52386
  break;
52092
52387
  case "DUPLICATE_SHEET":
52093
- const filterValues = {};
52094
- for (const newFilter of this.getters.getFilters(cmd.sheetIdTo)) {
52095
- const zone = newFilter.rangeWithHeaders.zone;
52096
- filterValues[newFilter.id] = this.getFilterHiddenValues({
52097
- sheetId: cmd.sheetId,
52098
- col: zone.left,
52099
- row: zone.top,
52100
- });
52101
- }
52102
- this.filterValues[cmd.sheetIdTo] = filterValues;
52388
+ this.filterValues[cmd.sheetIdTo] = deepCopy(this.filterValues[cmd.sheetId]);
52103
52389
  break;
52104
52390
  // If we don't handle DELETE_SHEET, on one hand we will have some residual data, on the other hand we keep the data
52105
52391
  // on DELETE_SHEET followed by undo
@@ -54043,8 +54329,9 @@ const coreViewsPluginRegistry = new Registry()
54043
54329
  .add("evaluation_chart", EvaluationChartPlugin)
54044
54330
  .add("evaluation_cf", EvaluationConditionalFormatPlugin)
54045
54331
  .add("row_size", HeaderSizeUIPlugin)
54046
- .add("custom_colors", CustomColorsPlugin)
54047
- .add("data_validation_ui", EvaluationDataValidationPlugin);
54332
+ .add("data_validation_ui", EvaluationDataValidationPlugin)
54333
+ .add("dynamic_tables", DynamicTablesPlugin)
54334
+ .add("custom_colors", CustomColorsPlugin);
54048
54335
 
54049
54336
  const clickableCellRegistry = new Registry();
54050
54337
  clickableCellRegistry.add("link", {
@@ -60134,6 +60421,6 @@ const constants = {
60134
60421
  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 };
60135
60422
 
60136
60423
 
60137
- __info__.version = "17.2.1";
60138
- __info__.date = "2024-03-25T09:41:58.737Z";
60139
- __info__.hash = "f97284c";
60424
+ __info__.version = "17.3.0-alpha.1";
60425
+ __info__.date = "2024-03-25T09:43:36.072Z";
60426
+ __info__.hash = "4095c41";