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