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