@odoo/o-spreadsheet 17.1.0-alpha.1 → 17.1.0-alpha.3

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.1.0-alpha.1
6
- * @date 2023-11-02T09:35:10.532Z
7
- * @hash 3b1fe3b
5
+ * @version 17.1.0-alpha.3
6
+ * @date 2023-11-16T12:04:09.636Z
7
+ * @hash 2ef5b1a
8
8
  */
9
9
 
10
10
  import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -5966,7 +5966,7 @@ autofillModifiersRegistry
5966
5966
  return { cellData: {} };
5967
5967
  }
5968
5968
  const sheetId = data.sheetId;
5969
- const content = getters.getTranslatedCellFormula(sheetId, x, y, cell.compiledFormula, cell.dependencies);
5969
+ const content = getters.getTranslatedCellFormula(sheetId, x, y, cell.compiledFormula);
5970
5970
  return {
5971
5971
  cellData: {
5972
5972
  border: data.border,
@@ -10718,6 +10718,17 @@ const SELECTION_CONTAINS_FILTER = (env) => {
10718
10718
  const IS_ONLY_ONE_RANGE = (env) => {
10719
10719
  return env.model.getters.getSelectedZones().length === 1;
10720
10720
  };
10721
+ const CAN_INSERT_HEADER = (env, dimension) => {
10722
+ if (!IS_ONLY_ONE_RANGE(env)) {
10723
+ return false;
10724
+ }
10725
+ const activeHeaders = dimension === "COL" ? env.model.getters.getActiveCols() : env.model.getters.getActiveRows();
10726
+ const ortogonalActiveHeaders = dimension === "COL" ? env.model.getters.getActiveRows() : env.model.getters.getActiveCols();
10727
+ const sheetId = env.model.getters.getActiveSheetId();
10728
+ const zone = env.model.getters.getSelectedZone();
10729
+ const allSheetSelected = isEqual(zone, env.model.getters.getSheetZone(sheetId));
10730
+ return isConsecutive(activeHeaders) && (ortogonalActiveHeaders.size === 0 || allSheetSelected);
10731
+ };
10721
10732
 
10722
10733
  const undo = {
10723
10734
  name: _t("Undo"),
@@ -19395,9 +19406,7 @@ const insertRow = {
19395
19406
  const number = getRowsNumber(env);
19396
19407
  return number === 1 ? _t("Insert row") : _t("Insert %s rows", number.toString());
19397
19408
  },
19398
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
19399
- IS_ONLY_ONE_RANGE(env) &&
19400
- env.model.getters.getActiveCols().size === 0,
19409
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
19401
19410
  icon: "o-spreadsheet-Icon.INSERT_ROW",
19402
19411
  };
19403
19412
  const rowInsertRowBefore = {
@@ -19406,9 +19415,7 @@ const rowInsertRowBefore = {
19406
19415
  return number === 1 ? _t("Insert row above") : _t("Insert %s rows above", number.toString());
19407
19416
  },
19408
19417
  execute: INSERT_ROWS_BEFORE_ACTION,
19409
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
19410
- IS_ONLY_ONE_RANGE(env) &&
19411
- env.model.getters.getActiveCols().size === 0,
19418
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
19412
19419
  icon: "o-spreadsheet-Icon.INSERT_ROW_BEFORE",
19413
19420
  };
19414
19421
  const topBarInsertRowsBefore = {
@@ -19439,9 +19446,7 @@ const rowInsertRowsAfter = {
19439
19446
  const number = getRowsNumber(env);
19440
19447
  return number === 1 ? _t("Insert row below") : _t("Insert %s rows below", number.toString());
19441
19448
  },
19442
- isVisible: (env) => isConsecutive(env.model.getters.getActiveRows()) &&
19443
- IS_ONLY_ONE_RANGE(env) &&
19444
- env.model.getters.getActiveCols().size === 0,
19449
+ isVisible: (env) => CAN_INSERT_HEADER(env, "ROW"),
19445
19450
  icon: "o-spreadsheet-Icon.INSERT_ROW_AFTER",
19446
19451
  };
19447
19452
  const topBarInsertRowsAfter = {
@@ -19459,9 +19464,7 @@ const insertCol = {
19459
19464
  const number = getColumnsNumber(env);
19460
19465
  return number === 1 ? _t("Insert column") : _t("Insert %s columns", number.toString());
19461
19466
  },
19462
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
19463
- IS_ONLY_ONE_RANGE(env) &&
19464
- env.model.getters.getActiveRows().size === 0,
19467
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
19465
19468
  icon: "o-spreadsheet-Icon.INSERT_COL",
19466
19469
  };
19467
19470
  const colInsertColsBefore = {
@@ -19472,9 +19475,7 @@ const colInsertColsBefore = {
19472
19475
  : _t("Insert %s columns left", number.toString());
19473
19476
  },
19474
19477
  execute: INSERT_COLUMNS_BEFORE_ACTION,
19475
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
19476
- IS_ONLY_ONE_RANGE(env) &&
19477
- env.model.getters.getActiveRows().size === 0,
19478
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
19478
19479
  icon: "o-spreadsheet-Icon.INSERT_COL_BEFORE",
19479
19480
  };
19480
19481
  const topBarInsertColsBefore = {
@@ -19507,9 +19508,7 @@ const colInsertColsAfter = {
19507
19508
  : _t("Insert %s columns right", number.toString());
19508
19509
  },
19509
19510
  execute: INSERT_COLUMNS_AFTER_ACTION,
19510
- isVisible: (env) => isConsecutive(env.model.getters.getActiveCols()) &&
19511
- IS_ONLY_ONE_RANGE(env) &&
19512
- env.model.getters.getActiveRows().size === 0,
19511
+ isVisible: (env) => CAN_INSERT_HEADER(env, "COL"),
19513
19512
  icon: "o-spreadsheet-Icon.INSERT_COL_AFTER",
19514
19513
  };
19515
19514
  const topBarInsertColsAfter = {
@@ -23850,7 +23849,7 @@ css /* scss */ `
23850
23849
  }
23851
23850
  .o-input-count {
23852
23851
  width: fit-content;
23853
- padding: 4 0 4 4;
23852
+ padding: 4px 0 4px 4px;
23854
23853
  }
23855
23854
  }
23856
23855
  }
@@ -34966,6 +34965,9 @@ const functionCache = {};
34966
34965
  // -----------------------------------------------------------------------------
34967
34966
  function compile(formula) {
34968
34967
  const tokens = rangeTokenize(formula);
34968
+ return compileTokens(tokens);
34969
+ }
34970
+ function compileTokens(tokens) {
34969
34971
  const { dependencies, constantValues } = formulaArguments(tokens);
34970
34972
  const cacheKey = compilationCacheKey(tokens, dependencies, constantValues);
34971
34973
  if (!functionCache[cacheKey]) {
@@ -35332,11 +35334,11 @@ class CellPlugin extends CorePlugin {
35332
35334
  for (const sheet of Object.keys(this.cells)) {
35333
35335
  for (const cell of Object.values(this.cells[sheet] || {})) {
35334
35336
  if (cell.isFormula) {
35335
- for (const range of cell.dependencies) {
35337
+ for (const range of cell.compiledFormula.dependencies) {
35336
35338
  if (!sheetId || range.sheetId === sheetId) {
35337
35339
  const change = applyChange(range);
35338
35340
  if (change.changeType !== "NONE") {
35339
- this.history.update("cells", sheet, cell.id, "dependencies", cell.dependencies.indexOf(range), change.range);
35341
+ this.history.update("cells", sheet, cell.id, "compiledFormula", "dependencies", cell.compiledFormula.dependencies.indexOf(range), change.range);
35340
35342
  }
35341
35343
  }
35342
35344
  }
@@ -35532,10 +35534,10 @@ class CellPlugin extends CorePlugin {
35532
35534
  /*
35533
35535
  * Reconstructs the original formula string based on a normalized form and its dependencies
35534
35536
  */
35535
- getFormulaCellContent(sheetId, cell, dependencies) {
35536
- const ranges = dependencies || cell.dependencies;
35537
+ getFormulaCellContent(sheetId, compiledFormula, dependencies) {
35538
+ const ranges = dependencies || compiledFormula.dependencies;
35537
35539
  let rangeIndex = 0;
35538
- return concat(cell.compiledFormula.tokens.map((token) => {
35540
+ return concat(compiledFormula.tokens.map((token) => {
35539
35541
  if (token.type === "REFERENCE") {
35540
35542
  const range = ranges[rangeIndex++];
35541
35543
  return this.getters.getRangeString(range, sheetId);
@@ -35546,12 +35548,9 @@ class CellPlugin extends CorePlugin {
35546
35548
  /*
35547
35549
  * Constructs a formula string based on an initial formula and a translation vector
35548
35550
  */
35549
- getTranslatedCellFormula(sheetId, offsetX, offsetY, compiledFormula, dependencies) {
35550
- const adaptedDependencies = this.getters.createAdaptedRanges(dependencies, offsetX, offsetY, sheetId);
35551
- return this.getFormulaCellContent(sheetId, {
35552
- compiledFormula,
35553
- dependencies: adaptedDependencies,
35554
- });
35551
+ getTranslatedCellFormula(sheetId, offsetX, offsetY, compiledFormula) {
35552
+ const adaptedDependencies = this.getters.createAdaptedRanges(compiledFormula.dependencies, offsetX, offsetY, sheetId);
35553
+ return this.getFormulaCellContent(sheetId, compiledFormula, adaptedDependencies);
35555
35554
  }
35556
35555
  getCellStyle(position) {
35557
35556
  return this.getters.getCell(position)?.style || {};
@@ -35731,8 +35730,10 @@ class CellPlugin extends CorePlugin {
35731
35730
  style,
35732
35731
  format,
35733
35732
  isFormula: true,
35734
- compiledFormula,
35735
- dependencies: [],
35733
+ compiledFormula: {
35734
+ ...compiledFormula,
35735
+ dependencies: [],
35736
+ },
35736
35737
  };
35737
35738
  }
35738
35739
  /**
@@ -35741,7 +35742,7 @@ class CellPlugin extends CorePlugin {
35741
35742
  */
35742
35743
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
35743
35744
  const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
35744
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getFormulaCellContent.bind(this));
35745
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
35745
35746
  }
35746
35747
  createErrorFormula(id, content, format, style, error) {
35747
35748
  return {
@@ -35751,13 +35752,12 @@ class CellPlugin extends CorePlugin {
35751
35752
  format,
35752
35753
  isFormula: true,
35753
35754
  compiledFormula: {
35754
- dependencies: [],
35755
35755
  tokens: tokenize(content),
35756
+ dependencies: [],
35756
35757
  execute: function () {
35757
35758
  throw error;
35758
35759
  },
35759
35760
  },
35760
- dependencies: [],
35761
35761
  };
35762
35762
  }
35763
35763
  checkCellOutOfSheet(sheetId, col, row) {
@@ -35770,27 +35770,44 @@ class CellPlugin extends CorePlugin {
35770
35770
  }
35771
35771
  class FormulaCellWithDependencies {
35772
35772
  id;
35773
- compiledFormula;
35774
35773
  format;
35775
35774
  style;
35776
- dependencies;
35777
35775
  sheetId;
35778
- getFormulaCellContent;
35776
+ getRangeString;
35779
35777
  isFormula = true;
35780
- constructor(id, compiledFormula, format, style, dependencies, sheetId, getFormulaCellContent) {
35778
+ compiledFormula;
35779
+ constructor(id, compiledFormula, format, style, dependencies, sheetId, getRangeString) {
35781
35780
  this.id = id;
35782
- this.compiledFormula = compiledFormula;
35783
35781
  this.format = format;
35784
35782
  this.style = style;
35785
- this.dependencies = dependencies;
35786
35783
  this.sheetId = sheetId;
35787
- this.getFormulaCellContent = getFormulaCellContent;
35784
+ this.getRangeString = getRangeString;
35785
+ let rangeIndex = 0;
35786
+ const tokens = compiledFormula.tokens.map((token) => {
35787
+ if (token.type === "REFERENCE") {
35788
+ const index = rangeIndex++;
35789
+ return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
35790
+ }
35791
+ return token;
35792
+ });
35793
+ this.compiledFormula = {
35794
+ ...compiledFormula,
35795
+ dependencies,
35796
+ tokens,
35797
+ };
35788
35798
  }
35789
35799
  get content() {
35790
- return this.getFormulaCellContent(this.sheetId, {
35791
- dependencies: this.dependencies,
35792
- compiledFormula: this.compiledFormula,
35793
- });
35800
+ return concat(this.compiledFormula.tokens.map((token) => token.value));
35801
+ }
35802
+ }
35803
+ class RangeReferenceToken {
35804
+ getValue;
35805
+ type = "REFERENCE";
35806
+ constructor(getValue) {
35807
+ this.getValue = getValue;
35808
+ }
35809
+ get value() {
35810
+ return this.getValue();
35794
35811
  }
35795
35812
  }
35796
35813
 
@@ -36935,6 +36952,13 @@ class FiltersPlugin extends CorePlugin {
36935
36952
  }
36936
36953
  onDeleteColumnsRows(cmd) {
36937
36954
  for (const table of this.getFilterTables(cmd.sheetId)) {
36955
+ // Remove the filter tables whose data filter headers are in the removed rows.
36956
+ if (cmd.dimension === "ROW" && cmd.elements.includes(table.zone.top)) {
36957
+ const tables = { ...this.tables[cmd.sheetId] };
36958
+ delete tables[table.id];
36959
+ this.history.update("tables", cmd.sheetId, tables);
36960
+ continue;
36961
+ }
36938
36962
  const zone = reduceZoneOnDeletion(table.zone, cmd.dimension === "COL" ? "left" : "top", cmd.elements);
36939
36963
  if (!zone) {
36940
36964
  const tables = { ...this.tables[cmd.sheetId] };
@@ -39983,6 +40007,13 @@ class Evaluator {
39983
40007
  return (this.evaluatedCells.get(this.encodePosition(position)) ||
39984
40008
  createEvaluatedCell("", { locale: this.getters.getLocale() }));
39985
40009
  }
40010
+ getSpreadPositionsOf(position) {
40011
+ const positionId = this.encodePosition(position);
40012
+ if (!this.spreadingRelations.isArrayFormula(positionId)) {
40013
+ return [];
40014
+ }
40015
+ return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map(this.decodePosition.bind(this));
40016
+ }
39986
40017
  getArrayFormulaSpreadingOn(position) {
39987
40018
  const positionId = this.encodePosition(position);
39988
40019
  const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
@@ -40087,9 +40118,6 @@ class Evaluator {
40087
40118
  }
40088
40119
  }
40089
40120
  setEvaluatedCell(positionId, evaluatedCell) {
40090
- if (this.nextPositionsToUpdate.has(positionId)) {
40091
- this.nextPositionsToUpdate.delete(positionId);
40092
- }
40093
40121
  this.evaluatedCells.set(positionId, evaluatedCell);
40094
40122
  }
40095
40123
  computeCell(positionId) {
@@ -40144,7 +40172,7 @@ class Evaluator {
40144
40172
  const position = this.compilationParams[2].getters.getCellPosition(cellId);
40145
40173
  return toXC(position.col, position.row);
40146
40174
  };
40147
- const formulaReturn = cellData.compiledFormula.execute(cellData.dependencies, ...this.compilationParams);
40175
+ const formulaReturn = cellData.compiledFormula.execute(cellData.compiledFormula.dependencies, ...this.compilationParams);
40148
40176
  if (!isMatrix(formulaReturn)) {
40149
40177
  return createEvaluatedCell(formulaReturn.value, {
40150
40178
  format: cellData.format || formulaReturn.format,
@@ -40244,7 +40272,7 @@ class Evaluator {
40244
40272
  return [];
40245
40273
  }
40246
40274
  const dependencies = [];
40247
- for (const range of cell.dependencies) {
40275
+ for (const range of cell.compiledFormula.dependencies) {
40248
40276
  if (range.invalidSheetName || range.invalidXc) {
40249
40277
  continue;
40250
40278
  }
@@ -40450,6 +40478,7 @@ class EvaluationPlugin extends UIPlugin {
40450
40478
  "getEvaluatedCell",
40451
40479
  "getEvaluatedCells",
40452
40480
  "getEvaluatedCellsInZone",
40481
+ "getSpreadPositionsOf",
40453
40482
  ];
40454
40483
  shouldRebuildDependenciesGraph = true;
40455
40484
  evaluator;
@@ -40559,6 +40588,9 @@ class EvaluationPlugin extends UIPlugin {
40559
40588
  getEvaluatedCellsInZone(sheetId, zone) {
40560
40589
  return positions(zone).map(({ col, row }) => this.getters.getEvaluatedCell({ sheetId, col, row }));
40561
40590
  }
40591
+ getSpreadPositionsOf(position) {
40592
+ return this.evaluator.getSpreadPositionsOf(position);
40593
+ }
40562
40594
  // ---------------------------------------------------------------------------
40563
40595
  // Export
40564
40596
  // ---------------------------------------------------------------------------
@@ -40596,10 +40628,10 @@ class EvaluationPlugin extends UIPlugin {
40596
40628
  */
40597
40629
  getCorrespondingFormulaCell(position) {
40598
40630
  const cell = this.getters.getCell(position);
40599
- if (cell && cell.content) {
40600
- if (cell.isFormula && !isBadExpression(cell.content)) {
40601
- return cell;
40602
- }
40631
+ if (cell && cell.isFormula) {
40632
+ return isBadExpression(cell.compiledFormula.tokens) ? undefined : cell;
40633
+ }
40634
+ else if (cell && cell.content) {
40603
40635
  return undefined;
40604
40636
  }
40605
40637
  const spreadingFormulaPosition = this.evaluator.getArrayFormulaSpreadingOn(position);
@@ -40616,9 +40648,9 @@ class EvaluationPlugin extends UIPlugin {
40616
40648
  return buildCompilationParameters(this.config.custom, this.getters, (position) => this.evaluator.getEvaluatedCell(position));
40617
40649
  }
40618
40650
  }
40619
- function isBadExpression(formula) {
40651
+ function isBadExpression(tokens) {
40620
40652
  try {
40621
- compile(formula);
40653
+ compileTokens(tokens);
40622
40654
  return false;
40623
40655
  }
40624
40656
  catch (error) {
@@ -40787,22 +40819,20 @@ class EvaluationChartPlugin extends UIPlugin {
40787
40819
  invalidateCFEvaluationCommands.has(cmd.type) ||
40788
40820
  cmd.type === "EVALUATE_CELLS" ||
40789
40821
  cmd.type === "UPDATE_CELL") {
40790
- if (cmd.type !== "UNDO" && cmd.type !== "REDO") {
40791
- for (const chartId in this.charts) {
40792
- this.history.update("charts", chartId, undefined);
40793
- }
40822
+ for (const chartId in this.charts) {
40823
+ this.charts[chartId] = undefined;
40794
40824
  }
40795
40825
  }
40796
40826
  switch (cmd.type) {
40797
40827
  case "UPDATE_CHART":
40798
40828
  case "CREATE_CHART":
40799
40829
  case "DELETE_FIGURE":
40800
- this.history.update("charts", cmd.id, undefined);
40830
+ this.charts[cmd.id] = undefined;
40801
40831
  break;
40802
40832
  case "DELETE_SHEET":
40803
40833
  for (let chartId in this.charts) {
40804
40834
  if (!this.getters.isChartDefined(chartId)) {
40805
- this.history.update("charts", chartId, undefined);
40835
+ this.charts[chartId] = undefined;
40806
40836
  }
40807
40837
  }
40808
40838
  break;
@@ -40814,8 +40844,7 @@ class EvaluationChartPlugin extends UIPlugin {
40814
40844
  if (!chart) {
40815
40845
  throw new Error(`No chart for the given id: ${figureId}`);
40816
40846
  }
40817
- const runtime = this.createRuntimeChart(chart);
40818
- this.history.update("charts", figureId, runtime);
40847
+ this.charts[figureId] = this.createRuntimeChart(chart);
40819
40848
  }
40820
40849
  return this.charts[figureId];
40821
40850
  }
@@ -40967,7 +40996,10 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
40967
40996
  const values = cf.rule.values.map((value, i) => {
40968
40997
  const compiledFormula = formulas[i];
40969
40998
  if (compiledFormula) {
40970
- return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, compiledFormula, compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
40999
+ return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, {
41000
+ ...compiledFormula,
41001
+ dependencies: compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
41002
+ });
40971
41003
  }
40972
41004
  return value;
40973
41005
  });
@@ -41339,7 +41371,10 @@ class EvaluationDataValidationPlugin extends UIPlugin {
41339
41371
  }
41340
41372
  try {
41341
41373
  const formula = compile(value);
41342
- const translatedFormula = this.getters.getTranslatedCellFormula(sheetId, offset.col, offset.row, formula, formula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
41374
+ const translatedFormula = this.getters.getTranslatedCellFormula(sheetId, offset.col, offset.row, {
41375
+ ...formula,
41376
+ dependencies: formula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)),
41377
+ });
41343
41378
  const evaluated = this.getters.evaluateFormula(sheetId, translatedFormula);
41344
41379
  return evaluated && !isMatrix(evaluated) ? evaluated.toString() : "";
41345
41380
  }
@@ -43593,7 +43628,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
43593
43628
  }
43594
43629
  let content = origin.cell.content;
43595
43630
  if (origin.cell.isFormula && operation === "COPY") {
43596
- content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula, origin.cell.dependencies);
43631
+ content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula);
43597
43632
  }
43598
43633
  this.dispatch("UPDATE_CELL", {
43599
43634
  ...target,
@@ -44125,20 +44160,29 @@ class FindAndReplacePlugin extends UIPlugin {
44125
44160
  const sheetId = this.getters.getActiveSheetId();
44126
44161
  const cells = this.getters.getCells(sheetId);
44127
44162
  const matches = [];
44128
- if (this.toSearch) {
44163
+ if (this.toSearch && this.currentSearchRegex) {
44129
44164
  for (const cell of Object.values(cells)) {
44130
44165
  const { col, row } = this.getters.getCellPosition(cell.id);
44166
+ const cellPosition = { sheetId, col, row };
44131
44167
  const isColHidden = this.getters.isColHidden(sheetId, col);
44132
44168
  const isRowHidden = this.getters.isRowHidden(sheetId, row);
44133
44169
  if (isColHidden || isRowHidden) {
44134
44170
  continue;
44135
44171
  }
44136
- if (cell &&
44137
- this.currentSearchRegex &&
44138
- this.currentSearchRegex.test(this.getSearchableString({ sheetId, col, row }))) {
44172
+ if (this.currentSearchRegex.test(this.getSearchableString(cellPosition))) {
44139
44173
  const match = { col, row, selected: false };
44140
44174
  matches.push(match);
44141
44175
  }
44176
+ for (const spreadPosition of this.getters.getSpreadPositionsOf(cellPosition)) {
44177
+ if (this.currentSearchRegex.test(this.getSearchableString(spreadPosition))) {
44178
+ const match = {
44179
+ col: spreadPosition.col,
44180
+ row: spreadPosition.row,
44181
+ selected: false,
44182
+ };
44183
+ matches.push(match);
44184
+ }
44185
+ }
44142
44186
  }
44143
44187
  }
44144
44188
  return matches.sort(this.sortByRowThenColumn);
@@ -44199,16 +44243,19 @@ class FindAndReplacePlugin extends UIPlugin {
44199
44243
  return;
44200
44244
  }
44201
44245
  const sheetId = this.getters.getActiveSheetId();
44202
- const cell = this.getters.getCell({ sheetId, ...selectedMatch });
44203
- const { col, row } = selectedMatch;
44246
+ const position = { sheetId, ...selectedMatch };
44247
+ const cell = this.getters.getCell(position);
44248
+ if (!cell?.content) {
44249
+ return;
44250
+ }
44204
44251
  if (cell?.isFormula && !this.searchOptions.searchFormulas) {
44205
44252
  return;
44206
44253
  }
44207
44254
  const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
44208
- const toReplace = this.getSearchableString({ sheetId, col, row });
44255
+ const toReplace = this.getSearchableString(position);
44209
44256
  const content = toReplace.replace(replaceRegex, replaceWith);
44210
44257
  const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
44211
- this.dispatch("UPDATE_CELL", { sheetId, col, row, content: canonicalContent });
44258
+ this.dispatch("UPDATE_CELL", { ...position, content: canonicalContent });
44212
44259
  }
44213
44260
  /**
44214
44261
  * Replace the value of the currently selected match
@@ -45537,7 +45584,6 @@ class SelectionInputsManagerPlugin extends UIPlugin {
45537
45584
  }
45538
45585
 
45539
45586
  class SortPlugin extends UIPlugin {
45540
- static getters = ["getContiguousZone"];
45541
45587
  allowDispatch(cmd) {
45542
45588
  switch (cmd.type) {
45543
45589
  case "SORT_CELLS":
@@ -45585,143 +45631,6 @@ class SortPlugin extends UIPlugin {
45585
45631
  }
45586
45632
  return "Success" /* CommandResult.Success */;
45587
45633
  }
45588
- // getContiguousZone helpers
45589
- /**
45590
- * safe-version of expandZone to make sure we don't get out of the grid
45591
- */
45592
- expand(sheetId, z) {
45593
- const { left, right, top, bottom } = this.getters.expandZone(sheetId, z);
45594
- return {
45595
- left: Math.max(0, left),
45596
- right: Math.min(this.getters.getNumberCols(sheetId) - 1, right),
45597
- top: Math.max(0, top),
45598
- bottom: Math.min(this.getters.getNumberRows(sheetId) - 1, bottom),
45599
- };
45600
- }
45601
- /**
45602
- * verifies the presence of at least one non-empty cell in the given zone
45603
- */
45604
- checkExpandedValues(sheetId, z) {
45605
- const expandedZone = this.expand(sheetId, z);
45606
- let cell;
45607
- if (this.getters.doesIntersectMerge(sheetId, expandedZone)) {
45608
- const { left, right, top, bottom } = expandedZone;
45609
- for (let c = left; c <= right; c++) {
45610
- for (let r = top; r <= bottom; r++) {
45611
- const { col, row } = this.getters.getMainCellPosition({ sheetId, col: c, row: r });
45612
- cell = this.getters.getEvaluatedCell({ sheetId, col, row });
45613
- if (cell.formattedValue) {
45614
- return true;
45615
- }
45616
- }
45617
- }
45618
- }
45619
- else {
45620
- for (let cell of this.getters.getEvaluatedCellsInZone(sheetId, expandedZone)) {
45621
- if (cell.formattedValue) {
45622
- return true;
45623
- }
45624
- }
45625
- }
45626
- return false;
45627
- }
45628
- /**
45629
- * This function will expand the provided zone in directions (top, bottom, left, right) for which there
45630
- * are non-null cells on the external boundary of the zone in the given direction.
45631
- *
45632
- * Example:
45633
- * A B C D E
45634
- * ___ ___ ___ ___ ___
45635
- * 1 | | D | | | |
45636
- * ___ ___ ___ ___ ___
45637
- * 2 | 5 | | 1 | D | |
45638
- * ___ ___ ___ ___ ___
45639
- * 3 | | | A | X | |
45640
- * ___ ___ ___ ___ ___
45641
- * 4 | | | | | |
45642
- * ___ ___ ___ ___ ___
45643
- *
45644
- * Let's consider a provided zone corresponding to (C2:D3) - (left:2, right: 3, top:1, bottom:2)
45645
- * - the top external boundary is (B1:E1)
45646
- * Since we have B1='D' != "", we expand to the top: => (C1:D3)
45647
- * The top boundary having reached the top of the grid, we cannot expand in that direction anymore
45648
- *
45649
- * - the left boundary is (B1:B4)
45650
- * since we have B1 again, we expand to the left => (B1:D3)
45651
- *
45652
- * - the right and bottom boundaries are a dead end for now as (E1:E4) and (A4:E4) are empty.
45653
- *
45654
- * - the left boundary is now (A1:A4)
45655
- * Since we have A2=5 != "", we can therefore expand to the left => (A1:D3)
45656
- *
45657
- * This will be the final zone as left and top have reached the boundaries of the grid and
45658
- * the other boundaries (E1:E4) and (A4:E4) are empty.
45659
- *
45660
- * @param sheetId UID of concerned sheet
45661
- * @param zone Zone
45662
- *
45663
- */
45664
- getContiguousZone(sheetId, zone) {
45665
- let { top, bottom, left, right } = zone;
45666
- let canExpand;
45667
- let stop = false;
45668
- while (!stop) {
45669
- stop = true;
45670
- /** top row external boundary */
45671
- if (top > 0) {
45672
- canExpand = this.checkExpandedValues(sheetId, {
45673
- left: left - 1,
45674
- right: right + 1,
45675
- top: top - 1,
45676
- bottom: top - 1,
45677
- });
45678
- if (canExpand) {
45679
- stop = false;
45680
- top--;
45681
- }
45682
- }
45683
- /** left column external boundary */
45684
- if (left > 0) {
45685
- canExpand = this.checkExpandedValues(sheetId, {
45686
- left: left - 1,
45687
- right: left - 1,
45688
- top: top - 1,
45689
- bottom: bottom + 1,
45690
- });
45691
- if (canExpand) {
45692
- stop = false;
45693
- left--;
45694
- }
45695
- }
45696
- /** right column external boundary */
45697
- if (right < this.getters.getNumberCols(sheetId) - 1) {
45698
- canExpand = this.checkExpandedValues(sheetId, {
45699
- left: right + 1,
45700
- right: right + 1,
45701
- top: top - 1,
45702
- bottom: bottom + 1,
45703
- });
45704
- if (canExpand) {
45705
- stop = false;
45706
- right++;
45707
- }
45708
- }
45709
- /** bottom row external boundary */
45710
- if (bottom < this.getters.getNumberRows(sheetId) - 1) {
45711
- canExpand = this.checkExpandedValues(sheetId, {
45712
- left: left - 1,
45713
- right: right + 1,
45714
- top: bottom + 1,
45715
- bottom: bottom + 1,
45716
- });
45717
- if (canExpand) {
45718
- stop = false;
45719
- bottom++;
45720
- }
45721
- }
45722
- }
45723
- return { left, right, top, bottom };
45724
- }
45725
45634
  /**
45726
45635
  * This function evaluates if the top row of a provided zone can be considered as a `header`
45727
45636
  * by checking the following criteria:
@@ -45786,7 +45695,7 @@ class SortPlugin extends UIPlugin {
45786
45695
  if (cell.isFormula) {
45787
45696
  const position = this.getters.getCellPosition(cell.id);
45788
45697
  // we only have a vertical offset
45789
- content = this.getters.getTranslatedCellFormula(sheetId, 0, newRow - position.row, cell.compiledFormula, cell.dependencies);
45698
+ content = this.getters.getTranslatedCellFormula(sheetId, 0, newRow - position.row, cell.compiledFormula);
45790
45699
  }
45791
45700
  newCellValues.style = cell.style;
45792
45701
  newCellValues.content = content;
@@ -45855,6 +45764,8 @@ class SheetUIPlugin extends UIPlugin {
45855
45764
  "getTextWidth",
45856
45765
  "getCellText",
45857
45766
  "getCellMultiLineText",
45767
+ "getContiguousZone",
45768
+ "isCellEmpty",
45858
45769
  ];
45859
45770
  ctx = document.createElement("canvas").getContext("2d");
45860
45771
  // ---------------------------------------------------------------------------
@@ -45944,9 +45855,52 @@ class SheetUIPlugin extends UIPlugin {
45944
45855
  const hasListIcon = !this.getters.isReadonly() && this.getters.cellHasListDataValidationIcon(position);
45945
45856
  return isFilterHeader || hasListIcon;
45946
45857
  }
45947
- // ---------------------------------------------------------------------------
45948
- // Grid manipulation
45949
- // ---------------------------------------------------------------------------
45858
+ /**
45859
+ * Expands the given zone until bordered by empty cells or reached the sheet boundaries.
45860
+ */
45861
+ getContiguousZone(sheetId, zoneToExpand) {
45862
+ /** Try to expand the zone by one col/row in any direction to include a new non-empty cell */
45863
+ const expandZone = (zone) => {
45864
+ for (const col of range(zone.left, zone.right + 1)) {
45865
+ if (!this.isCellEmpty({ sheetId, col, row: zone.top - 1 })) {
45866
+ return { ...zone, top: zone.top - 1 };
45867
+ }
45868
+ if (!this.isCellEmpty({ sheetId, col, row: zone.bottom + 1 })) {
45869
+ return { ...zone, bottom: zone.bottom + 1 };
45870
+ }
45871
+ }
45872
+ for (const row of range(zone.top, zone.bottom + 1)) {
45873
+ if (!this.isCellEmpty({ sheetId, col: zone.left - 1, row })) {
45874
+ return { ...zone, left: zone.left - 1 };
45875
+ }
45876
+ if (!this.isCellEmpty({ sheetId, col: zone.right + 1, row })) {
45877
+ return { ...zone, right: zone.right + 1 };
45878
+ }
45879
+ }
45880
+ return zone;
45881
+ };
45882
+ let hasExpanded = false;
45883
+ let zone = zoneToExpand;
45884
+ do {
45885
+ hasExpanded = false;
45886
+ const newZone = expandZone(zone);
45887
+ if (!isEqual(zone, newZone)) {
45888
+ hasExpanded = true;
45889
+ zone = newZone;
45890
+ continue;
45891
+ }
45892
+ } while (hasExpanded);
45893
+ return zone;
45894
+ }
45895
+ /**
45896
+ * Check if a cell is empty. If the cell is part of a merge,
45897
+ * check if the merge containing the cell is empty.
45898
+ */
45899
+ isCellEmpty(position) {
45900
+ const mainPosition = this.getters.getMainCellPosition(position);
45901
+ const cell = this.getters.getEvaluatedCell(mainPosition);
45902
+ return cell.type === CellValueType.empty;
45903
+ }
45950
45904
  getColMaxWidth(sheetId, index) {
45951
45905
  const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
45952
45906
  const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
@@ -47851,6 +47805,8 @@ class FilterEvaluationPlugin extends UIPlugin {
47851
47805
  case "EVALUATE_CELLS":
47852
47806
  case "ACTIVATE_SHEET":
47853
47807
  case "REMOVE_FILTER_TABLE":
47808
+ case "ADD_COLUMNS_ROWS":
47809
+ case "REMOVE_COLUMNS_ROWS":
47854
47810
  this.isEvaluationDirty = true;
47855
47811
  break;
47856
47812
  case "START":
@@ -53369,7 +53325,7 @@ class SelectionStreamProcessorImpl {
53369
53325
  scrollIntoView: false,
53370
53326
  });
53371
53327
  }
53372
- const tableZone = this.expandZoneToTable(anchor.zone);
53328
+ const tableZone = this.getters.getContiguousZone(sheetId, anchor.zone);
53373
53329
  return !deepEquals(tableZone, anchor.zone)
53374
53330
  ? this.modifyAnchor({ ...anchor, zone: tableZone }, "updateAnchor", {
53375
53331
  scrollIntoView: false,
@@ -53382,7 +53338,8 @@ class SelectionStreamProcessorImpl {
53382
53338
  * cells bordering it
53383
53339
  */
53384
53340
  selectTableAroundSelection() {
53385
- const tableZone = this.expandZoneToTable(this.anchor.zone);
53341
+ const sheetId = this.getters.getActiveSheetId();
53342
+ const tableZone = this.getters.getContiguousZone(sheetId, this.anchor.zone);
53386
53343
  return this.modifyAnchor({ ...this.anchor, zone: tableZone }, "updateAnchor", {
53387
53344
  scrollIntoView: false,
53388
53345
  });
@@ -53559,11 +53516,12 @@ class SelectionStreamProcessorImpl {
53559
53516
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
53560
53517
  */
53561
53518
  getEndOfCluster(startPosition, dim, dir) {
53562
- const sheet = this.getters.getActiveSheet();
53519
+ const sheetId = this.getters.getActiveSheetId();
53563
53520
  let currentPosition = startPosition;
53564
53521
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
53565
53522
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
53566
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
53523
+ let mode = !this.getters.isCellEmpty({ ...currentPosition, sheetId }) &&
53524
+ !this.getters.isCellEmpty({ ...nextCellPosition, sheetId })
53567
53525
  ? "endOfCluster"
53568
53526
  : "nextCluster";
53569
53527
  while (true) {
@@ -53573,7 +53531,7 @@ class SelectionStreamProcessorImpl {
53573
53531
  currentPosition.row === nextCellPosition.row) {
53574
53532
  break;
53575
53533
  }
53576
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
53534
+ const isNextCellEmpty = this.getters.isCellEmpty({ ...nextCellPosition, sheetId });
53577
53535
  if (mode === "endOfCluster" && isNextCellEmpty) {
53578
53536
  break;
53579
53537
  }
@@ -53586,15 +53544,6 @@ class SelectionStreamProcessorImpl {
53586
53544
  }
53587
53545
  return dim === "cols" ? currentPosition.col : currentPosition.row;
53588
53546
  }
53589
- /**
53590
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
53591
- * check if the merge containing the cell is empty.
53592
- */
53593
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
53594
- const position = this.getters.getMainCellPosition({ sheetId, col, row });
53595
- const cell = this.getters.getEvaluatedCell(position);
53596
- return cell.type === CellValueType.empty;
53597
- }
53598
53547
  /** Computes the next cell position in the given direction by crossing through merges and skipping hidden cells.
53599
53548
  *
53600
53549
  * This has the same behaviour as getNextAvailablePosition() for certain arguments, but use this method instead
@@ -53613,45 +53562,6 @@ class SelectionStreamProcessorImpl {
53613
53562
  getPosition() {
53614
53563
  return { ...this.anchor.cell };
53615
53564
  }
53616
- /**
53617
- * Expand the given zone to a table.
53618
- * We define a table by the smallest zone that contain the anchor and that have only empty
53619
- * cells bordering it
53620
- */
53621
- expandZoneToTable(zoneToExpand) {
53622
- /** Try to expand the zone by one col/row in any direction to include a new non-empty cell */
53623
- const expandZone = (zone) => {
53624
- for (const col of range(zone.left, zone.right + 1)) {
53625
- if (!this.isCellEmpty({ col, row: zone.top - 1 })) {
53626
- return { ...zone, top: zone.top - 1 };
53627
- }
53628
- if (!this.isCellEmpty({ col, row: zone.bottom + 1 })) {
53629
- return { ...zone, bottom: zone.bottom + 1 };
53630
- }
53631
- }
53632
- for (const row of range(zone.top, zone.bottom + 1)) {
53633
- if (!this.isCellEmpty({ col: zone.left - 1, row })) {
53634
- return { ...zone, left: zone.left - 1 };
53635
- }
53636
- if (!this.isCellEmpty({ col: zone.right + 1, row })) {
53637
- return { ...zone, right: zone.right + 1 };
53638
- }
53639
- }
53640
- return zone;
53641
- };
53642
- let hasExpanded = false;
53643
- let zone = zoneToExpand;
53644
- do {
53645
- hasExpanded = false;
53646
- const newZone = expandZone(zone);
53647
- if (!isEqual(zone, newZone)) {
53648
- hasExpanded = true;
53649
- zone = newZone;
53650
- continue;
53651
- }
53652
- } while (hasExpanded);
53653
- return zone;
53654
- }
53655
53565
  }
53656
53566
 
53657
53567
  class StateObserver {
@@ -55860,9 +55770,9 @@ const constants = {
55860
55770
  DEFAULT_LOCALE,
55861
55771
  };
55862
55772
 
55863
- export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
55773
+ export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
55864
55774
 
55865
55775
 
55866
- __info__.version = "17.1.0-alpha.1";
55867
- __info__.date = "2023-11-02T09:35:10.532Z";
55868
- __info__.hash = "3b1fe3b";
55776
+ __info__.version = "17.1.0-alpha.3";
55777
+ __info__.date = "2023-11-16T12:04:09.636Z";
55778
+ __info__.hash = "2ef5b1a";