@odoo/o-spreadsheet 16.5.0-alpha.1 → 16.5.0-alpha.2

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.
@@ -2995,11 +2995,18 @@ class RangeImpl {
2995
2995
  constructor(args, getSheetSize) {
2996
2996
  this.getSheetSize = getSheetSize;
2997
2997
  this._zone = args.zone;
2998
- this.parts = args.parts;
2999
2998
  this.prefixSheet = args.prefixSheet;
3000
2999
  this.invalidXc = args.invalidXc;
3001
3000
  this.sheetId = args.sheetId;
3002
3001
  this.invalidSheetName = args.invalidSheetName;
3002
+ let _fixedParts = [...args.parts];
3003
+ if (args.parts.length === 1 && getZoneArea(this.zone) > 1) {
3004
+ _fixedParts.push({ ...args.parts[0] });
3005
+ }
3006
+ else if (args.parts.length === 2 && getZoneArea(this.zone) === 1) {
3007
+ _fixedParts.pop();
3008
+ }
3009
+ this.parts = _fixedParts;
3003
3010
  }
3004
3011
  static fromRange(range, getters) {
3005
3012
  if (range instanceof RangeImpl) {
@@ -4049,8 +4056,6 @@ const readonlyAllowedCommands = new Set([
4049
4056
  "START",
4050
4057
  "ACTIVATE_SHEET",
4051
4058
  "COPY",
4052
- "PREPARE_SELECTION_INPUT_EXPANSION",
4053
- "STOP_SELECTION_INPUT",
4054
4059
  "RESIZE_SHEETVIEW",
4055
4060
  "SET_VIEWPORT_OFFSET",
4056
4061
  "SELECT_SEARCH_NEXT_MATCH",
@@ -5126,8 +5131,7 @@ autofillModifiersRegistry
5126
5131
  return { cellData: {} };
5127
5132
  }
5128
5133
  const sheetId = data.sheetId;
5129
- const ranges = getters.createAdaptedRanges(cell.dependencies, x, y, sheetId);
5130
- const content = getters.buildFormulaContent(sheetId, cell, ranges);
5134
+ const content = getters.getTranslatedCellFormula(sheetId, x, y, cell.compiledFormula, cell.dependencies);
5131
5135
  return {
5132
5136
  cellData: {
5133
5137
  border: data.border,
@@ -22419,6 +22423,18 @@ class ConditionalFormattingPanel extends Component {
22419
22423
  style[tool] = !style[tool];
22420
22424
  this.closeMenus();
22421
22425
  }
22426
+ onKeydown(event) {
22427
+ if (event.key === "F4") {
22428
+ const target = event.target;
22429
+ const update = this.env.model.getters.getCycledReference({ start: target.selectionStart ?? 0, end: target.selectionEnd ?? 0 }, target.value);
22430
+ if (!update) {
22431
+ return;
22432
+ }
22433
+ target.value = update.content;
22434
+ target.setSelectionRange(update.selection.start, update.selection.end);
22435
+ target.dispatchEvent(new Event("input"));
22436
+ }
22437
+ }
22422
22438
  setColor(target, color) {
22423
22439
  this.state.rules.cellIs.style[target] = color;
22424
22440
  this.closeMenus();
@@ -24689,6 +24705,15 @@ function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }
24689
24705
  y = initialFigure.y - deltaY;
24690
24706
  }
24691
24707
  }
24708
+ // Restrict resizing if x or y reaches header boundaries
24709
+ if (x < 0) {
24710
+ width += x;
24711
+ x = 0;
24712
+ }
24713
+ if (y < 0) {
24714
+ height += y;
24715
+ y = 0;
24716
+ }
24692
24717
  return { ...initialFigure, x, y, width, height };
24693
24718
  }
24694
24719
 
@@ -25281,10 +25306,29 @@ function useCellHovered(env, gridRef, callback) {
25281
25306
  setPosition(undefined, undefined);
25282
25307
  }
25283
25308
  }
25309
+ function onMouseLeave(e) {
25310
+ const x = e.offsetX;
25311
+ const y = e.offsetY;
25312
+ const gridRect = getBoundingRectAsPOJO(gridRef.el);
25313
+ if (y < 0 || y > gridRect.height || x < 0 || x > gridRect.width) {
25314
+ return updateMousePosition(e);
25315
+ }
25316
+ else {
25317
+ return pause();
25318
+ }
25319
+ }
25284
25320
  useRefListener(gridRef, "mousemove", updateMousePosition);
25285
- useRefListener(gridRef, "mouseleave", pause);
25321
+ useRefListener(gridRef, "mouseleave", onMouseLeave);
25286
25322
  useRefListener(gridRef, "mouseenter", resume);
25287
25323
  useRefListener(gridRef, "mousedown", recompute);
25324
+ useExternalListener(window, "click", handleGlobalClick);
25325
+ function handleGlobalClick(e) {
25326
+ const target = e.target;
25327
+ const grid = gridRef.el;
25328
+ if (!grid.contains(target)) {
25329
+ setPosition(undefined, undefined);
25330
+ }
25331
+ }
25288
25332
  function setPosition(col, row) {
25289
25333
  if (col !== hoveredPosition.col || row !== hoveredPosition.row) {
25290
25334
  hoveredPosition.col = col;
@@ -25608,7 +25652,6 @@ class AbstractResizer extends Component {
25608
25652
  const mouseUpSelect = () => {
25609
25653
  this.state.isSelecting = false;
25610
25654
  this.lastSelectedElementIndex = null;
25611
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
25612
25655
  this._computeGrabDisplay(ev);
25613
25656
  };
25614
25657
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -26173,9 +26216,7 @@ class Highlight extends Component {
26173
26216
  let lastCol = isLeft ? z.left : z.right;
26174
26217
  let lastRow = isTop ? z.top : z.bottom;
26175
26218
  let currentZone = z;
26176
- this.env.model.dispatch("START_CHANGE_HIGHLIGHT", {
26177
- range: this.env.model.getters.getRangeDataFromZone(activeSheetId, currentZone),
26178
- });
26219
+ this.env.model.dispatch("START_CHANGE_HIGHLIGHT", { zone: currentZone });
26179
26220
  const mouseMove = (col, row) => {
26180
26221
  if (lastCol !== col || lastRow !== row) {
26181
26222
  lastCol = clip(col === -1 ? lastCol : col, 0, this.env.model.getters.getNumberCols(activeSheetId) - 1);
@@ -26186,21 +26227,17 @@ class Highlight extends Component {
26186
26227
  right: Math.max(pivotCol, lastCol),
26187
26228
  bottom: Math.max(pivotRow, lastRow),
26188
26229
  };
26189
- newZone = this.env.model.getters.expandZone(activeSheetId, newZone);
26190
26230
  if (!isEqual(newZone, currentZone)) {
26191
- this.env.model.dispatch("CHANGE_HIGHLIGHT", {
26192
- range: this.env.model.getters.getRangeFromZone(activeSheetId, this.env.model.getters.getUnboundedZone(activeSheetId, newZone)).rangeData,
26193
- });
26231
+ this.env.model.selection.selectZone({
26232
+ cell: { col: newZone.left, row: newZone.top },
26233
+ zone: newZone,
26234
+ }, { unbounded: true });
26194
26235
  currentZone = newZone;
26195
26236
  }
26196
26237
  }
26197
26238
  };
26198
26239
  const mouseUp = () => {
26199
26240
  this.highlightState.shiftingMode = "none";
26200
- // To do:
26201
- // Command used here to restore focus to the current composer,
26202
- // to be changed when refactoring the 'edition' plugin
26203
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26204
26241
  };
26205
26242
  dragAndDropBeyondTheViewport(this.env, mouseMove, mouseUp);
26206
26243
  }
@@ -26216,9 +26253,7 @@ class Highlight extends Component {
26216
26253
  const deltaRowMin = -z.top;
26217
26254
  const deltaRowMax = this.env.model.getters.getNumberRows(activeSheetId) - z.bottom - 1;
26218
26255
  let currentZone = z;
26219
- this.env.model.dispatch("START_CHANGE_HIGHLIGHT", {
26220
- range: this.env.model.getters.getRangeDataFromZone(activeSheetId, currentZone),
26221
- });
26256
+ this.env.model.dispatch("START_CHANGE_HIGHLIGHT", { zone: currentZone });
26222
26257
  let lastCol = initCol;
26223
26258
  let lastRow = initRow;
26224
26259
  const mouseMove = (col, row) => {
@@ -26233,21 +26268,17 @@ class Highlight extends Component {
26233
26268
  right: z.right + deltaCol,
26234
26269
  bottom: z.bottom + deltaRow,
26235
26270
  };
26236
- newZone = this.env.model.getters.expandZone(activeSheetId, newZone);
26237
26271
  if (!isEqual(newZone, currentZone)) {
26238
- this.env.model.dispatch("CHANGE_HIGHLIGHT", {
26239
- range: this.env.model.getters.getRangeFromZone(activeSheetId, this.env.model.getters.getUnboundedZone(activeSheetId, newZone)).rangeData,
26240
- });
26272
+ this.env.model.selection.selectZone({
26273
+ cell: { col: newZone.left, row: newZone.top },
26274
+ zone: newZone,
26275
+ }, { unbounded: true });
26241
26276
  currentZone = newZone;
26242
26277
  }
26243
26278
  }
26244
26279
  };
26245
26280
  const mouseUp = () => {
26246
26281
  this.highlightState.shiftingMode = "none";
26247
- // To do:
26248
- // Command used here to restore focus to the current composer,
26249
- // to be changed when refactoring the 'edition' plugin
26250
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26251
26282
  };
26252
26283
  dragAndDropBeyondTheViewport(this.env, mouseMove, mouseUp);
26253
26284
  }
@@ -26740,9 +26771,6 @@ class Grid extends Component {
26740
26771
  // Zone selection with mouse
26741
26772
  // ---------------------------------------------------------------------------
26742
26773
  onCellClicked(col, row, { ctrlKey, shiftKey }) {
26743
- if (ctrlKey) {
26744
- this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
26745
- }
26746
26774
  if (this.env.model.getters.hasOpenedPopover()) {
26747
26775
  this.closeOpenedPopover();
26748
26776
  }
@@ -26768,7 +26796,6 @@ class Grid extends Component {
26768
26796
  }
26769
26797
  };
26770
26798
  const onMouseUp = () => {
26771
- this.env.model.dispatch("STOP_SELECTION_INPUT");
26772
26799
  if (this.env.model.getters.isPaintingFormat()) {
26773
26800
  this.env.model.dispatch("PASTE", {
26774
26801
  target: this.env.model.getters.getSelectedZones(),
@@ -31486,8 +31513,8 @@ class CellPlugin extends CorePlugin {
31486
31513
  "zoneToXC",
31487
31514
  "getCells",
31488
31515
  "getFormulaCellContent",
31516
+ "getTranslatedCellFormula",
31489
31517
  "getCellStyle",
31490
- "buildFormulaContent",
31491
31518
  "getCellById",
31492
31519
  ];
31493
31520
  nextId = 1;
@@ -31686,7 +31713,7 @@ class CellPlugin extends CorePlugin {
31686
31713
  /*
31687
31714
  * Reconstructs the original formula string based on a normalized form and its dependencies
31688
31715
  */
31689
- buildFormulaContent(sheetId, cell, dependencies) {
31716
+ getFormulaCellContent(sheetId, cell, dependencies) {
31690
31717
  const ranges = dependencies || [...cell.dependencies];
31691
31718
  return concat(cell.compiledFormula.tokens.map((token) => {
31692
31719
  if (token.type === "REFERENCE") {
@@ -31696,8 +31723,15 @@ class CellPlugin extends CorePlugin {
31696
31723
  return token.value;
31697
31724
  }));
31698
31725
  }
31699
- getFormulaCellContent(sheetId, cell) {
31700
- return this.buildFormulaContent(sheetId, cell);
31726
+ /*
31727
+ * Constructs a formula string based on an initial formula and a translation vector
31728
+ */
31729
+ getTranslatedCellFormula(sheetId, offsetX, offsetY, compiledFormula, dependencies) {
31730
+ const adaptedDependencies = this.getters.createAdaptedRanges(dependencies, offsetX, offsetY, sheetId);
31731
+ return this.getFormulaCellContent(sheetId, {
31732
+ compiledFormula,
31733
+ dependencies: adaptedDependencies,
31734
+ });
31701
31735
  }
31702
31736
  getCellStyle(position) {
31703
31737
  return this.getters.getCell(position)?.style || {};
@@ -31887,7 +31921,7 @@ class CellPlugin extends CorePlugin {
31887
31921
  */
31888
31922
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
31889
31923
  const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
31890
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.buildFormulaContent.bind(this));
31924
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getFormulaCellContent.bind(this));
31891
31925
  }
31892
31926
  createErrorFormula(id, content, format, style, error) {
31893
31927
  return {
@@ -31921,19 +31955,19 @@ class FormulaCellWithDependencies {
31921
31955
  style;
31922
31956
  dependencies;
31923
31957
  sheetId;
31924
- buildFormulaContent;
31958
+ getFormulaCellContent;
31925
31959
  isFormula = true;
31926
- constructor(id, compiledFormula, format, style, dependencies, sheetId, buildFormulaContent) {
31960
+ constructor(id, compiledFormula, format, style, dependencies, sheetId, getFormulaCellContent) {
31927
31961
  this.id = id;
31928
31962
  this.compiledFormula = compiledFormula;
31929
31963
  this.format = format;
31930
31964
  this.style = style;
31931
31965
  this.dependencies = dependencies;
31932
31966
  this.sheetId = sheetId;
31933
- this.buildFormulaContent = buildFormulaContent;
31967
+ this.getFormulaCellContent = getFormulaCellContent;
31934
31968
  }
31935
31969
  get content() {
31936
- return this.buildFormulaContent(this.sheetId, {
31970
+ return this.getFormulaCellContent(this.sheetId, {
31937
31971
  dependencies: this.dependencies,
31938
31972
  compiledFormula: this.compiledFormula,
31939
31973
  });
@@ -36448,13 +36482,21 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
36448
36482
  }
36449
36483
  break;
36450
36484
  case "CellIsRule":
36485
+ const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
36451
36486
  for (let ref of cf.ranges) {
36452
36487
  const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
36453
36488
  for (let row = zone.top; row <= zone.bottom; row++) {
36454
36489
  for (let col = zone.left; col <= zone.right; col++) {
36455
- const pr = this.rulePredicate[cf.rule.type];
36456
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
36457
- if (pr && pr(cell, cf.rule)) {
36490
+ const predicate = this.rulePredicate[cf.rule.type];
36491
+ const target = { sheetId, col, row };
36492
+ const values = cf.rule.values.map((value, i) => {
36493
+ const compiledFormula = formulas[i];
36494
+ if (compiledFormula) {
36495
+ return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, compiledFormula, compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
36496
+ }
36497
+ return value;
36498
+ });
36499
+ if (predicate && predicate(target, { ...cf.rule, values })) {
36458
36500
  if (!computedStyle[col])
36459
36501
  computedStyle[col] = [];
36460
36502
  // we must combine all the properties of all the CF rules applied to the given cell
@@ -36622,12 +36664,18 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
36622
36664
  * Execute the predicate to know if a conditional formatting rule should be applied to a cell
36623
36665
  */
36624
36666
  rulePredicate = {
36625
- CellIsRule: (cell, rule) => {
36667
+ CellIsRule: (target, rule) => {
36668
+ const cell = this.getters.getEvaluatedCell(target);
36626
36669
  if (cell.type === CellValueType.error) {
36627
36670
  return false;
36628
36671
  }
36629
36672
  const locale = this.getters.getLocale();
36630
- const values = rule.values.map((val) => parseLiteral(val, locale));
36673
+ const values = rule.values.map((value) => {
36674
+ if (value.startsWith("=")) {
36675
+ return this.getters.evaluateFormula(target.sheetId, value);
36676
+ }
36677
+ return parseLiteral(value, locale);
36678
+ });
36631
36679
  switch (rule.operator) {
36632
36680
  case "IsEmpty":
36633
36681
  return cell.value.toString().trim() === "";
@@ -38552,9 +38600,17 @@ class FindAndReplacePlugin extends UIPlugin {
38552
38600
  case "REPLACE_ALL_SEARCH":
38553
38601
  this.replaceAll(cmd.replaceWith);
38554
38602
  break;
38603
+ case "EVALUATE_CELLS":
38604
+ case "UPDATE_CELL":
38605
+ case "REMOVE_FILTER_TABLE":
38606
+ case "UPDATE_FILTER":
38607
+ this.isSearchDirty = true;
38608
+ break;
38555
38609
  case "UNDO":
38556
38610
  case "REDO":
38557
38611
  case "REMOVE_COLUMNS_ROWS":
38612
+ case "HIDE_COLUMNS_ROWS":
38613
+ case "UNHIDE_COLUMNS_ROWS":
38558
38614
  case "ADD_COLUMNS_ROWS":
38559
38615
  case "EVALUATE_CELLS":
38560
38616
  case "UPDATE_CELL":
@@ -38690,37 +38746,32 @@ class FindAndReplacePlugin extends UIPlugin {
38690
38746
  // ---------------------------------------------------------------------------
38691
38747
  // Replace
38692
38748
  // ---------------------------------------------------------------------------
38693
- /**
38694
- * Replace the value of the currently selected match
38695
- */
38696
- replace(replaceWith) {
38697
- if (this.selectedMatchIndex === null || !this.currentSearchRegex) {
38749
+ replaceMatch(selectedMatch, replaceWith) {
38750
+ if (!this.currentSearchRegex) {
38698
38751
  return;
38699
38752
  }
38700
- const matches = this.searchMatches;
38701
- const selectedMatch = matches[this.selectedMatchIndex];
38702
38753
  const sheetId = this.getters.getActiveSheetId();
38703
38754
  const cell = this.getters.getCell({ sheetId, ...selectedMatch });
38755
+ const { col, row } = selectedMatch;
38704
38756
  if (cell?.isFormula && !this.searchOptions.searchFormulas) {
38705
- this.selectNextCell(Direction.next);
38757
+ return;
38706
38758
  }
38707
- else {
38708
- const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
38709
- const toReplace = this.getSearchableString({
38710
- sheetId,
38711
- col: selectedMatch.col,
38712
- row: selectedMatch.row,
38713
- });
38714
- const newContent = toReplace.replace(replaceRegex, replaceWith);
38715
- this.dispatch("UPDATE_CELL", {
38716
- sheetId: this.getters.getActiveSheetId(),
38717
- col: selectedMatch.col,
38718
- row: selectedMatch.row,
38719
- content: canonicalizeContent(newContent, this.getters.getLocale()),
38720
- });
38721
- this.searchMatches.splice(this.selectedMatchIndex, 1);
38722
- this.selectNextCell(Direction.current);
38759
+ const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
38760
+ const toReplace = this.getSearchableString({ sheetId, col, row });
38761
+ const content = toReplace.replace(replaceRegex, replaceWith);
38762
+ const canonicalContent = canonicalizeContent(content, this.getters.getLocale());
38763
+ this.dispatch("UPDATE_CELL", { sheetId, col, row, content: canonicalContent });
38764
+ }
38765
+ /**
38766
+ * Replace the value of the currently selected match
38767
+ */
38768
+ replace(replaceWith) {
38769
+ if (this.selectedMatchIndex === null) {
38770
+ return;
38723
38771
  }
38772
+ const selectedMatch = this.searchMatches[this.selectedMatchIndex];
38773
+ this.replaceMatch(selectedMatch, replaceWith);
38774
+ this.selectNextCell(Direction.next);
38724
38775
  }
38725
38776
  /**
38726
38777
  * Apply the replace function to all the matches one time.
@@ -38728,7 +38779,7 @@ class FindAndReplacePlugin extends UIPlugin {
38728
38779
  replaceAll(replaceWith) {
38729
38780
  const matchCount = this.searchMatches.length;
38730
38781
  for (let i = 0; i < matchCount; i++) {
38731
- this.replace(replaceWith);
38782
+ this.replaceMatch(this.searchMatches[i], replaceWith);
38732
38783
  }
38733
38784
  }
38734
38785
  getSearchableString(position) {
@@ -39650,8 +39701,7 @@ class SelectionInputPlugin extends UIPlugin {
39650
39701
  static getters = [];
39651
39702
  ranges = [];
39652
39703
  focusedRangeIndex = null;
39653
- activeSheet;
39654
- willAddNewRange = false;
39704
+ inputSheetId;
39655
39705
  constructor(config, initialRanges, inputHasSingleRange) {
39656
39706
  if (inputHasSingleRange && initialRanges.length > 1) {
39657
39707
  throw new Error("Input with a single range cannot be instantiated with several range references.");
@@ -39659,7 +39709,7 @@ class SelectionInputPlugin extends UIPlugin {
39659
39709
  super(config);
39660
39710
  this.inputHasSingleRange = inputHasSingleRange;
39661
39711
  this.insertNewRange(0, initialRanges);
39662
- this.activeSheet = this.getters.getActiveSheetId();
39712
+ this.inputSheetId = this.getters.getActiveSheetId();
39663
39713
  if (this.ranges.length === 0) {
39664
39714
  this.insertNewRange(this.ranges.length, [""]);
39665
39715
  this.focusLast();
@@ -39685,11 +39735,33 @@ class SelectionInputPlugin extends UIPlugin {
39685
39735
  return 0 /* CommandResult.Success */;
39686
39736
  }
39687
39737
  handleEvent(event) {
39688
- const inputSheetId = this.activeSheet;
39689
- const sheetId = this.getters.getActiveSheetId();
39690
- const zone = event.anchor.zone;
39691
- const range = this.getters.getRangeFromZone(sheetId, event.options.unbounded ? this.getters.getUnboundedZone(sheetId, zone) : zone);
39692
- this.add([this.getters.getSelectionRangeString(range, inputSheetId)]);
39738
+ if (this.focusedRangeIndex === null) {
39739
+ return;
39740
+ }
39741
+ const inputSheetId = this.inputSheetId;
39742
+ const activeSheetId = this.getters.getActiveSheetId();
39743
+ const zone = event.options.unbounded
39744
+ ? this.getters.getUnboundedZone(activeSheetId, event.anchor.zone)
39745
+ : event.anchor.zone;
39746
+ const range = this.getters.getRangeFromZone(activeSheetId, zone);
39747
+ const willAddNewRange = event.mode === "newAnchor" &&
39748
+ !this.inputHasSingleRange &&
39749
+ this.ranges[this.focusedRangeIndex].xc.trim() !== "";
39750
+ if (willAddNewRange) {
39751
+ const xc = this.getters.getSelectionRangeString(range, inputSheetId);
39752
+ this.insertNewRange(this.ranges.length, [xc]);
39753
+ this.focusLast();
39754
+ }
39755
+ else {
39756
+ let parts = range.parts;
39757
+ const previousXc = this.ranges[this.focusedRangeIndex].xc.trim();
39758
+ if (previousXc) {
39759
+ parts = this.getters.getRangeFromSheetXC(inputSheetId, previousXc).parts;
39760
+ }
39761
+ const newRange = range.clone({ parts });
39762
+ const xc = this.getters.getSelectionRangeString(newRange, inputSheetId);
39763
+ this.setRange(this.focusedRangeIndex, [xc]);
39764
+ }
39693
39765
  }
39694
39766
  handle(cmd) {
39695
39767
  switch (cmd.type) {
@@ -39725,16 +39797,6 @@ class SelectionInputPlugin extends UIPlugin {
39725
39797
  this.removeRange(index);
39726
39798
  }
39727
39799
  break;
39728
- case "STOP_SELECTION_INPUT":
39729
- this.willAddNewRange = false;
39730
- break;
39731
- case "PREPARE_SELECTION_INPUT_EXPANSION": {
39732
- const index = this.focusedRangeIndex;
39733
- if (index !== null && !this.inputHasSingleRange) {
39734
- this.willAddNewRange = this.ranges[index].xc.trim() !== "";
39735
- }
39736
- break;
39737
- }
39738
39800
  case "ACTIVATE_SHEET": {
39739
39801
  if (cmd.sheetIdFrom !== cmd.sheetIdTo) {
39740
39802
  const { col, row } = this.getters.getNextVisibleCellPosition({
@@ -39745,7 +39807,26 @@ class SelectionInputPlugin extends UIPlugin {
39745
39807
  const zone = this.getters.expandZone(cmd.sheetIdTo, positionToZone({ col, row }));
39746
39808
  this.selection.resetAnchor(this, { cell: { col, row }, zone });
39747
39809
  }
39810
+ break;
39748
39811
  }
39812
+ case "START_CHANGE_HIGHLIGHT":
39813
+ const activeSheetId = this.getters.getActiveSheetId();
39814
+ const newZone = this.getters.expandZone(activeSheetId, cmd.zone);
39815
+ const focusIndex = this.ranges.findIndex((range) => {
39816
+ const { xc, sheetName: sheet } = splitReference(range.xc);
39817
+ const sheetName = sheet || this.getters.getSheetName(this.inputSheetId);
39818
+ if (this.getters.getSheetName(activeSheetId) !== sheetName) {
39819
+ return false;
39820
+ }
39821
+ const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
39822
+ return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), newZone);
39823
+ });
39824
+ if (focusIndex !== -1) {
39825
+ this.focus(focusIndex);
39826
+ const { left, top } = newZone;
39827
+ this.selection.resetAnchor(this, { cell: { col: left, row: top }, zone: newZone });
39828
+ }
39829
+ break;
39749
39830
  }
39750
39831
  }
39751
39832
  // ---------------------------------------------------------------------------
@@ -39774,19 +39855,6 @@ class SelectionInputPlugin extends UIPlugin {
39774
39855
  unfocus() {
39775
39856
  this.focusedRangeIndex = null;
39776
39857
  }
39777
- add(newRanges) {
39778
- if (this.focusedRangeIndex === null || newRanges.length === 0) {
39779
- return;
39780
- }
39781
- if (this.willAddNewRange) {
39782
- this.insertNewRange(this.ranges.length, newRanges);
39783
- this.focusLast();
39784
- this.willAddNewRange = false;
39785
- }
39786
- else {
39787
- this.setRange(this.focusedRangeIndex, newRanges);
39788
- }
39789
- }
39790
39858
  setContent(index, xc) {
39791
39859
  this.ranges[index] = {
39792
39860
  ...this.ranges[index],
@@ -39832,12 +39900,12 @@ class SelectionInputPlugin extends UIPlugin {
39832
39900
  inputToHighlights({ xc, color }) {
39833
39901
  const XCs = this.cleanInputs([xc])
39834
39902
  .filter((range) => this.getters.isRangeValid(range))
39835
- .filter((reference) => this.shouldBeHighlighted(this.activeSheet, reference));
39903
+ .filter((reference) => this.shouldBeHighlighted(this.inputSheetId, reference));
39836
39904
  return XCs.map((xc) => {
39837
39905
  const { sheetName } = splitReference(xc);
39838
39906
  return {
39839
- zone: this.getters.getRangeFromSheetXC(this.activeSheet, xc).zone,
39840
- sheetId: (sheetName && this.getters.getSheetIdByName(sheetName)) || this.activeSheet,
39907
+ zone: this.getters.getRangeFromSheetXC(this.inputSheetId, xc).zone,
39908
+ sheetId: (sheetName && this.getters.getSheetIdByName(sheetName)) || this.inputSheetId,
39841
39909
  color,
39842
39910
  };
39843
39911
  });
@@ -40262,10 +40330,8 @@ class SortPlugin extends UIPlugin {
40262
40330
  let content = cell.content;
40263
40331
  if (cell.isFormula) {
40264
40332
  const position = this.getters.getCellPosition(cell.id);
40265
- const offsetY = newRow - position.row;
40266
40333
  // we only have a vertical offset
40267
- const ranges = this.getters.createAdaptedRanges(cell.dependencies, 0, offsetY, sheetId);
40268
- content = this.getters.buildFormulaContent(sheetId, cell, ranges);
40334
+ content = this.getters.getTranslatedCellFormula(sheetId, 0, newRow - position.row, cell.compiledFormula, cell.dependencies);
40269
40335
  }
40270
40336
  newCellValues.style = cell.style;
40271
40337
  newCellValues.content = content;
@@ -41372,9 +41438,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
41372
41438
  }
41373
41439
  let content = origin.cell.content;
41374
41440
  if (origin.cell.isFormula && operation === "COPY") {
41375
- const offsetX = col - origin.position.col;
41376
- const offsetY = row - origin.position.row;
41377
- content = this.getUpdatedContent(sheetId, origin.cell, offsetX, offsetY, operation);
41441
+ content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula, origin.cell.dependencies);
41378
41442
  }
41379
41443
  this.dispatch("UPDATE_CELL", {
41380
41444
  ...target,
@@ -41395,13 +41459,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
41395
41459
  }
41396
41460
  }
41397
41461
  }
41398
- /**
41399
- * Get the newly updated formula, after applying offsets
41400
- */
41401
- getUpdatedContent(sheetId, cell, offsetX, offsetY, operation) {
41402
- const ranges = this.getters.createAdaptedRanges(cell.dependencies, offsetX, offsetY, sheetId);
41403
- return this.getters.buildFormulaContent(sheetId, cell, ranges);
41404
- }
41405
41462
  /**
41406
41463
  * If the origin position given is the top left of a merge, merge the target
41407
41464
  * position.
@@ -42143,6 +42200,7 @@ class EditionPlugin extends UIPlugin {
42143
42200
  "getTokenAtCursor",
42144
42201
  "getComposerHighlights",
42145
42202
  "getCurrentEditedCell",
42203
+ "getCycledReference",
42146
42204
  ];
42147
42205
  col = 0;
42148
42206
  row = 0;
@@ -42152,10 +42210,7 @@ class EditionPlugin extends UIPlugin {
42152
42210
  currentTokens = [];
42153
42211
  selectionStart = 0;
42154
42212
  selectionEnd = 0;
42155
- selectionInitialStart = 0;
42156
42213
  initialContent = "";
42157
- previousRef = "";
42158
- previousRange = undefined;
42159
42214
  colorIndexByRange = {};
42160
42215
  // ---------------------------------------------------------------------------
42161
42216
  // Command Handling
@@ -42184,9 +42239,6 @@ class EditionPlugin extends UIPlugin {
42184
42239
  }
42185
42240
  }
42186
42241
  handleEvent(event) {
42187
- if (this.mode !== "selecting") {
42188
- return;
42189
- }
42190
42242
  const sheetId = this.getters.getActiveSheetId();
42191
42243
  let unboundedZone;
42192
42244
  if (event.options.unbounded) {
@@ -42197,10 +42249,17 @@ class EditionPlugin extends UIPlugin {
42197
42249
  }
42198
42250
  switch (event.mode) {
42199
42251
  case "newAnchor":
42200
- this.insertSelectedRange(unboundedZone);
42252
+ if (this.mode === "selecting") {
42253
+ this.insertSelectedRange(unboundedZone);
42254
+ }
42201
42255
  break;
42202
42256
  default:
42203
- this.replaceSelectedRanges(unboundedZone);
42257
+ if (this.mode === "selecting") {
42258
+ this.replaceSelectedRange(unboundedZone);
42259
+ }
42260
+ else {
42261
+ this.updateComposerRange(event.previousAnchor.zone, unboundedZone);
42262
+ }
42204
42263
  break;
42205
42264
  }
42206
42265
  }
@@ -42257,35 +42316,12 @@ class EditionPlugin extends UIPlugin {
42257
42316
  }
42258
42317
  break;
42259
42318
  case "START_CHANGE_HIGHLIGHT":
42260
- // FIXME: thiws whole ordeal could be handled with the Selection Processor which would extend the feature to selection inputs
42261
- this.dispatch("STOP_COMPOSER_RANGE_SELECTION");
42262
- // FIXME: we should check range SheetId compared to this.activeSheetId r maybe not have a sheetId in the payload ??
42263
- const range = this.getters.getRangeFromRangeData(cmd.range);
42264
- const previousRefToken = this.currentTokens
42265
- .filter((token) => token.type === "REFERENCE")
42266
- .find((token) => {
42267
- const { xc, sheetName: sheet } = splitReference(token.value);
42268
- const sheetName = sheet || this.getters.getSheetName(this.sheetId);
42269
- const activeSheetId = this.getters.getActiveSheetId();
42270
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
42271
- return false;
42272
- }
42273
- const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
42274
- return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), range.zone);
42275
- });
42276
- this.previousRef = previousRefToken.value;
42277
- this.previousRange = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), this.previousRef);
42278
- this.selectionInitialStart = previousRefToken.start;
42279
- break;
42280
- case "CHANGE_HIGHLIGHT":
42281
- const cmdRange = this.getters.getRangeFromRangeData(cmd.range);
42282
- const newRef = this.getRangeReference(cmdRange, this.previousRange.parts);
42283
- this.selectionStart = this.selectionInitialStart;
42284
- this.selectionEnd = this.selectionInitialStart + this.previousRef.length;
42285
- this.replaceSelection(newRef);
42286
- this.previousRef = newRef;
42287
- this.selectionStart = this.currentContent.length;
42288
- this.selectionEnd = this.currentContent.length;
42319
+ const { left, top } = cmd.zone;
42320
+ // changing the highlight can conflit with the 'selecting' mode
42321
+ if (this.isSelectingForComposer()) {
42322
+ this.mode = "editing";
42323
+ }
42324
+ this.selection.resetAnchor(this, { cell: { col: left, row: top }, zone: cmd.zone });
42289
42325
  break;
42290
42326
  case "ACTIVATE_SHEET":
42291
42327
  if (!this.currentContent.startsWith("=")) {
@@ -42369,33 +42405,45 @@ class EditionPlugin extends UIPlugin {
42369
42405
  return this.currentTokens.find((t) => t.start <= start && t.end >= end);
42370
42406
  }
42371
42407
  }
42372
- // ---------------------------------------------------------------------------
42373
- // Misc
42374
- // ---------------------------------------------------------------------------
42375
- cycleReferences() {
42376
- const tokens = this.getTokensInSelection();
42408
+ /**
42409
+ * Return the cycled reference if any (A1 -> $A$1 -> A$1 -> $A1 -> A1)
42410
+ */
42411
+ getCycledReference(selection, content) {
42412
+ const locale = this.getters.getLocale();
42413
+ const currentTokens = content.startsWith("=") ? composerTokenize(content, locale) : [];
42414
+ const tokens = currentTokens.filter((t) => (t.start <= selection.start && t.end >= selection.start) ||
42415
+ (t.start >= selection.start && t.start < selection.end));
42377
42416
  const refTokens = tokens.filter((token) => token.type === "REFERENCE");
42378
- if (refTokens.length === 0)
42417
+ if (refTokens.length === 0) {
42379
42418
  return;
42419
+ }
42380
42420
  const updatedReferences = tokens
42381
42421
  .map(loopThroughReferenceType)
42382
42422
  .map((token) => token.value)
42383
42423
  .join("");
42384
- const content = this.currentContent;
42385
42424
  const start = tokens[0].start;
42386
42425
  const end = tokens[tokens.length - 1].end;
42387
42426
  const newContent = content.slice(0, start) + updatedReferences + content.slice(end);
42388
42427
  const lengthDiff = newContent.length - content.length;
42389
42428
  const startOfTokens = refTokens[0].start;
42390
42429
  const endOfTokens = refTokens[refTokens.length - 1].end + lengthDiff;
42391
- const selection = { start: startOfTokens, end: endOfTokens };
42392
- // Put the selection at the end of the token if we cycled on a single token
42393
- if (refTokens.length === 1 && this.selectionStart === this.selectionEnd) {
42394
- selection.start = selection.end;
42430
+ const newSelection = { start: startOfTokens, end: endOfTokens };
42431
+ if (refTokens.length === 1 && selection.start === selection.end) {
42432
+ newSelection.start = newSelection.end;
42433
+ }
42434
+ return { content: newContent, selection: newSelection };
42435
+ }
42436
+ // ---------------------------------------------------------------------------
42437
+ // Misc
42438
+ // ---------------------------------------------------------------------------
42439
+ cycleReferences() {
42440
+ const updated = this.getCycledReference(this.getComposerSelection(), this.currentContent);
42441
+ if (updated === undefined) {
42442
+ return;
42395
42443
  }
42396
42444
  this.dispatch("SET_CURRENT_CONTENT", {
42397
- content: newContent,
42398
- selection,
42445
+ content: updated.content,
42446
+ selection: updated.selection,
42399
42447
  });
42400
42448
  }
42401
42449
  validateSelection(length, start, end) {
@@ -42443,7 +42491,6 @@ class EditionPlugin extends UIPlugin {
42443
42491
  this.selection.resetAnchor(this, { cell: { col: this.col, row: this.row }, zone });
42444
42492
  }
42445
42493
  this.mode = "selecting";
42446
- this.selectionInitialStart = this.selectionStart;
42447
42494
  }
42448
42495
  /**
42449
42496
  * start the edition of a cell
@@ -42603,19 +42650,49 @@ class EditionPlugin extends UIPlugin {
42603
42650
  const ref = this.getZoneReference(zone);
42604
42651
  if (this.canStartComposerRangeSelection()) {
42605
42652
  this.insertText(ref, start);
42606
- this.selectionInitialStart = start;
42607
42653
  }
42608
42654
  else {
42609
42655
  this.insertText("," + ref, start);
42610
- this.selectionInitialStart = start + 1;
42611
42656
  }
42612
42657
  }
42613
42658
  /**
42614
42659
  * Replace the current reference selected by the new one.
42615
42660
  * */
42616
- replaceSelectedRanges(zone) {
42661
+ replaceSelectedRange(zone) {
42617
42662
  const ref = this.getZoneReference(zone);
42618
- this.replaceText(ref, this.selectionInitialStart, this.selectionEnd);
42663
+ const currentToken = this.getTokenAtCursor();
42664
+ const start = currentToken?.type === "REFERENCE" ? currentToken.start : this.selectionStart;
42665
+ this.replaceText(ref, start, this.selectionEnd);
42666
+ }
42667
+ /**
42668
+ * Replace the reference of the old zone by the new one.
42669
+ */
42670
+ updateComposerRange(oldZone, newZone) {
42671
+ const activeSheetId = this.getters.getActiveSheetId();
42672
+ const tokentAtCursor = this.getTokenAtCursor();
42673
+ const tokens = tokentAtCursor ? [tokentAtCursor, ...this.currentTokens] : this.currentTokens;
42674
+ const previousRefToken = tokens
42675
+ .filter((token) => token.type === "REFERENCE")
42676
+ .find((token) => {
42677
+ const { xc, sheetName: sheet } = splitReference(token.value);
42678
+ const sheetName = sheet || this.getters.getSheetName(this.sheetId);
42679
+ if (this.getters.getSheetName(activeSheetId) !== sheetName) {
42680
+ return false;
42681
+ }
42682
+ const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
42683
+ return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
42684
+ });
42685
+ // this function assumes that the previous range is always found because
42686
+ // it's called when changing a highlight, which exists by definition
42687
+ if (!previousRefToken) {
42688
+ throw new Error("Previous range not found");
42689
+ }
42690
+ const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
42691
+ this.selectionStart = previousRefToken.start;
42692
+ this.selectionEnd = this.selectionStart + previousRefToken.value.length;
42693
+ const newRange = this.getters.getRangeFromZone(activeSheetId, newZone);
42694
+ const newRef = this.getRangeReference(newRange, previousRange.parts);
42695
+ this.replaceSelection(newRef);
42619
42696
  }
42620
42697
  getZoneReference(zone) {
42621
42698
  const inputSheetId = this.getters.getCurrentEditedCell().sheetId;
@@ -42623,14 +42700,8 @@ class EditionPlugin extends UIPlugin {
42623
42700
  const range = this.getters.getRangeFromZone(sheetId, zone);
42624
42701
  return this.getters.getSelectionRangeString(range, inputSheetId);
42625
42702
  }
42626
- getRangeReference(range, fixedParts = [{ colFixed: false, rowFixed: false }]) {
42703
+ getRangeReference(range, fixedParts) {
42627
42704
  let _fixedParts = [...fixedParts];
42628
- if (fixedParts.length === 1 && getZoneArea(range.zone) > 1) {
42629
- _fixedParts.push({ ...fixedParts[0] });
42630
- }
42631
- else if (fixedParts.length === 2 && getZoneArea(range.zone) === 1) {
42632
- _fixedParts.pop();
42633
- }
42634
42705
  const newRange = range.clone({ parts: _fixedParts });
42635
42706
  return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell().sheetId);
42636
42707
  }
@@ -42757,15 +42828,6 @@ class EditionPlugin extends UIPlugin {
42757
42828
  }
42758
42829
  return false;
42759
42830
  }
42760
- /**
42761
- * Return all the tokens between selectionStart and selectionEnd.
42762
- * Includes token that begin right on selectionStart or end right on selectionEnd.
42763
- */
42764
- getTokensInSelection() {
42765
- const start = Math.min(this.selectionStart, this.selectionEnd);
42766
- const end = Math.max(this.selectionStart, this.selectionEnd);
42767
- return this.currentTokens.filter((t) => (t.start <= start && t.end >= start) || (t.start >= start && t.start < end));
42768
- }
42769
42831
  }
42770
42832
 
42771
42833
  class FilterEvaluationPlugin extends UIPlugin {
@@ -50299,10 +50361,17 @@ class Model extends EventBus {
50299
50361
  * It will call `beforeHandle` and `handle`
50300
50362
  */
50301
50363
  dispatchToHandlers(handlers, command) {
50364
+ const isCommandCore = isCoreCommand(command);
50302
50365
  for (const handler of handlers) {
50366
+ if (!isCommandCore && handler instanceof CorePlugin) {
50367
+ continue;
50368
+ }
50303
50369
  handler.beforeHandle(command);
50304
50370
  }
50305
50371
  for (const handler of handlers) {
50372
+ if (!isCommandCore && handler instanceof CorePlugin) {
50373
+ continue;
50374
+ }
50306
50375
  handler.handle(command);
50307
50376
  }
50308
50377
  }
@@ -50488,6 +50557,9 @@ const components = {
50488
50557
  };
50489
50558
  function addFunction(functionName, functionDescription) {
50490
50559
  functionRegistry.add(functionName, functionDescription);
50560
+ return {
50561
+ addFunction: (fName, fDescription) => addFunction(fName, fDescription),
50562
+ };
50491
50563
  }
50492
50564
  const constants = {
50493
50565
  DEFAULT_LOCALE,
@@ -50496,6 +50568,6 @@ const constants = {
50496
50568
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
50497
50569
 
50498
50570
 
50499
- __info__.version = '16.5.0-alpha.1';
50500
- __info__.date = '2023-07-26T12:11:43.109Z';
50501
- __info__.hash = '9160731';
50571
+ __info__.version = '16.5.0-alpha.2';
50572
+ __info__.date = '2023-08-07T09:02:00.623Z';
50573
+ __info__.hash = 'e74f3ef';