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