@odoo/o-spreadsheet 17.1.3 → 17.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.1.3
6
- * @date 2024-02-02T13:46:54.332Z
7
- * @hash 798f979
5
+ * @version 17.1.5
6
+ * @date 2024-02-16T14:23:54.651Z
7
+ * @hash 29d8ad6
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -452,8 +452,8 @@
452
452
  * If the given item does not exist in the dictionary, it creates one with a new id.
453
453
  */
454
454
  function getItemId(item, itemsDic) {
455
- for (let [key, value] of Object.entries(itemsDic)) {
456
- if (deepEquals(value, item)) {
455
+ for (const key in itemsDic) {
456
+ if (deepEquals(itemsDic[key], item)) {
457
457
  return parseInt(key, 10);
458
458
  }
459
459
  }
@@ -552,11 +552,13 @@
552
552
  if (typeof o1 !== "object")
553
553
  return o1 === o2;
554
554
  // Objects can have different keys if the values are undefined
555
- const keys = new Set();
556
- Object.keys(o1).forEach((key) => keys.add(key));
557
- Object.keys(o2).forEach((key) => keys.add(key));
558
- for (let key of keys) {
559
- if (typeof o1[key] !== typeof o1[key])
555
+ for (const key in o2) {
556
+ if (!(key in o1) && o2[key] !== undefined) {
557
+ return false;
558
+ }
559
+ }
560
+ for (const key in o1) {
561
+ if (typeof o1[key] !== typeof o2[key])
560
562
  return false;
561
563
  if (typeof o1[key] === "object") {
562
564
  if (!deepEquals(o1[key], o2[key]))
@@ -626,18 +628,18 @@
626
628
  return true;
627
629
  }
628
630
  class JetSet extends Set {
629
- add(...iterable) {
631
+ addMany(iterable) {
630
632
  for (const element of iterable) {
631
633
  super.add(element);
632
634
  }
633
635
  return this;
634
636
  }
635
- delete(...iterable) {
636
- let deleted = false;
637
+ deleteMany(iterable) {
638
+ let wasDeleted = false;
637
639
  for (const element of iterable) {
638
- deleted ||= super.delete(element);
640
+ wasDeleted ||= super.delete(element);
639
641
  }
640
- return deleted;
642
+ return wasDeleted;
641
643
  }
642
644
  }
643
645
  /**
@@ -1935,7 +1937,7 @@
1935
1937
  * Static helper which returns a successful DispatchResult
1936
1938
  */
1937
1939
  static get Success() {
1938
- return new DispatchResult();
1940
+ return SUCCESS;
1939
1941
  }
1940
1942
  get isSuccessful() {
1941
1943
  return this.reasons.length === 0;
@@ -1948,6 +1950,7 @@
1948
1950
  return this.reasons.includes(reason);
1949
1951
  }
1950
1952
  }
1953
+ const SUCCESS = new DispatchResult();
1951
1954
  exports.CommandResult = void 0;
1952
1955
  (function (CommandResult) {
1953
1956
  CommandResult["Success"] = "Success";
@@ -2939,6 +2942,9 @@
2939
2942
  }
2940
2943
  switch (typeof value) {
2941
2944
  case "string":
2945
+ if (value.includes('\\"')) {
2946
+ return value.replace(/\\"/g, '"');
2947
+ }
2942
2948
  return value;
2943
2949
  case "boolean":
2944
2950
  return value ? "TRUE" : "FALSE";
@@ -4992,14 +4998,8 @@
4992
4998
  : undefined));
4993
4999
  }
4994
5000
  }
4995
- else if (zone.left === zone.right && zone.top === zone.bottom) {
4996
- // A single cell. If it's only the title, the dataset is not added.
4997
- if (!dataSetsHaveTitle) {
4998
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
4999
- }
5000
- }
5001
5001
  else {
5002
- /* 1 row or 1 column */
5002
+ /* 1 cell, 1 row or 1 column */
5003
5003
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
5004
5004
  ? {
5005
5005
  top: zone.top,
@@ -7442,7 +7442,7 @@
7442
7442
  while (!chars.isOver()) {
7443
7443
  let token = tokenizeSpace(chars) ||
7444
7444
  tokenizeArgsSeparator(chars, locale) ||
7445
- tokenizeMisc(chars) ||
7445
+ tokenizeParenthesis(chars) ||
7446
7446
  tokenizeOperator(chars) ||
7447
7447
  tokenizeString(chars) ||
7448
7448
  tokenizeDebugger(chars) ||
@@ -7463,15 +7463,14 @@
7463
7463
  }
7464
7464
  return null;
7465
7465
  }
7466
- const misc$1 = {
7467
- "(": "LEFT_PAREN",
7468
- ")": "RIGHT_PAREN",
7466
+ const parenthesis = {
7467
+ "(": { type: "LEFT_PAREN", value: "(" },
7468
+ ")": { type: "RIGHT_PAREN", value: ")" },
7469
7469
  };
7470
- function tokenizeMisc(chars) {
7471
- if (chars.current in misc$1) {
7470
+ function tokenizeParenthesis(chars) {
7471
+ if (chars.current === "(" || chars.current === ")") {
7472
7472
  const value = chars.shift();
7473
- const type = misc$1[value];
7474
- return { type, value };
7473
+ return parenthesis[value];
7475
7474
  }
7476
7475
  return null;
7477
7476
  }
@@ -7492,7 +7491,12 @@
7492
7491
  }
7493
7492
  return null;
7494
7493
  }
7494
+ const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
7495
7495
  function tokenizeNumber(chars, locale) {
7496
+ if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
7497
+ chars.current !== locale.decimalSeparator) {
7498
+ return null;
7499
+ }
7496
7500
  const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
7497
7501
  if (match) {
7498
7502
  chars.advanceBy(match[0].length);
@@ -7517,7 +7521,7 @@
7517
7521
  }
7518
7522
  return null;
7519
7523
  }
7520
- const separatorRegexp = /\w|\.|!|\$/;
7524
+ const separatorRegexp = /^[\w\.!\$]+/;
7521
7525
  /**
7522
7526
  * A "Symbol" is just basically any word-like element that can appear in a
7523
7527
  * formula, which is not a string. So:
@@ -7557,8 +7561,11 @@
7557
7561
  };
7558
7562
  }
7559
7563
  }
7560
- while (chars.current && separatorRegexp.test(chars.current)) {
7561
- result += chars.shift();
7564
+ const match = chars.remaining().match(separatorRegexp);
7565
+ if (match) {
7566
+ const value = match[0];
7567
+ result += value;
7568
+ chars.advanceBy(value.length);
7562
7569
  }
7563
7570
  if (result.length) {
7564
7571
  const value = result;
@@ -7620,7 +7627,10 @@
7620
7627
  return this.text.substring(this.currentIndex);
7621
7628
  }
7622
7629
  currentStartsWith(str) {
7623
- for (let j = 0; j < str.length; j++) {
7630
+ if (this.current !== str[0]) {
7631
+ return false;
7632
+ }
7633
+ for (let j = 1; j < str.length; j++) {
7624
7634
  if (this.text[this.currentIndex + j] !== str[j]) {
7625
7635
  return false;
7626
7636
  }
@@ -7647,6 +7657,9 @@
7647
7657
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7648
7658
  return false;
7649
7659
  }
7660
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
7661
+ return false;
7662
+ }
7650
7663
  try {
7651
7664
  formatValue(1, { locale, format: "#,##0.00" });
7652
7665
  formatValue(1, { locale, format: locale.dateFormat });
@@ -7737,7 +7750,7 @@
7737
7750
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7738
7751
  return content;
7739
7752
  }
7740
- return content.replace(locale.decimalSeparator, ".");
7753
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7741
7754
  }
7742
7755
  /**
7743
7756
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -8652,7 +8665,7 @@
8652
8665
  return undefined;
8653
8666
  const dataSets = this.dataSets
8654
8667
  .map((ds) => toExcelDataset(this.getters, ds))
8655
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
8668
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
8656
8669
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
8657
8670
  return {
8658
8671
  ...this.getDefinition(),
@@ -9230,7 +9243,7 @@
9230
9243
  return undefined;
9231
9244
  const dataSets = this.dataSets
9232
9245
  .map((ds) => toExcelDataset(this.getters, ds))
9233
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
9246
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
9234
9247
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
9235
9248
  return {
9236
9249
  ...this.getDefinition(),
@@ -9536,7 +9549,7 @@
9536
9549
  return undefined;
9537
9550
  const dataSets = this.dataSets
9538
9551
  .map((ds) => toExcelDataset(this.getters, ds))
9539
- .filter((ds) => ds.range !== ""); // && range !== CellErrorType.InvalidReference ? show incorrect #ref ?
9552
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
9540
9553
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
9541
9554
  return {
9542
9555
  ...this.getDefinition(),
@@ -10832,6 +10845,12 @@
10832
10845
  let { col, row } = env.model.getters.getActivePosition();
10833
10846
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
10834
10847
  };
10848
+ const INSERT_LINK_NAME = (env) => {
10849
+ const sheetId = env.model.getters.getActiveSheetId();
10850
+ const { col, row } = env.model.getters.getActivePosition();
10851
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
10852
+ return cell && cell.link ? _t("Edit link") : _t("Insert link");
10853
+ };
10835
10854
  //------------------------------------------------------------------------------
10836
10855
  // Filters action
10837
10856
  //------------------------------------------------------------------------------
@@ -18683,6 +18702,9 @@
18683
18702
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18684
18703
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18685
18704
  assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18705
+ if (searchKey && isEvaluationError(searchKey.value)) {
18706
+ return searchKey;
18707
+ }
18686
18708
  const getValueFromRange = (range, index) => range[index][0].value;
18687
18709
  const _isSorted = toBoolean(isSorted.value);
18688
18710
  const colIndex = _isSorted
@@ -18850,6 +18872,9 @@
18850
18872
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18851
18873
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18852
18874
  assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18875
+ if (searchKey && isEvaluationError(searchKey.value)) {
18876
+ return searchKey;
18877
+ }
18853
18878
  const getValueFromRange = (range, index) => range[0][index].value;
18854
18879
  const _isSorted = toBoolean(isSorted.value);
18855
18880
  const rowIndex = _isSorted
@@ -18889,6 +18914,9 @@
18889
18914
  assert(() => lookupDirection === "col"
18890
18915
  ? returnRange[0].length === lookupRange[0].length
18891
18916
  : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18917
+ if (searchKey && isEvaluationError(searchKey.value)) {
18918
+ return [[searchKey]];
18919
+ }
18892
18920
  const getElement = lookupDirection === "col"
18893
18921
  ? (range, index) => range[0][index].value
18894
18922
  : (range, index) => range[index][0].value;
@@ -20067,7 +20095,7 @@
20067
20095
  })
20068
20096
  .add("insert_link", {
20069
20097
  ...insertLink,
20070
- name: _t("Insert link"),
20098
+ name: INSERT_LINK_NAME,
20071
20099
  sequence: 150,
20072
20100
  separator: true,
20073
20101
  });
@@ -26510,6 +26538,17 @@
26510
26538
  onValueSelected: Function,
26511
26539
  onValueHovered: Function,
26512
26540
  };
26541
+ autoCompleteListRef = owl.useRef("autoCompleteList");
26542
+ setup() {
26543
+ owl.useEffect(() => {
26544
+ const selectedIndex = this.props.selectedIndex;
26545
+ if (selectedIndex === undefined) {
26546
+ return;
26547
+ }
26548
+ const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
26549
+ selectedElement?.scrollIntoView?.({ block: "nearest" });
26550
+ }, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
26551
+ }
26513
26552
  }
26514
26553
 
26515
26554
  class ContentEditableHelper {
@@ -26954,6 +26993,7 @@
26954
26993
  position: absolute;
26955
26994
  margin: 1px 4px;
26956
26995
  pointer-events: none;
26996
+ overflow: auto;
26957
26997
 
26958
26998
  .o-semi-bold {
26959
26999
  /** FIXME: to remove in favor of Bootstrap
@@ -26970,15 +27010,17 @@
26970
27010
  focus: {
26971
27011
  validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
26972
27012
  },
26973
- onComposerContentFocused: Function,
26974
27013
  inputStyle: { type: String, optional: true },
26975
27014
  rect: { type: Object, optional: true },
26976
27015
  delimitation: { type: Object, optional: true },
26977
- onComposerUnmounted: { type: Function, optional: true },
27016
+ onComposerCellFocused: { type: Function, optional: true },
27017
+ onComposerContentFocused: Function,
27018
+ isDefaultFocus: { type: Boolean, optional: true },
26978
27019
  };
26979
27020
  static components = { TextValueProvider, FunctionDescriptionProvider };
26980
27021
  static defaultProps = {
26981
27022
  inputStyle: "",
27023
+ isDefaultFocus: false,
26982
27024
  };
26983
27025
  composerRef = owl.useRef("o_composer");
26984
27026
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -27009,7 +27051,10 @@
27009
27051
  if (this.props.delimitation && this.props.rect) {
27010
27052
  const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
27011
27053
  const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
27054
+ assistantStyle["max-height"] = `${remainingHeight}px`;
27012
27055
  if (cellY > remainingHeight) {
27056
+ const availableSpaceAbove = cellY;
27057
+ assistantStyle["max-height"] = `${availableSpaceAbove}px`;
27013
27058
  // render top
27014
27059
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
27015
27060
  assistantStyle.top = `-3px`;
@@ -27035,7 +27080,6 @@
27035
27080
  F4: this.processF4Key,
27036
27081
  Tab: (ev) => this.processTabKey(ev, "right"),
27037
27082
  "Shift+Tab": (ev) => this.processTabKey(ev, "left"),
27038
- "Ctrl+ ": this.processSpaceKey,
27039
27083
  };
27040
27084
  keyCodeMapping = {
27041
27085
  NumpadDecimal: this.processNumpadDecimal,
@@ -27043,12 +27087,12 @@
27043
27087
  setup() {
27044
27088
  owl.onMounted(() => {
27045
27089
  const el = this.composerRef.el;
27090
+ if (this.props.isDefaultFocus) {
27091
+ this.env.focusableElement.setFocusableElement(el);
27092
+ }
27046
27093
  this.contentHelper.updateEl(el);
27047
27094
  this.processTokenAtCursor();
27048
27095
  });
27049
- owl.onWillUnmount(() => {
27050
- this.props.onComposerUnmounted?.();
27051
- });
27052
27096
  owl.useEffect(() => {
27053
27097
  this.processContent();
27054
27098
  });
@@ -27057,7 +27101,8 @@
27057
27101
  // Handlers
27058
27102
  // ---------------------------------------------------------------------------
27059
27103
  processArrowKeys(ev) {
27060
- if (this.env.model.getters.isSelectingForComposer()) {
27104
+ if (this.env.model.getters.isSelectingForComposer() ||
27105
+ this.env.model.getters.getEditionMode() === "inactive") {
27061
27106
  this.functionDescriptionState.showDescription = false;
27062
27107
  // Prevent the default content editable behavior which moves the cursor
27063
27108
  ev.preventDefault();
@@ -27100,23 +27145,19 @@
27100
27145
  processTabKey(ev, direction) {
27101
27146
  ev.preventDefault();
27102
27147
  ev.stopPropagation();
27103
- const state = this.autoCompleteState;
27104
- if (state.showProvider && state.selectedIndex !== undefined) {
27105
- const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
27106
- if (autoCompleteValue) {
27107
- this.autoComplete(autoCompleteValue);
27108
- return;
27148
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
27149
+ const state = this.autoCompleteState;
27150
+ if (state.showProvider && state.selectedIndex !== undefined) {
27151
+ const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
27152
+ if (autoCompleteValue) {
27153
+ this.autoComplete(autoCompleteValue);
27154
+ return;
27155
+ }
27109
27156
  }
27157
+ interactiveStopEdition(this.env);
27110
27158
  }
27111
- interactiveStopEdition(this.env);
27112
27159
  this.env.model.selection.moveAnchorCell(direction, 1);
27113
27160
  }
27114
- processSpaceKey(ev) {
27115
- ev.preventDefault();
27116
- ev.stopPropagation();
27117
- this.showFunctionAutocomplete("");
27118
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27119
- }
27120
27161
  processEnterKey(ev, direction) {
27121
27162
  ev.preventDefault();
27122
27163
  ev.stopPropagation();
@@ -27178,6 +27219,9 @@
27178
27219
  this.compositionActive = false;
27179
27220
  }
27180
27221
  onKeydown(ev) {
27222
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27223
+ return;
27224
+ }
27181
27225
  if (ev.key.startsWith("Arrow")) {
27182
27226
  this.processArrowKeys(ev);
27183
27227
  return;
@@ -27191,14 +27235,33 @@
27191
27235
  ev.stopPropagation();
27192
27236
  }
27193
27237
  }
27238
+ onPaste(ev) {
27239
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
27240
+ ev.stopPropagation();
27241
+ }
27242
+ }
27194
27243
  /*
27195
27244
  * Triggered automatically by the content-editable between the keydown and key up
27196
27245
  * */
27197
27246
  onInput(ev) {
27198
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
27247
+ if (!this.shouldProcessInputEvents) {
27199
27248
  return;
27200
27249
  }
27201
- let content = this.contentHelper.getText();
27250
+ if (ev.inputType === "insertFromPaste" &&
27251
+ this.env.model.getters.getEditionMode() === "inactive") {
27252
+ return;
27253
+ }
27254
+ ev.stopPropagation();
27255
+ let content;
27256
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27257
+ content = ev.data || "";
27258
+ }
27259
+ else {
27260
+ content = this.contentHelper.getText();
27261
+ }
27262
+ if (this.props.focus === "inactive") {
27263
+ return this.props.onComposerCellFocused?.(content);
27264
+ }
27202
27265
  let selection = this.contentHelper.getCurrentSelection();
27203
27266
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27204
27267
  this.env.model.dispatch("SET_CURRENT_CONTENT", {
@@ -27274,9 +27337,8 @@
27274
27337
  }
27275
27338
  const newSelection = this.contentHelper.getCurrentSelection();
27276
27339
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
27277
- if (this.props.focus === "inactive") {
27278
- this.props.onComposerContentFocused(newSelection);
27279
- }
27340
+ this.props.onComposerContentFocused();
27341
+ if (this.props.focus === "inactive") ;
27280
27342
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
27281
27343
  this.processTokenAtCursor();
27282
27344
  }
@@ -27443,7 +27505,7 @@
27443
27505
  const highlights = this.env.model.getters.getHighlights();
27444
27506
  const refSheet = sheetName
27445
27507
  ? this.env.model.getters.getSheetIdByName(sheetName)
27446
- : this.env.model.getters.getCurrentEditedCell().sheetId;
27508
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
27447
27509
  const highlight = highlights.find((highlight) => {
27448
27510
  if (highlight.sheetId !== refSheet)
27449
27511
  return false;
@@ -27570,56 +27632,30 @@
27570
27632
  focus: {
27571
27633
  validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value),
27572
27634
  },
27573
- onComposerUnmounted: Function,
27635
+ onComposerCellFocused: Function,
27574
27636
  onComposerContentFocused: Function,
27575
27637
  gridDims: Object,
27576
27638
  };
27577
27639
  static components = { Composer };
27578
- gridComposerRef;
27579
- zone;
27580
- rect;
27640
+ rect = this.defaultRect;
27641
+ isEditing = false;
27581
27642
  isCellReferenceVisible;
27582
- composerState;
27643
+ get defaultRect() {
27644
+ return { x: 0, y: 0, width: 0, height: 0 };
27645
+ }
27583
27646
  setup() {
27584
- this.gridComposerRef = owl.useRef("gridComposer");
27585
- this.composerState = owl.useState({
27586
- rect: undefined,
27587
- delimitation: undefined,
27588
- });
27589
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
27590
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
27591
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
27592
- this.isCellReferenceVisible = false;
27593
- owl.onMounted(() => {
27594
- const el = this.gridComposerRef.el;
27595
- this.composerState.rect = {
27596
- x: this.rect.x,
27597
- y: this.rect.y,
27598
- width: el.clientWidth,
27599
- height: el.clientHeight,
27600
- };
27601
- this.composerState.delimitation = {
27602
- width: this.props.gridDims.width,
27603
- height: this.props.gridDims.height,
27604
- };
27605
- });
27606
27647
  owl.onWillUpdateProps(() => {
27607
- if (this.isCellReferenceVisible) {
27608
- return;
27609
- }
27610
- const sheetId = this.env.model.getters.getActiveSheetId();
27611
- const zone = this.env.model.getters.getSelectedZone();
27612
- const rect = this.env.model.getters.getVisibleRect(zone);
27613
- if (!deepEquals(rect, this.rect) ||
27614
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27615
- this.isCellReferenceVisible = true;
27616
- }
27648
+ this.updateComponentPosition();
27649
+ this.updateCellReferenceVisibility();
27617
27650
  });
27618
27651
  }
27619
27652
  get shouldDisplayCellReference() {
27620
27653
  return this.isCellReferenceVisible;
27621
27654
  }
27622
27655
  get cellReference() {
27656
+ if (!this.env.model.getters.getCurrentEditedCell()) {
27657
+ return "";
27658
+ }
27623
27659
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27624
27660
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27625
27661
  return getFullReference(prefixSheet ? this.env.model.getters.getSheetName(sheetId) : undefined, toXC(col, row));
@@ -27631,7 +27667,27 @@
27631
27667
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
27632
27668
  });
27633
27669
  }
27670
+ get composerProps() {
27671
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27672
+ return {
27673
+ rect: { ...this.rect },
27674
+ delimitation: {
27675
+ width,
27676
+ height,
27677
+ },
27678
+ focus: this.props.focus,
27679
+ isDefaultFocus: true,
27680
+ onComposerContentFocused: this.props.onComposerContentFocused,
27681
+ onComposerCellFocused: this.props.onComposerCellFocused,
27682
+ };
27683
+ }
27634
27684
  get containerStyle() {
27685
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27686
+ return `
27687
+ position: absolute;
27688
+ z-index: -1000;
27689
+ `;
27690
+ }
27635
27691
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27636
27692
  const cell = this.env.model.getters.getActiveCell();
27637
27693
  const position = this.env.model.getters.getActivePosition();
@@ -27651,6 +27707,8 @@
27651
27707
  if (!isFormula) {
27652
27708
  textAlign = style.align || cell.defaultAlign;
27653
27709
  }
27710
+ const maxHeight = this.props.gridDims.height - this.rect.y;
27711
+ const maxWidth = this.props.gridDims.width - this.rect.x;
27654
27712
  /**
27655
27713
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
27656
27714
  * including all the paddings/margins of the composer
@@ -27662,6 +27720,8 @@
27662
27720
  top: `${top}px`,
27663
27721
  "min-width": `${width + 1}px`,
27664
27722
  "min-height": `${height + 1}px`,
27723
+ "max-width": `${maxWidth}px`,
27724
+ "max-height": `${maxHeight}px`,
27665
27725
  background,
27666
27726
  color,
27667
27727
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -27671,13 +27731,31 @@
27671
27731
  "text-align": textAlign,
27672
27732
  });
27673
27733
  }
27674
- get composerStyle() {
27675
- const maxHeight = this.props.gridDims.height - this.rect.y;
27676
- const maxWidth = this.props.gridDims.width - this.rect.x;
27677
- return cssPropertiesToCss({
27678
- "max-width": `${maxWidth}px`,
27679
- "max-height": `${maxHeight}px`,
27680
- });
27734
+ updateComponentPosition() {
27735
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
27736
+ if (this.isEditing !== isEditing) {
27737
+ this.isEditing = isEditing;
27738
+ if (!isEditing) {
27739
+ this.rect = this.defaultRect;
27740
+ this.env.focusableElement.focus();
27741
+ return;
27742
+ }
27743
+ const position = this.env.model.getters.getActivePosition();
27744
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
27745
+ this.rect = this.env.model.getters.getVisibleRect(zone);
27746
+ }
27747
+ }
27748
+ updateCellReferenceVisibility() {
27749
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
27750
+ return;
27751
+ }
27752
+ const sheetId = this.env.model.getters.getActiveSheetId();
27753
+ const zone = this.env.model.getters.getSelectedZone();
27754
+ const rect = this.env.model.getters.getVisibleRect(zone);
27755
+ if (!deepEquals(rect, this.rect) ||
27756
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27757
+ this.isCellReferenceVisible = true;
27758
+ }
27681
27759
  }
27682
27760
  }
27683
27761
 
@@ -28777,7 +28855,6 @@
28777
28855
  this.props.onCellDoubleClicked(col, row);
28778
28856
  }
28779
28857
  onContextMenu(ev) {
28780
- ev.preventDefault();
28781
28858
  const [col, row] = this.getCartesianCoordinates(ev);
28782
28859
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
28783
28860
  }
@@ -29842,7 +29919,6 @@
29842
29919
  HEADER_WIDTH = HEADER_WIDTH;
29843
29920
  menuState;
29844
29921
  gridRef;
29845
- hiddenInput;
29846
29922
  onMouseWheel;
29847
29923
  canvasPosition;
29848
29924
  hoveredCell;
@@ -29853,15 +29929,14 @@
29853
29929
  menuItems: [],
29854
29930
  });
29855
29931
  this.gridRef = owl.useRef("grid");
29856
- this.hiddenInput = owl.useRef("hiddenInput");
29857
29932
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
29858
29933
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
29859
29934
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
29860
29935
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
29861
29936
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
29862
29937
  owl.useExternalListener(document.body, "paste", this.paste);
29863
- owl.onMounted(() => this.focus());
29864
- this.props.exposeFocus(() => this.focus());
29938
+ owl.onMounted(() => this.focusDefaultElement());
29939
+ this.props.exposeFocus(() => this.focusDefaultElement());
29865
29940
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29866
29941
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29867
29942
  this.moveCanvas(deltaX, deltaY);
@@ -29885,7 +29960,7 @@
29885
29960
  if (this.env.model.getters.hasOpenedPopover()) {
29886
29961
  this.closeOpenedPopover();
29887
29962
  }
29888
- this.focus();
29963
+ this.focusDefaultElement();
29889
29964
  }
29890
29965
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
29891
29966
  // down itself
@@ -30064,10 +30139,10 @@
30064
30139
  "Alt+Shift+ArrowUp": () => this.processHeaderGroupingKey("up"),
30065
30140
  "Alt+Shift+ArrowDown": () => this.processHeaderGroupingKey("down"),
30066
30141
  };
30067
- focus() {
30142
+ focusDefaultElement() {
30068
30143
  if (!this.env.model.getters.getSelectedFigureId() &&
30069
30144
  this.env.model.getters.getEditionMode() === "inactive") {
30070
- this.hiddenInput.el?.focus();
30145
+ this.env.focusableElement.focus();
30071
30146
  }
30072
30147
  }
30073
30148
  get gridEl() {
@@ -30203,19 +30278,6 @@
30203
30278
  return;
30204
30279
  }
30205
30280
  }
30206
- onInput(ev) {
30207
- // the user meant to paste in the sheet, not open the composer with the pasted content
30208
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
30209
- return;
30210
- }
30211
- if (ev.data) {
30212
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
30213
- // character
30214
- ev.preventDefault();
30215
- ev.stopPropagation();
30216
- this.props.onGridComposerCellFocused(ev.data);
30217
- }
30218
- }
30219
30281
  // ---------------------------------------------------------------------------
30220
30282
  // Context Menu
30221
30283
  // ---------------------------------------------------------------------------
@@ -30330,7 +30392,7 @@
30330
30392
  }
30331
30393
  closeMenu() {
30332
30394
  this.menuState.isOpen = false;
30333
- this.focus();
30395
+ this.focusDefaultElement();
30334
30396
  }
30335
30397
  processHeaderGroupingKey(direction) {
30336
30398
  if (this.env.model.getters.getSelectedZones().length !== 1) {
@@ -34247,6 +34309,31 @@
34247
34309
  return data;
34248
34310
  },
34249
34311
  },
34312
+ {
34313
+ description: "Fix datafilter duplication",
34314
+ from: 12,
34315
+ to: 12.5,
34316
+ applyMigration(data) {
34317
+ for (let sheet of data.sheets || []) {
34318
+ let knownDataFilterZones = [];
34319
+ for (let filterTable of sheet.filterTables || []) {
34320
+ const zone = toZone(filterTable.range);
34321
+ // See commit message for the details
34322
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
34323
+ if (intersectZoneIndex !== -1) {
34324
+ knownDataFilterZones[intersectZoneIndex] = zone;
34325
+ }
34326
+ else {
34327
+ knownDataFilterZones.push(zone);
34328
+ }
34329
+ }
34330
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
34331
+ range: zoneToXc(zone),
34332
+ }));
34333
+ }
34334
+ return data;
34335
+ },
34336
+ },
34250
34337
  {
34251
34338
  description: "Change Border description structure",
34252
34339
  from: 12,
@@ -35086,22 +35173,7 @@
35086
35173
  }
35087
35174
  }
35088
35175
  export(data) {
35089
- // Borders
35090
- let borderId = 0;
35091
35176
  const borders = {};
35092
- /**
35093
- * Get the id of the given border. If the border does not exist, it creates
35094
- * one.
35095
- */
35096
- function getBorderId(border) {
35097
- for (let [key, value] of Object.entries(borders)) {
35098
- if (deepEquals(value, border)) {
35099
- return parseInt(key, 10);
35100
- }
35101
- }
35102
- borders[++borderId] = border;
35103
- return borderId;
35104
- }
35105
35177
  for (let sheet of data.sheets) {
35106
35178
  for (let col = 0; col < sheet.colNumber; col++) {
35107
35179
  for (let row = 0; row < sheet.rowNumber; row++) {
@@ -35109,7 +35181,7 @@
35109
35181
  if (border) {
35110
35182
  const xc = toXC(col, row);
35111
35183
  const cell = sheet.cells[xc];
35112
- const borderId = getBorderId(border);
35184
+ const borderId = getItemId(border, borders);
35113
35185
  if (cell) {
35114
35186
  cell.border = borderId;
35115
35187
  }
@@ -35898,9 +35970,9 @@
35898
35970
  allowDispatch(cmd) {
35899
35971
  switch (cmd.type) {
35900
35972
  case "UPDATE_CELL":
35901
- return this.checkCellOutOfSheet(cmd);
35973
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35902
35974
  case "CLEAR_CELL":
35903
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35975
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35904
35976
  default:
35905
35977
  return "Success" /* CommandResult.Success */;
35906
35978
  }
@@ -36033,9 +36105,6 @@
36033
36105
  format: cell.format ? getItemId(cell.format, formats) : undefined,
36034
36106
  content: cell.content || undefined,
36035
36107
  };
36036
- if (cell instanceof FormulaCellWithDependencies) {
36037
- cells[xc].content = cell.contentWithFixedReferences || undefined;
36038
- }
36039
36108
  }
36040
36109
  _sheet.cells = cells;
36041
36110
  }
@@ -36053,8 +36122,8 @@
36053
36122
  }
36054
36123
  removeDefaultStyleValues(style) {
36055
36124
  const cleanedStyle = { ...style };
36056
- for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
36057
- if (cleanedStyle[property] === defaultValue) {
36125
+ for (const property in DEFAULT_STYLE) {
36126
+ if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
36058
36127
  delete cleanedStyle[property];
36059
36128
  }
36060
36129
  }
@@ -36210,10 +36279,7 @@
36210
36279
  else {
36211
36280
  style = before ? before.style : undefined;
36212
36281
  }
36213
- const locale = this.getters.getLocale();
36214
- let format = ("format" in after ? after.format : before && before.format) ||
36215
- detectDateFormat(afterContent, locale) ||
36216
- detectNumberFormat(afterContent);
36282
+ const format = "format" in after ? after.format : before && before.format;
36217
36283
  /* Read the following IF as:
36218
36284
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
36219
36285
  * - the command says the new content is empty and has no border/format/style
@@ -36254,6 +36320,7 @@
36254
36320
  }
36255
36321
  createLiteralCell(id, content, format, style) {
36256
36322
  const locale = this.getters.getLocale();
36323
+ format = format || detectDateFormat(content, locale) || detectNumberFormat(content);
36257
36324
  if (format !== PLAIN_TEXT_FORMAT && !isEvaluationError(content)) {
36258
36325
  content = toString(parseLiteral(content, locale));
36259
36326
  }
@@ -36287,8 +36354,11 @@
36287
36354
  * being a computed property to rebuild the dependencies XC.
36288
36355
  */
36289
36356
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
36290
- const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
36291
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
36357
+ const dependencies = [];
36358
+ for (const xc of compiledFormula.dependencies) {
36359
+ dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
36360
+ }
36361
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
36292
36362
  }
36293
36363
  createErrorFormula(id, content, format, style, error) {
36294
36364
  return {
@@ -36323,6 +36393,18 @@
36323
36393
  }
36324
36394
  return "Success" /* CommandResult.Success */;
36325
36395
  }
36396
+ checkUselessUpdateCell(cmd) {
36397
+ const cell = this.getters.getCell(cmd);
36398
+ const hasContent = "content" in cmd || "formula" in cmd;
36399
+ const hasStyle = "style" in cmd;
36400
+ const hasFormat = "format" in cmd;
36401
+ if ((!hasContent || cell?.content === cmd.content) &&
36402
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
36403
+ (!hasFormat || cell?.format === cmd.format)) {
36404
+ return "NoChanges" /* CommandResult.NoChanges */;
36405
+ }
36406
+ return "Success" /* CommandResult.Success */;
36407
+ }
36326
36408
  }
36327
36409
  class FormulaCellWithDependencies {
36328
36410
  id;
@@ -36342,7 +36424,7 @@
36342
36424
  const tokens = compiledFormula.tokens.map((token) => {
36343
36425
  if (token.type === "REFERENCE") {
36344
36426
  const index = rangeIndex++;
36345
- return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
36427
+ return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
36346
36428
  }
36347
36429
  return token;
36348
36430
  });
@@ -36369,13 +36451,20 @@
36369
36451
  }
36370
36452
  }
36371
36453
  class RangeReferenceToken {
36372
- getValue;
36454
+ ranges;
36455
+ rangeIndex;
36456
+ sheetId;
36457
+ getRangeString;
36373
36458
  type = "REFERENCE";
36374
- constructor(getValue) {
36375
- this.getValue = getValue;
36459
+ constructor(ranges, rangeIndex, sheetId, getRangeString) {
36460
+ this.ranges = ranges;
36461
+ this.rangeIndex = rangeIndex;
36462
+ this.sheetId = sheetId;
36463
+ this.getRangeString = getRangeString;
36376
36464
  }
36377
36465
  get value() {
36378
- return this.getValue();
36466
+ const range = this.ranges[this.rangeIndex];
36467
+ return this.getRangeString(range, this.sheetId);
36379
36468
  }
36380
36469
  }
36381
36470
 
@@ -41100,7 +41189,15 @@
41100
41189
  if (!this.rTrees[sheetId]) {
41101
41190
  return;
41102
41191
  }
41103
- this.rTrees[sheetId].remove(item, deepEquals);
41192
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
41193
+ }
41194
+ rtreeItemComparer(left, right) {
41195
+ return (left.data == right.data &&
41196
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
41197
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
41198
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
41199
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
41200
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
41104
41201
  }
41105
41202
  }
41106
41203
  /**
@@ -41178,7 +41275,7 @@
41178
41275
  const queue = Array.from(ranges).reverse();
41179
41276
  while (queue.length > 0) {
41180
41277
  const range = queue.pop();
41181
- visited.add(...this.encoder.encodeBoundingBox(range));
41278
+ visited.addMany(this.encoder.encodeBoundingBox(range));
41182
41279
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
41183
41280
  for (const positionId of impactedPositionIds) {
41184
41281
  if (!visited.has(positionId)) {
@@ -41186,7 +41283,7 @@
41186
41283
  }
41187
41284
  }
41188
41285
  }
41189
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41286
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41190
41287
  return visited;
41191
41288
  }
41192
41289
  }
@@ -41326,9 +41423,9 @@
41326
41423
  const cells = positions.map((p) => this.encoder.encode(p));
41327
41424
  const cellsToCompute = new JetSet(cells);
41328
41425
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
41329
- cellsToCompute.add(...this.getCellsDependingOn(cells));
41330
- cellsToCompute.add(...arrayFormulasPositionIds);
41331
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
41426
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
41427
+ cellsToCompute.addMany(arrayFormulasPositionIds);
41428
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
41332
41429
  this.evaluate(cellsToCompute);
41333
41430
  }
41334
41431
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -41342,7 +41439,7 @@
41342
41439
  }
41343
41440
  if (!content) {
41344
41441
  // The previous content could have blocked some array formulas
41345
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41442
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41346
41443
  }
41347
41444
  }
41348
41445
  return impactedPositionIds;
@@ -41394,7 +41491,7 @@
41394
41491
  }
41395
41492
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
41396
41493
  const cells = new JetSet(arrayFormulas);
41397
- cells.add(...this.getCellsDependingOn(arrayFormulas));
41494
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
41398
41495
  return cells;
41399
41496
  }
41400
41497
  nextPositionsToUpdate = new JetSet();
@@ -41532,7 +41629,7 @@
41532
41629
  this.setEvaluatedCell(positionId, evaluatedCell);
41533
41630
  // check if formula dependencies present in the spread zone
41534
41631
  // if so, they need to be recomputed
41535
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
41632
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41536
41633
  };
41537
41634
  }
41538
41635
  invalidateSpreading(positionId) {
@@ -41547,8 +41644,8 @@
41547
41644
  continue;
41548
41645
  }
41549
41646
  this.evaluatedCells.delete(child);
41550
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
41551
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
41647
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
41648
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
41552
41649
  }
41553
41650
  this.spreadingRelations.removeNode(positionId);
41554
41651
  }
@@ -41910,7 +42007,13 @@
41910
42007
  const format = newFormat
41911
42008
  ? getItemId(newFormat, data.formats)
41912
42009
  : exportedCellData.format;
41913
- const content = !isExported ? newContent : exportedCellData.content;
42010
+ let content;
42011
+ if (formulaCell instanceof FormulaCellWithDependencies) {
42012
+ content = formulaCell.contentWithFixedReferences;
42013
+ }
42014
+ else {
42015
+ content = !isExported ? newContent : exportedCellData.content;
42016
+ }
41914
42017
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
41915
42018
  }
41916
42019
  }
@@ -45411,7 +45514,7 @@
45411
45514
  }
45412
45515
  finalize() {
45413
45516
  if (this.isSearchDirty) {
45414
- this.refreshSearch();
45517
+ this.refreshSearch(false);
45415
45518
  this.isSearchDirty = false;
45416
45519
  }
45417
45520
  }
@@ -45455,10 +45558,10 @@
45455
45558
  /**
45456
45559
  * refresh the matches according to the current search options
45457
45560
  */
45458
- refreshSearch() {
45561
+ refreshSearch(jumpToMatchSheet = true) {
45459
45562
  this.selectedMatchIndex = null;
45460
45563
  this.findMatches();
45461
- this.selectNextCell(Direction.current);
45564
+ this.selectNextCell(Direction.current, jumpToMatchSheet);
45462
45565
  }
45463
45566
  /**
45464
45567
  * Updates the regex based on the current searchOptions and
@@ -45540,7 +45643,7 @@
45540
45643
  * It is also used to keep coherence between the selected searchMatch
45541
45644
  * and selectedMatchIndex.
45542
45645
  */
45543
- selectNextCell(indexChange) {
45646
+ selectNextCell(indexChange, jumpToMatchSheet = true) {
45544
45647
  const matches = this.searchMatches;
45545
45648
  if (!matches.length) {
45546
45649
  this.selectedMatchIndex = null;
@@ -45566,7 +45669,7 @@
45566
45669
  this.selectedMatchIndex = nextIndex;
45567
45670
  const selectedMatch = matches[nextIndex];
45568
45671
  // Switch to the sheet where the match is located
45569
- if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
45672
+ if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
45570
45673
  this.dispatch("ACTIVATE_SHEET", {
45571
45674
  sheetIdFrom: this.getters.getActiveSheetId(),
45572
45675
  sheetIdTo: selectedMatch.sheetId,
@@ -47138,7 +47241,7 @@
47138
47241
  "getCellText",
47139
47242
  "getCellMultiLineText",
47140
47243
  "getContiguousZone",
47141
- "isCellEmpty",
47244
+ "isEvaluatedCellEmpty",
47142
47245
  ];
47143
47246
  ctx = document.createElement("canvas").getContext("2d");
47144
47247
  // ---------------------------------------------------------------------------
@@ -47266,14 +47369,23 @@
47266
47369
  return zone;
47267
47370
  }
47268
47371
  /**
47269
- * Check if a cell is empty. If the cell is part of a merge,
47270
- * check if the merge containing the cell is empty.
47372
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
47373
+ * the check applies to the main cell of the merge.
47271
47374
  */
47272
- isCellEmpty(position) {
47375
+ isEvaluatedCellEmpty(position) {
47273
47376
  const mainPosition = this.getters.getMainCellPosition(position);
47274
47377
  const cell = this.getters.getEvaluatedCell(mainPosition);
47275
47378
  return cell.type === CellValueType.empty;
47276
47379
  }
47380
+ /**
47381
+ * Checks if a cell is empty (i.e. does not have a content or a formula does not spread over it).
47382
+ * If the cell is part of a merge, the check applies to the main cell of the merge.
47383
+ */
47384
+ isCellEmpty(position) {
47385
+ const mainPosition = this.getters.getMainCellPosition(position);
47386
+ return !(this.getters.getCorrespondingFormulaCell(mainPosition) ||
47387
+ this.getters.getCell(mainPosition)?.content);
47388
+ }
47277
47389
  getColMaxWidth(sheetId, index) {
47278
47390
  const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
47279
47391
  const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
@@ -51094,6 +51206,16 @@
51094
51206
  }
51095
51207
  }
51096
51208
 
51209
+ class FocusableElement {
51210
+ focusableElement = undefined;
51211
+ setFocusableElement(element) {
51212
+ this.focusableElement = element;
51213
+ }
51214
+ focus() {
51215
+ this.focusableElement?.focus();
51216
+ }
51217
+ }
51218
+
51097
51219
  const RIPPLE_KEY_FRAMES = [
51098
51220
  { transform: "scale(0)" },
51099
51221
  { transform: "scale(0.8)", offset: 0.33 },
@@ -53248,6 +53370,7 @@
53248
53370
  toggleSidePanel: this.toggleSidePanel.bind(this),
53249
53371
  clipboard: this.env.clipboard || instantiateClipboard(),
53250
53372
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
53373
+ focusableElement: new FocusableElement(),
53251
53374
  });
53252
53375
  owl.useEffect(() => {
53253
53376
  /**
@@ -53366,16 +53489,17 @@
53366
53489
  }
53367
53490
  this.composer.topBarFocus = "contentFocus";
53368
53491
  this.composer.gridFocusMode = "inactive";
53369
- this.setComposerContent({ selection } || {});
53492
+ this.setComposerContent({ selection });
53370
53493
  }
53371
- onGridComposerContentFocused() {
53494
+ onGridComposerContentFocused(selection) {
53372
53495
  if (this.model.getters.isReadonly()) {
53373
53496
  return;
53374
53497
  }
53375
53498
  this.composer.topBarFocus = "inactive";
53376
53499
  this.composer.gridFocusMode = "contentFocus";
53377
- this.setComposerContent({});
53500
+ this.setComposerContent({ selection });
53378
53501
  }
53502
+ // TODO: either both are defined or none of them. change those args to an object
53379
53503
  onGridComposerCellFocused(content, selection) {
53380
53504
  if (this.model.getters.isReadonly()) {
53381
53505
  return;
@@ -54803,8 +54927,8 @@
54803
54927
  let currentPosition = startPosition;
54804
54928
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
54805
54929
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
54806
- let mode = !this.getters.isCellEmpty({ ...currentPosition, sheetId }) &&
54807
- !this.getters.isCellEmpty({ ...nextCellPosition, sheetId })
54930
+ let mode = !this.getters.isEvaluatedCellEmpty({ ...currentPosition, sheetId }) &&
54931
+ !this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId })
54808
54932
  ? "endOfCluster"
54809
54933
  : "nextCluster";
54810
54934
  while (true) {
@@ -54814,7 +54938,7 @@
54814
54938
  currentPosition.row === nextCellPosition.row) {
54815
54939
  break;
54816
54940
  }
54817
- const isNextCellEmpty = this.getters.isCellEmpty({ ...nextCellPosition, sheetId });
54941
+ const isNextCellEmpty = this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId });
54818
54942
  if (mode === "endOfCluster" && isNextCellEmpty) {
54819
54943
  break;
54820
54944
  }
@@ -57099,9 +57223,9 @@
57099
57223
  exports.tokenize = tokenize;
57100
57224
 
57101
57225
 
57102
- __info__.version = "17.1.3";
57103
- __info__.date = "2024-02-02T13:46:54.332Z";
57104
- __info__.hash = "798f979";
57226
+ __info__.version = "17.1.5";
57227
+ __info__.date = "2024-02-16T14:23:54.651Z";
57228
+ __info__.hash = "29d8ad6";
57105
57229
 
57106
57230
 
57107
57231
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);