@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.
@@ -2999,11 +2999,18 @@ class RangeImpl {
2999
2999
  constructor(args, getSheetSize) {
3000
3000
  this.getSheetSize = getSheetSize;
3001
3001
  this._zone = args.zone;
3002
- this.parts = args.parts;
3003
3002
  this.prefixSheet = args.prefixSheet;
3004
3003
  this.invalidXc = args.invalidXc;
3005
3004
  this.sheetId = args.sheetId;
3006
3005
  this.invalidSheetName = args.invalidSheetName;
3006
+ let _fixedParts = [...args.parts];
3007
+ if (args.parts.length === 1 && getZoneArea(this.zone) > 1) {
3008
+ _fixedParts.push({ ...args.parts[0] });
3009
+ }
3010
+ else if (args.parts.length === 2 && getZoneArea(this.zone) === 1) {
3011
+ _fixedParts.pop();
3012
+ }
3013
+ this.parts = _fixedParts;
3007
3014
  }
3008
3015
  static fromRange(range, getters) {
3009
3016
  if (range instanceof RangeImpl) {
@@ -4053,8 +4060,6 @@ const readonlyAllowedCommands = new Set([
4053
4060
  "START",
4054
4061
  "ACTIVATE_SHEET",
4055
4062
  "COPY",
4056
- "PREPARE_SELECTION_INPUT_EXPANSION",
4057
- "STOP_SELECTION_INPUT",
4058
4063
  "RESIZE_SHEETVIEW",
4059
4064
  "SET_VIEWPORT_OFFSET",
4060
4065
  "SELECT_SEARCH_NEXT_MATCH",
@@ -5130,8 +5135,7 @@ autofillModifiersRegistry
5130
5135
  return { cellData: {} };
5131
5136
  }
5132
5137
  const sheetId = data.sheetId;
5133
- const ranges = getters.createAdaptedRanges(cell.dependencies, x, y, sheetId);
5134
- const content = getters.buildFormulaContent(sheetId, cell, ranges);
5138
+ const content = getters.getTranslatedCellFormula(sheetId, x, y, cell.compiledFormula, cell.dependencies);
5135
5139
  return {
5136
5140
  cellData: {
5137
5141
  border: data.border,
@@ -22423,6 +22427,18 @@ class ConditionalFormattingPanel extends owl.Component {
22423
22427
  style[tool] = !style[tool];
22424
22428
  this.closeMenus();
22425
22429
  }
22430
+ onKeydown(event) {
22431
+ if (event.key === "F4") {
22432
+ const target = event.target;
22433
+ const update = this.env.model.getters.getCycledReference({ start: target.selectionStart ?? 0, end: target.selectionEnd ?? 0 }, target.value);
22434
+ if (!update) {
22435
+ return;
22436
+ }
22437
+ target.value = update.content;
22438
+ target.setSelectionRange(update.selection.start, update.selection.end);
22439
+ target.dispatchEvent(new Event("input"));
22440
+ }
22441
+ }
22426
22442
  setColor(target, color) {
22427
22443
  this.state.rules.cellIs.style[target] = color;
22428
22444
  this.closeMenus();
@@ -24693,6 +24709,15 @@ function dragFigureForResize(initialFigure, dirX, dirY, { x: mouseX, y: mouseY }
24693
24709
  y = initialFigure.y - deltaY;
24694
24710
  }
24695
24711
  }
24712
+ // Restrict resizing if x or y reaches header boundaries
24713
+ if (x < 0) {
24714
+ width += x;
24715
+ x = 0;
24716
+ }
24717
+ if (y < 0) {
24718
+ height += y;
24719
+ y = 0;
24720
+ }
24696
24721
  return { ...initialFigure, x, y, width, height };
24697
24722
  }
24698
24723
 
@@ -25285,10 +25310,29 @@ function useCellHovered(env, gridRef, callback) {
25285
25310
  setPosition(undefined, undefined);
25286
25311
  }
25287
25312
  }
25313
+ function onMouseLeave(e) {
25314
+ const x = e.offsetX;
25315
+ const y = e.offsetY;
25316
+ const gridRect = getBoundingRectAsPOJO(gridRef.el);
25317
+ if (y < 0 || y > gridRect.height || x < 0 || x > gridRect.width) {
25318
+ return updateMousePosition(e);
25319
+ }
25320
+ else {
25321
+ return pause();
25322
+ }
25323
+ }
25288
25324
  useRefListener(gridRef, "mousemove", updateMousePosition);
25289
- useRefListener(gridRef, "mouseleave", pause);
25325
+ useRefListener(gridRef, "mouseleave", onMouseLeave);
25290
25326
  useRefListener(gridRef, "mouseenter", resume);
25291
25327
  useRefListener(gridRef, "mousedown", recompute);
25328
+ owl.useExternalListener(window, "click", handleGlobalClick);
25329
+ function handleGlobalClick(e) {
25330
+ const target = e.target;
25331
+ const grid = gridRef.el;
25332
+ if (!grid.contains(target)) {
25333
+ setPosition(undefined, undefined);
25334
+ }
25335
+ }
25292
25336
  function setPosition(col, row) {
25293
25337
  if (col !== hoveredPosition.col || row !== hoveredPosition.row) {
25294
25338
  hoveredPosition.col = col;
@@ -25612,7 +25656,6 @@ class AbstractResizer extends owl.Component {
25612
25656
  const mouseUpSelect = () => {
25613
25657
  this.state.isSelecting = false;
25614
25658
  this.lastSelectedElementIndex = null;
25615
- this.env.model.dispatch(ev.ctrlKey ? "PREPARE_SELECTION_INPUT_EXPANSION" : "STOP_SELECTION_INPUT");
25616
25659
  this._computeGrabDisplay(ev);
25617
25660
  };
25618
25661
  dragAndDropBeyondTheViewport(this.env, mouseMoveSelect, mouseUpSelect);
@@ -26177,9 +26220,7 @@ class Highlight extends owl.Component {
26177
26220
  let lastCol = isLeft ? z.left : z.right;
26178
26221
  let lastRow = isTop ? z.top : z.bottom;
26179
26222
  let currentZone = z;
26180
- this.env.model.dispatch("START_CHANGE_HIGHLIGHT", {
26181
- range: this.env.model.getters.getRangeDataFromZone(activeSheetId, currentZone),
26182
- });
26223
+ this.env.model.dispatch("START_CHANGE_HIGHLIGHT", { zone: currentZone });
26183
26224
  const mouseMove = (col, row) => {
26184
26225
  if (lastCol !== col || lastRow !== row) {
26185
26226
  lastCol = clip(col === -1 ? lastCol : col, 0, this.env.model.getters.getNumberCols(activeSheetId) - 1);
@@ -26190,21 +26231,17 @@ class Highlight extends owl.Component {
26190
26231
  right: Math.max(pivotCol, lastCol),
26191
26232
  bottom: Math.max(pivotRow, lastRow),
26192
26233
  };
26193
- newZone = this.env.model.getters.expandZone(activeSheetId, newZone);
26194
26234
  if (!isEqual(newZone, currentZone)) {
26195
- this.env.model.dispatch("CHANGE_HIGHLIGHT", {
26196
- range: this.env.model.getters.getRangeFromZone(activeSheetId, this.env.model.getters.getUnboundedZone(activeSheetId, newZone)).rangeData,
26197
- });
26235
+ this.env.model.selection.selectZone({
26236
+ cell: { col: newZone.left, row: newZone.top },
26237
+ zone: newZone,
26238
+ }, { unbounded: true });
26198
26239
  currentZone = newZone;
26199
26240
  }
26200
26241
  }
26201
26242
  };
26202
26243
  const mouseUp = () => {
26203
26244
  this.highlightState.shiftingMode = "none";
26204
- // To do:
26205
- // Command used here to restore focus to the current composer,
26206
- // to be changed when refactoring the 'edition' plugin
26207
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26208
26245
  };
26209
26246
  dragAndDropBeyondTheViewport(this.env, mouseMove, mouseUp);
26210
26247
  }
@@ -26220,9 +26257,7 @@ class Highlight extends owl.Component {
26220
26257
  const deltaRowMin = -z.top;
26221
26258
  const deltaRowMax = this.env.model.getters.getNumberRows(activeSheetId) - z.bottom - 1;
26222
26259
  let currentZone = z;
26223
- this.env.model.dispatch("START_CHANGE_HIGHLIGHT", {
26224
- range: this.env.model.getters.getRangeDataFromZone(activeSheetId, currentZone),
26225
- });
26260
+ this.env.model.dispatch("START_CHANGE_HIGHLIGHT", { zone: currentZone });
26226
26261
  let lastCol = initCol;
26227
26262
  let lastRow = initRow;
26228
26263
  const mouseMove = (col, row) => {
@@ -26237,21 +26272,17 @@ class Highlight extends owl.Component {
26237
26272
  right: z.right + deltaCol,
26238
26273
  bottom: z.bottom + deltaRow,
26239
26274
  };
26240
- newZone = this.env.model.getters.expandZone(activeSheetId, newZone);
26241
26275
  if (!isEqual(newZone, currentZone)) {
26242
- this.env.model.dispatch("CHANGE_HIGHLIGHT", {
26243
- range: this.env.model.getters.getRangeFromZone(activeSheetId, this.env.model.getters.getUnboundedZone(activeSheetId, newZone)).rangeData,
26244
- });
26276
+ this.env.model.selection.selectZone({
26277
+ cell: { col: newZone.left, row: newZone.top },
26278
+ zone: newZone,
26279
+ }, { unbounded: true });
26245
26280
  currentZone = newZone;
26246
26281
  }
26247
26282
  }
26248
26283
  };
26249
26284
  const mouseUp = () => {
26250
26285
  this.highlightState.shiftingMode = "none";
26251
- // To do:
26252
- // Command used here to restore focus to the current composer,
26253
- // to be changed when refactoring the 'edition' plugin
26254
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26255
26286
  };
26256
26287
  dragAndDropBeyondTheViewport(this.env, mouseMove, mouseUp);
26257
26288
  }
@@ -26744,9 +26775,6 @@ class Grid extends owl.Component {
26744
26775
  // Zone selection with mouse
26745
26776
  // ---------------------------------------------------------------------------
26746
26777
  onCellClicked(col, row, { ctrlKey, shiftKey }) {
26747
- if (ctrlKey) {
26748
- this.env.model.dispatch("PREPARE_SELECTION_INPUT_EXPANSION");
26749
- }
26750
26778
  if (this.env.model.getters.hasOpenedPopover()) {
26751
26779
  this.closeOpenedPopover();
26752
26780
  }
@@ -26772,7 +26800,6 @@ class Grid extends owl.Component {
26772
26800
  }
26773
26801
  };
26774
26802
  const onMouseUp = () => {
26775
- this.env.model.dispatch("STOP_SELECTION_INPUT");
26776
26803
  if (this.env.model.getters.isPaintingFormat()) {
26777
26804
  this.env.model.dispatch("PASTE", {
26778
26805
  target: this.env.model.getters.getSelectedZones(),
@@ -31490,8 +31517,8 @@ class CellPlugin extends CorePlugin {
31490
31517
  "zoneToXC",
31491
31518
  "getCells",
31492
31519
  "getFormulaCellContent",
31520
+ "getTranslatedCellFormula",
31493
31521
  "getCellStyle",
31494
- "buildFormulaContent",
31495
31522
  "getCellById",
31496
31523
  ];
31497
31524
  nextId = 1;
@@ -31690,7 +31717,7 @@ class CellPlugin extends CorePlugin {
31690
31717
  /*
31691
31718
  * Reconstructs the original formula string based on a normalized form and its dependencies
31692
31719
  */
31693
- buildFormulaContent(sheetId, cell, dependencies) {
31720
+ getFormulaCellContent(sheetId, cell, dependencies) {
31694
31721
  const ranges = dependencies || [...cell.dependencies];
31695
31722
  return concat(cell.compiledFormula.tokens.map((token) => {
31696
31723
  if (token.type === "REFERENCE") {
@@ -31700,8 +31727,15 @@ class CellPlugin extends CorePlugin {
31700
31727
  return token.value;
31701
31728
  }));
31702
31729
  }
31703
- getFormulaCellContent(sheetId, cell) {
31704
- return this.buildFormulaContent(sheetId, cell);
31730
+ /*
31731
+ * Constructs a formula string based on an initial formula and a translation vector
31732
+ */
31733
+ getTranslatedCellFormula(sheetId, offsetX, offsetY, compiledFormula, dependencies) {
31734
+ const adaptedDependencies = this.getters.createAdaptedRanges(dependencies, offsetX, offsetY, sheetId);
31735
+ return this.getFormulaCellContent(sheetId, {
31736
+ compiledFormula,
31737
+ dependencies: adaptedDependencies,
31738
+ });
31705
31739
  }
31706
31740
  getCellStyle(position) {
31707
31741
  return this.getters.getCell(position)?.style || {};
@@ -31891,7 +31925,7 @@ class CellPlugin extends CorePlugin {
31891
31925
  */
31892
31926
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
31893
31927
  const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
31894
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.buildFormulaContent.bind(this));
31928
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getFormulaCellContent.bind(this));
31895
31929
  }
31896
31930
  createErrorFormula(id, content, format, style, error) {
31897
31931
  return {
@@ -31925,19 +31959,19 @@ class FormulaCellWithDependencies {
31925
31959
  style;
31926
31960
  dependencies;
31927
31961
  sheetId;
31928
- buildFormulaContent;
31962
+ getFormulaCellContent;
31929
31963
  isFormula = true;
31930
- constructor(id, compiledFormula, format, style, dependencies, sheetId, buildFormulaContent) {
31964
+ constructor(id, compiledFormula, format, style, dependencies, sheetId, getFormulaCellContent) {
31931
31965
  this.id = id;
31932
31966
  this.compiledFormula = compiledFormula;
31933
31967
  this.format = format;
31934
31968
  this.style = style;
31935
31969
  this.dependencies = dependencies;
31936
31970
  this.sheetId = sheetId;
31937
- this.buildFormulaContent = buildFormulaContent;
31971
+ this.getFormulaCellContent = getFormulaCellContent;
31938
31972
  }
31939
31973
  get content() {
31940
- return this.buildFormulaContent(this.sheetId, {
31974
+ return this.getFormulaCellContent(this.sheetId, {
31941
31975
  dependencies: this.dependencies,
31942
31976
  compiledFormula: this.compiledFormula,
31943
31977
  });
@@ -36452,13 +36486,21 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
36452
36486
  }
36453
36487
  break;
36454
36488
  case "CellIsRule":
36489
+ const formulas = cf.rule.values.map((value) => value.startsWith("=") ? compile(value) : undefined);
36455
36490
  for (let ref of cf.ranges) {
36456
36491
  const zone = this.getters.getRangeFromSheetXC(sheetId, ref).zone;
36457
36492
  for (let row = zone.top; row <= zone.bottom; row++) {
36458
36493
  for (let col = zone.left; col <= zone.right; col++) {
36459
- const pr = this.rulePredicate[cf.rule.type];
36460
- let cell = this.getters.getEvaluatedCell({ sheetId, col, row });
36461
- if (pr && pr(cell, cf.rule)) {
36494
+ const predicate = this.rulePredicate[cf.rule.type];
36495
+ const target = { sheetId, col, row };
36496
+ const values = cf.rule.values.map((value, i) => {
36497
+ const compiledFormula = formulas[i];
36498
+ if (compiledFormula) {
36499
+ return this.getters.getTranslatedCellFormula(sheetId, col - zone.left, row - zone.top, compiledFormula, compiledFormula.dependencies.map((d) => this.getters.getRangeFromSheetXC(sheetId, d)));
36500
+ }
36501
+ return value;
36502
+ });
36503
+ if (predicate && predicate(target, { ...cf.rule, values })) {
36462
36504
  if (!computedStyle[col])
36463
36505
  computedStyle[col] = [];
36464
36506
  // we must combine all the properties of all the CF rules applied to the given cell
@@ -36626,12 +36668,18 @@ class EvaluationConditionalFormatPlugin extends UIPlugin {
36626
36668
  * Execute the predicate to know if a conditional formatting rule should be applied to a cell
36627
36669
  */
36628
36670
  rulePredicate = {
36629
- CellIsRule: (cell, rule) => {
36671
+ CellIsRule: (target, rule) => {
36672
+ const cell = this.getters.getEvaluatedCell(target);
36630
36673
  if (cell.type === CellValueType.error) {
36631
36674
  return false;
36632
36675
  }
36633
36676
  const locale = this.getters.getLocale();
36634
- const values = rule.values.map((val) => parseLiteral(val, locale));
36677
+ const values = rule.values.map((value) => {
36678
+ if (value.startsWith("=")) {
36679
+ return this.getters.evaluateFormula(target.sheetId, value);
36680
+ }
36681
+ return parseLiteral(value, locale);
36682
+ });
36635
36683
  switch (rule.operator) {
36636
36684
  case "IsEmpty":
36637
36685
  return cell.value.toString().trim() === "";
@@ -38556,9 +38604,17 @@ class FindAndReplacePlugin extends UIPlugin {
38556
38604
  case "REPLACE_ALL_SEARCH":
38557
38605
  this.replaceAll(cmd.replaceWith);
38558
38606
  break;
38607
+ case "EVALUATE_CELLS":
38608
+ case "UPDATE_CELL":
38609
+ case "REMOVE_FILTER_TABLE":
38610
+ case "UPDATE_FILTER":
38611
+ this.isSearchDirty = true;
38612
+ break;
38559
38613
  case "UNDO":
38560
38614
  case "REDO":
38561
38615
  case "REMOVE_COLUMNS_ROWS":
38616
+ case "HIDE_COLUMNS_ROWS":
38617
+ case "UNHIDE_COLUMNS_ROWS":
38562
38618
  case "ADD_COLUMNS_ROWS":
38563
38619
  case "EVALUATE_CELLS":
38564
38620
  case "UPDATE_CELL":
@@ -38694,37 +38750,32 @@ class FindAndReplacePlugin extends UIPlugin {
38694
38750
  // ---------------------------------------------------------------------------
38695
38751
  // Replace
38696
38752
  // ---------------------------------------------------------------------------
38697
- /**
38698
- * Replace the value of the currently selected match
38699
- */
38700
- replace(replaceWith) {
38701
- if (this.selectedMatchIndex === null || !this.currentSearchRegex) {
38753
+ replaceMatch(selectedMatch, replaceWith) {
38754
+ if (!this.currentSearchRegex) {
38702
38755
  return;
38703
38756
  }
38704
- const matches = this.searchMatches;
38705
- const selectedMatch = matches[this.selectedMatchIndex];
38706
38757
  const sheetId = this.getters.getActiveSheetId();
38707
38758
  const cell = this.getters.getCell({ sheetId, ...selectedMatch });
38759
+ const { col, row } = selectedMatch;
38708
38760
  if (cell?.isFormula && !this.searchOptions.searchFormulas) {
38709
- this.selectNextCell(Direction.next);
38761
+ return;
38710
38762
  }
38711
- else {
38712
- const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
38713
- const toReplace = this.getSearchableString({
38714
- sheetId,
38715
- col: selectedMatch.col,
38716
- row: selectedMatch.row,
38717
- });
38718
- const newContent = toReplace.replace(replaceRegex, replaceWith);
38719
- this.dispatch("UPDATE_CELL", {
38720
- sheetId: this.getters.getActiveSheetId(),
38721
- col: selectedMatch.col,
38722
- row: selectedMatch.row,
38723
- content: canonicalizeContent(newContent, this.getters.getLocale()),
38724
- });
38725
- this.searchMatches.splice(this.selectedMatchIndex, 1);
38726
- this.selectNextCell(Direction.current);
38763
+ const replaceRegex = new RegExp(this.currentSearchRegex.source, this.currentSearchRegex.flags + "g");
38764
+ const toReplace = this.getSearchableString({ sheetId, col, row });
38765
+ const content = toReplace.replace(replaceRegex, replaceWith);
38766
+ const canonicalContent = canonicalizeContent(content, this.getters.getLocale());
38767
+ this.dispatch("UPDATE_CELL", { sheetId, col, row, content: canonicalContent });
38768
+ }
38769
+ /**
38770
+ * Replace the value of the currently selected match
38771
+ */
38772
+ replace(replaceWith) {
38773
+ if (this.selectedMatchIndex === null) {
38774
+ return;
38727
38775
  }
38776
+ const selectedMatch = this.searchMatches[this.selectedMatchIndex];
38777
+ this.replaceMatch(selectedMatch, replaceWith);
38778
+ this.selectNextCell(Direction.next);
38728
38779
  }
38729
38780
  /**
38730
38781
  * Apply the replace function to all the matches one time.
@@ -38732,7 +38783,7 @@ class FindAndReplacePlugin extends UIPlugin {
38732
38783
  replaceAll(replaceWith) {
38733
38784
  const matchCount = this.searchMatches.length;
38734
38785
  for (let i = 0; i < matchCount; i++) {
38735
- this.replace(replaceWith);
38786
+ this.replaceMatch(this.searchMatches[i], replaceWith);
38736
38787
  }
38737
38788
  }
38738
38789
  getSearchableString(position) {
@@ -39654,8 +39705,7 @@ class SelectionInputPlugin extends UIPlugin {
39654
39705
  static getters = [];
39655
39706
  ranges = [];
39656
39707
  focusedRangeIndex = null;
39657
- activeSheet;
39658
- willAddNewRange = false;
39708
+ inputSheetId;
39659
39709
  constructor(config, initialRanges, inputHasSingleRange) {
39660
39710
  if (inputHasSingleRange && initialRanges.length > 1) {
39661
39711
  throw new Error("Input with a single range cannot be instantiated with several range references.");
@@ -39663,7 +39713,7 @@ class SelectionInputPlugin extends UIPlugin {
39663
39713
  super(config);
39664
39714
  this.inputHasSingleRange = inputHasSingleRange;
39665
39715
  this.insertNewRange(0, initialRanges);
39666
- this.activeSheet = this.getters.getActiveSheetId();
39716
+ this.inputSheetId = this.getters.getActiveSheetId();
39667
39717
  if (this.ranges.length === 0) {
39668
39718
  this.insertNewRange(this.ranges.length, [""]);
39669
39719
  this.focusLast();
@@ -39689,11 +39739,33 @@ class SelectionInputPlugin extends UIPlugin {
39689
39739
  return 0 /* CommandResult.Success */;
39690
39740
  }
39691
39741
  handleEvent(event) {
39692
- const inputSheetId = this.activeSheet;
39693
- const sheetId = this.getters.getActiveSheetId();
39694
- const zone = event.anchor.zone;
39695
- const range = this.getters.getRangeFromZone(sheetId, event.options.unbounded ? this.getters.getUnboundedZone(sheetId, zone) : zone);
39696
- this.add([this.getters.getSelectionRangeString(range, inputSheetId)]);
39742
+ if (this.focusedRangeIndex === null) {
39743
+ return;
39744
+ }
39745
+ const inputSheetId = this.inputSheetId;
39746
+ const activeSheetId = this.getters.getActiveSheetId();
39747
+ const zone = event.options.unbounded
39748
+ ? this.getters.getUnboundedZone(activeSheetId, event.anchor.zone)
39749
+ : event.anchor.zone;
39750
+ const range = this.getters.getRangeFromZone(activeSheetId, zone);
39751
+ const willAddNewRange = event.mode === "newAnchor" &&
39752
+ !this.inputHasSingleRange &&
39753
+ this.ranges[this.focusedRangeIndex].xc.trim() !== "";
39754
+ if (willAddNewRange) {
39755
+ const xc = this.getters.getSelectionRangeString(range, inputSheetId);
39756
+ this.insertNewRange(this.ranges.length, [xc]);
39757
+ this.focusLast();
39758
+ }
39759
+ else {
39760
+ let parts = range.parts;
39761
+ const previousXc = this.ranges[this.focusedRangeIndex].xc.trim();
39762
+ if (previousXc) {
39763
+ parts = this.getters.getRangeFromSheetXC(inputSheetId, previousXc).parts;
39764
+ }
39765
+ const newRange = range.clone({ parts });
39766
+ const xc = this.getters.getSelectionRangeString(newRange, inputSheetId);
39767
+ this.setRange(this.focusedRangeIndex, [xc]);
39768
+ }
39697
39769
  }
39698
39770
  handle(cmd) {
39699
39771
  switch (cmd.type) {
@@ -39729,16 +39801,6 @@ class SelectionInputPlugin extends UIPlugin {
39729
39801
  this.removeRange(index);
39730
39802
  }
39731
39803
  break;
39732
- case "STOP_SELECTION_INPUT":
39733
- this.willAddNewRange = false;
39734
- break;
39735
- case "PREPARE_SELECTION_INPUT_EXPANSION": {
39736
- const index = this.focusedRangeIndex;
39737
- if (index !== null && !this.inputHasSingleRange) {
39738
- this.willAddNewRange = this.ranges[index].xc.trim() !== "";
39739
- }
39740
- break;
39741
- }
39742
39804
  case "ACTIVATE_SHEET": {
39743
39805
  if (cmd.sheetIdFrom !== cmd.sheetIdTo) {
39744
39806
  const { col, row } = this.getters.getNextVisibleCellPosition({
@@ -39749,7 +39811,26 @@ class SelectionInputPlugin extends UIPlugin {
39749
39811
  const zone = this.getters.expandZone(cmd.sheetIdTo, positionToZone({ col, row }));
39750
39812
  this.selection.resetAnchor(this, { cell: { col, row }, zone });
39751
39813
  }
39814
+ break;
39752
39815
  }
39816
+ case "START_CHANGE_HIGHLIGHT":
39817
+ const activeSheetId = this.getters.getActiveSheetId();
39818
+ const newZone = this.getters.expandZone(activeSheetId, cmd.zone);
39819
+ const focusIndex = this.ranges.findIndex((range) => {
39820
+ const { xc, sheetName: sheet } = splitReference(range.xc);
39821
+ const sheetName = sheet || this.getters.getSheetName(this.inputSheetId);
39822
+ if (this.getters.getSheetName(activeSheetId) !== sheetName) {
39823
+ return false;
39824
+ }
39825
+ const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
39826
+ return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), newZone);
39827
+ });
39828
+ if (focusIndex !== -1) {
39829
+ this.focus(focusIndex);
39830
+ const { left, top } = newZone;
39831
+ this.selection.resetAnchor(this, { cell: { col: left, row: top }, zone: newZone });
39832
+ }
39833
+ break;
39753
39834
  }
39754
39835
  }
39755
39836
  // ---------------------------------------------------------------------------
@@ -39778,19 +39859,6 @@ class SelectionInputPlugin extends UIPlugin {
39778
39859
  unfocus() {
39779
39860
  this.focusedRangeIndex = null;
39780
39861
  }
39781
- add(newRanges) {
39782
- if (this.focusedRangeIndex === null || newRanges.length === 0) {
39783
- return;
39784
- }
39785
- if (this.willAddNewRange) {
39786
- this.insertNewRange(this.ranges.length, newRanges);
39787
- this.focusLast();
39788
- this.willAddNewRange = false;
39789
- }
39790
- else {
39791
- this.setRange(this.focusedRangeIndex, newRanges);
39792
- }
39793
- }
39794
39862
  setContent(index, xc) {
39795
39863
  this.ranges[index] = {
39796
39864
  ...this.ranges[index],
@@ -39836,12 +39904,12 @@ class SelectionInputPlugin extends UIPlugin {
39836
39904
  inputToHighlights({ xc, color }) {
39837
39905
  const XCs = this.cleanInputs([xc])
39838
39906
  .filter((range) => this.getters.isRangeValid(range))
39839
- .filter((reference) => this.shouldBeHighlighted(this.activeSheet, reference));
39907
+ .filter((reference) => this.shouldBeHighlighted(this.inputSheetId, reference));
39840
39908
  return XCs.map((xc) => {
39841
39909
  const { sheetName } = splitReference(xc);
39842
39910
  return {
39843
- zone: this.getters.getRangeFromSheetXC(this.activeSheet, xc).zone,
39844
- sheetId: (sheetName && this.getters.getSheetIdByName(sheetName)) || this.activeSheet,
39911
+ zone: this.getters.getRangeFromSheetXC(this.inputSheetId, xc).zone,
39912
+ sheetId: (sheetName && this.getters.getSheetIdByName(sheetName)) || this.inputSheetId,
39845
39913
  color,
39846
39914
  };
39847
39915
  });
@@ -40266,10 +40334,8 @@ class SortPlugin extends UIPlugin {
40266
40334
  let content = cell.content;
40267
40335
  if (cell.isFormula) {
40268
40336
  const position = this.getters.getCellPosition(cell.id);
40269
- const offsetY = newRow - position.row;
40270
40337
  // we only have a vertical offset
40271
- const ranges = this.getters.createAdaptedRanges(cell.dependencies, 0, offsetY, sheetId);
40272
- content = this.getters.buildFormulaContent(sheetId, cell, ranges);
40338
+ content = this.getters.getTranslatedCellFormula(sheetId, 0, newRow - position.row, cell.compiledFormula, cell.dependencies);
40273
40339
  }
40274
40340
  newCellValues.style = cell.style;
40275
40341
  newCellValues.content = content;
@@ -41376,9 +41442,7 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
41376
41442
  }
41377
41443
  let content = origin.cell.content;
41378
41444
  if (origin.cell.isFormula && operation === "COPY") {
41379
- const offsetX = col - origin.position.col;
41380
- const offsetY = row - origin.position.row;
41381
- content = this.getUpdatedContent(sheetId, origin.cell, offsetX, offsetY, operation);
41445
+ content = this.getters.getTranslatedCellFormula(sheetId, col - origin.position.col, row - origin.position.row, origin.cell.compiledFormula, origin.cell.dependencies);
41382
41446
  }
41383
41447
  this.dispatch("UPDATE_CELL", {
41384
41448
  ...target,
@@ -41399,13 +41463,6 @@ class ClipboardCellsState extends ClipboardCellsAbstractState {
41399
41463
  }
41400
41464
  }
41401
41465
  }
41402
- /**
41403
- * Get the newly updated formula, after applying offsets
41404
- */
41405
- getUpdatedContent(sheetId, cell, offsetX, offsetY, operation) {
41406
- const ranges = this.getters.createAdaptedRanges(cell.dependencies, offsetX, offsetY, sheetId);
41407
- return this.getters.buildFormulaContent(sheetId, cell, ranges);
41408
- }
41409
41466
  /**
41410
41467
  * If the origin position given is the top left of a merge, merge the target
41411
41468
  * position.
@@ -42147,6 +42204,7 @@ class EditionPlugin extends UIPlugin {
42147
42204
  "getTokenAtCursor",
42148
42205
  "getComposerHighlights",
42149
42206
  "getCurrentEditedCell",
42207
+ "getCycledReference",
42150
42208
  ];
42151
42209
  col = 0;
42152
42210
  row = 0;
@@ -42156,10 +42214,7 @@ class EditionPlugin extends UIPlugin {
42156
42214
  currentTokens = [];
42157
42215
  selectionStart = 0;
42158
42216
  selectionEnd = 0;
42159
- selectionInitialStart = 0;
42160
42217
  initialContent = "";
42161
- previousRef = "";
42162
- previousRange = undefined;
42163
42218
  colorIndexByRange = {};
42164
42219
  // ---------------------------------------------------------------------------
42165
42220
  // Command Handling
@@ -42188,9 +42243,6 @@ class EditionPlugin extends UIPlugin {
42188
42243
  }
42189
42244
  }
42190
42245
  handleEvent(event) {
42191
- if (this.mode !== "selecting") {
42192
- return;
42193
- }
42194
42246
  const sheetId = this.getters.getActiveSheetId();
42195
42247
  let unboundedZone;
42196
42248
  if (event.options.unbounded) {
@@ -42201,10 +42253,17 @@ class EditionPlugin extends UIPlugin {
42201
42253
  }
42202
42254
  switch (event.mode) {
42203
42255
  case "newAnchor":
42204
- this.insertSelectedRange(unboundedZone);
42256
+ if (this.mode === "selecting") {
42257
+ this.insertSelectedRange(unboundedZone);
42258
+ }
42205
42259
  break;
42206
42260
  default:
42207
- this.replaceSelectedRanges(unboundedZone);
42261
+ if (this.mode === "selecting") {
42262
+ this.replaceSelectedRange(unboundedZone);
42263
+ }
42264
+ else {
42265
+ this.updateComposerRange(event.previousAnchor.zone, unboundedZone);
42266
+ }
42208
42267
  break;
42209
42268
  }
42210
42269
  }
@@ -42261,35 +42320,12 @@ class EditionPlugin extends UIPlugin {
42261
42320
  }
42262
42321
  break;
42263
42322
  case "START_CHANGE_HIGHLIGHT":
42264
- // FIXME: thiws whole ordeal could be handled with the Selection Processor which would extend the feature to selection inputs
42265
- this.dispatch("STOP_COMPOSER_RANGE_SELECTION");
42266
- // FIXME: we should check range SheetId compared to this.activeSheetId r maybe not have a sheetId in the payload ??
42267
- const range = this.getters.getRangeFromRangeData(cmd.range);
42268
- const previousRefToken = this.currentTokens
42269
- .filter((token) => token.type === "REFERENCE")
42270
- .find((token) => {
42271
- const { xc, sheetName: sheet } = splitReference(token.value);
42272
- const sheetName = sheet || this.getters.getSheetName(this.sheetId);
42273
- const activeSheetId = this.getters.getActiveSheetId();
42274
- if (this.getters.getSheetName(activeSheetId) !== sheetName) {
42275
- return false;
42276
- }
42277
- const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
42278
- return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), range.zone);
42279
- });
42280
- this.previousRef = previousRefToken.value;
42281
- this.previousRange = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), this.previousRef);
42282
- this.selectionInitialStart = previousRefToken.start;
42283
- break;
42284
- case "CHANGE_HIGHLIGHT":
42285
- const cmdRange = this.getters.getRangeFromRangeData(cmd.range);
42286
- const newRef = this.getRangeReference(cmdRange, this.previousRange.parts);
42287
- this.selectionStart = this.selectionInitialStart;
42288
- this.selectionEnd = this.selectionInitialStart + this.previousRef.length;
42289
- this.replaceSelection(newRef);
42290
- this.previousRef = newRef;
42291
- this.selectionStart = this.currentContent.length;
42292
- this.selectionEnd = this.currentContent.length;
42323
+ const { left, top } = cmd.zone;
42324
+ // changing the highlight can conflit with the 'selecting' mode
42325
+ if (this.isSelectingForComposer()) {
42326
+ this.mode = "editing";
42327
+ }
42328
+ this.selection.resetAnchor(this, { cell: { col: left, row: top }, zone: cmd.zone });
42293
42329
  break;
42294
42330
  case "ACTIVATE_SHEET":
42295
42331
  if (!this.currentContent.startsWith("=")) {
@@ -42373,33 +42409,45 @@ class EditionPlugin extends UIPlugin {
42373
42409
  return this.currentTokens.find((t) => t.start <= start && t.end >= end);
42374
42410
  }
42375
42411
  }
42376
- // ---------------------------------------------------------------------------
42377
- // Misc
42378
- // ---------------------------------------------------------------------------
42379
- cycleReferences() {
42380
- const tokens = this.getTokensInSelection();
42412
+ /**
42413
+ * Return the cycled reference if any (A1 -> $A$1 -> A$1 -> $A1 -> A1)
42414
+ */
42415
+ getCycledReference(selection, content) {
42416
+ const locale = this.getters.getLocale();
42417
+ const currentTokens = content.startsWith("=") ? composerTokenize(content, locale) : [];
42418
+ const tokens = currentTokens.filter((t) => (t.start <= selection.start && t.end >= selection.start) ||
42419
+ (t.start >= selection.start && t.start < selection.end));
42381
42420
  const refTokens = tokens.filter((token) => token.type === "REFERENCE");
42382
- if (refTokens.length === 0)
42421
+ if (refTokens.length === 0) {
42383
42422
  return;
42423
+ }
42384
42424
  const updatedReferences = tokens
42385
42425
  .map(loopThroughReferenceType)
42386
42426
  .map((token) => token.value)
42387
42427
  .join("");
42388
- const content = this.currentContent;
42389
42428
  const start = tokens[0].start;
42390
42429
  const end = tokens[tokens.length - 1].end;
42391
42430
  const newContent = content.slice(0, start) + updatedReferences + content.slice(end);
42392
42431
  const lengthDiff = newContent.length - content.length;
42393
42432
  const startOfTokens = refTokens[0].start;
42394
42433
  const endOfTokens = refTokens[refTokens.length - 1].end + lengthDiff;
42395
- const selection = { start: startOfTokens, end: endOfTokens };
42396
- // Put the selection at the end of the token if we cycled on a single token
42397
- if (refTokens.length === 1 && this.selectionStart === this.selectionEnd) {
42398
- selection.start = selection.end;
42434
+ const newSelection = { start: startOfTokens, end: endOfTokens };
42435
+ if (refTokens.length === 1 && selection.start === selection.end) {
42436
+ newSelection.start = newSelection.end;
42437
+ }
42438
+ return { content: newContent, selection: newSelection };
42439
+ }
42440
+ // ---------------------------------------------------------------------------
42441
+ // Misc
42442
+ // ---------------------------------------------------------------------------
42443
+ cycleReferences() {
42444
+ const updated = this.getCycledReference(this.getComposerSelection(), this.currentContent);
42445
+ if (updated === undefined) {
42446
+ return;
42399
42447
  }
42400
42448
  this.dispatch("SET_CURRENT_CONTENT", {
42401
- content: newContent,
42402
- selection,
42449
+ content: updated.content,
42450
+ selection: updated.selection,
42403
42451
  });
42404
42452
  }
42405
42453
  validateSelection(length, start, end) {
@@ -42447,7 +42495,6 @@ class EditionPlugin extends UIPlugin {
42447
42495
  this.selection.resetAnchor(this, { cell: { col: this.col, row: this.row }, zone });
42448
42496
  }
42449
42497
  this.mode = "selecting";
42450
- this.selectionInitialStart = this.selectionStart;
42451
42498
  }
42452
42499
  /**
42453
42500
  * start the edition of a cell
@@ -42607,19 +42654,49 @@ class EditionPlugin extends UIPlugin {
42607
42654
  const ref = this.getZoneReference(zone);
42608
42655
  if (this.canStartComposerRangeSelection()) {
42609
42656
  this.insertText(ref, start);
42610
- this.selectionInitialStart = start;
42611
42657
  }
42612
42658
  else {
42613
42659
  this.insertText("," + ref, start);
42614
- this.selectionInitialStart = start + 1;
42615
42660
  }
42616
42661
  }
42617
42662
  /**
42618
42663
  * Replace the current reference selected by the new one.
42619
42664
  * */
42620
- replaceSelectedRanges(zone) {
42665
+ replaceSelectedRange(zone) {
42621
42666
  const ref = this.getZoneReference(zone);
42622
- this.replaceText(ref, this.selectionInitialStart, this.selectionEnd);
42667
+ const currentToken = this.getTokenAtCursor();
42668
+ const start = currentToken?.type === "REFERENCE" ? currentToken.start : this.selectionStart;
42669
+ this.replaceText(ref, start, this.selectionEnd);
42670
+ }
42671
+ /**
42672
+ * Replace the reference of the old zone by the new one.
42673
+ */
42674
+ updateComposerRange(oldZone, newZone) {
42675
+ const activeSheetId = this.getters.getActiveSheetId();
42676
+ const tokentAtCursor = this.getTokenAtCursor();
42677
+ const tokens = tokentAtCursor ? [tokentAtCursor, ...this.currentTokens] : this.currentTokens;
42678
+ const previousRefToken = tokens
42679
+ .filter((token) => token.type === "REFERENCE")
42680
+ .find((token) => {
42681
+ const { xc, sheetName: sheet } = splitReference(token.value);
42682
+ const sheetName = sheet || this.getters.getSheetName(this.sheetId);
42683
+ if (this.getters.getSheetName(activeSheetId) !== sheetName) {
42684
+ return false;
42685
+ }
42686
+ const refRange = this.getters.getRangeFromSheetXC(activeSheetId, xc);
42687
+ return isEqual(this.getters.expandZone(activeSheetId, refRange.zone), oldZone);
42688
+ });
42689
+ // this function assumes that the previous range is always found because
42690
+ // it's called when changing a highlight, which exists by definition
42691
+ if (!previousRefToken) {
42692
+ throw new Error("Previous range not found");
42693
+ }
42694
+ const previousRange = this.getters.getRangeFromSheetXC(activeSheetId, previousRefToken.value);
42695
+ this.selectionStart = previousRefToken.start;
42696
+ this.selectionEnd = this.selectionStart + previousRefToken.value.length;
42697
+ const newRange = this.getters.getRangeFromZone(activeSheetId, newZone);
42698
+ const newRef = this.getRangeReference(newRange, previousRange.parts);
42699
+ this.replaceSelection(newRef);
42623
42700
  }
42624
42701
  getZoneReference(zone) {
42625
42702
  const inputSheetId = this.getters.getCurrentEditedCell().sheetId;
@@ -42627,14 +42704,8 @@ class EditionPlugin extends UIPlugin {
42627
42704
  const range = this.getters.getRangeFromZone(sheetId, zone);
42628
42705
  return this.getters.getSelectionRangeString(range, inputSheetId);
42629
42706
  }
42630
- getRangeReference(range, fixedParts = [{ colFixed: false, rowFixed: false }]) {
42707
+ getRangeReference(range, fixedParts) {
42631
42708
  let _fixedParts = [...fixedParts];
42632
- if (fixedParts.length === 1 && getZoneArea(range.zone) > 1) {
42633
- _fixedParts.push({ ...fixedParts[0] });
42634
- }
42635
- else if (fixedParts.length === 2 && getZoneArea(range.zone) === 1) {
42636
- _fixedParts.pop();
42637
- }
42638
42709
  const newRange = range.clone({ parts: _fixedParts });
42639
42710
  return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell().sheetId);
42640
42711
  }
@@ -42761,15 +42832,6 @@ class EditionPlugin extends UIPlugin {
42761
42832
  }
42762
42833
  return false;
42763
42834
  }
42764
- /**
42765
- * Return all the tokens between selectionStart and selectionEnd.
42766
- * Includes token that begin right on selectionStart or end right on selectionEnd.
42767
- */
42768
- getTokensInSelection() {
42769
- const start = Math.min(this.selectionStart, this.selectionEnd);
42770
- const end = Math.max(this.selectionStart, this.selectionEnd);
42771
- return this.currentTokens.filter((t) => (t.start <= start && t.end >= start) || (t.start >= start && t.start < end));
42772
- }
42773
42835
  }
42774
42836
 
42775
42837
  class FilterEvaluationPlugin extends UIPlugin {
@@ -50303,10 +50365,17 @@ class Model extends EventBus {
50303
50365
  * It will call `beforeHandle` and `handle`
50304
50366
  */
50305
50367
  dispatchToHandlers(handlers, command) {
50368
+ const isCommandCore = isCoreCommand(command);
50306
50369
  for (const handler of handlers) {
50370
+ if (!isCommandCore && handler instanceof CorePlugin) {
50371
+ continue;
50372
+ }
50307
50373
  handler.beforeHandle(command);
50308
50374
  }
50309
50375
  for (const handler of handlers) {
50376
+ if (!isCommandCore && handler instanceof CorePlugin) {
50377
+ continue;
50378
+ }
50310
50379
  handler.handle(command);
50311
50380
  }
50312
50381
  }
@@ -50492,6 +50561,9 @@ const components = {
50492
50561
  };
50493
50562
  function addFunction(functionName, functionDescription) {
50494
50563
  functionRegistry.add(functionName, functionDescription);
50564
+ return {
50565
+ addFunction: (fName, fDescription) => addFunction(fName, fDescription),
50566
+ };
50495
50567
  }
50496
50568
  const constants = {
50497
50569
  DEFAULT_LOCALE,
@@ -50532,6 +50604,6 @@ exports.setTranslationMethod = setTranslationMethod;
50532
50604
  exports.tokenize = tokenize;
50533
50605
 
50534
50606
 
50535
- __info__.version = '16.5.0-alpha.1';
50536
- __info__.date = '2023-07-26T12:11:43.109Z';
50537
- __info__.hash = '9160731';
50607
+ __info__.version = '16.5.0-alpha.2';
50608
+ __info__.date = '2023-08-07T09:02:00.623Z';
50609
+ __info__.hash = 'e74f3ef';