@odoo/o-spreadsheet 17.0.10 → 17.0.12

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.0.10
6
- * @date 2024-02-02T13:48:28.727Z
7
- * @hash be06aa9
5
+ * @version 17.0.12
6
+ * @date 2024-02-16T14:24:02.984Z
7
+ * @hash 8fa2bab
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';
@@ -550,8 +550,8 @@ function isObjectEmptyRecursive(argument) {
550
550
  * If the given item does not exist in the dictionary, it creates one with a new id.
551
551
  */
552
552
  function getItemId(item, itemsDic) {
553
- for (let [key, value] of Object.entries(itemsDic)) {
554
- if (deepEquals(value, item)) {
553
+ for (const key in itemsDic) {
554
+ if (deepEquals(itemsDic[key], item)) {
555
555
  return parseInt(key, 10);
556
556
  }
557
557
  }
@@ -650,11 +650,13 @@ function deepEquals(o1, o2) {
650
650
  if (typeof o1 !== "object")
651
651
  return o1 === o2;
652
652
  // Objects can have different keys if the values are undefined
653
- const keys = new Set();
654
- Object.keys(o1).forEach((key) => keys.add(key));
655
- Object.keys(o2).forEach((key) => keys.add(key));
656
- for (let key of keys) {
657
- if (typeof o1[key] !== typeof o1[key])
653
+ for (const key in o2) {
654
+ if (!(key in o1) && o2[key] !== undefined) {
655
+ return false;
656
+ }
657
+ }
658
+ for (const key in o1) {
659
+ if (typeof o1[key] !== typeof o2[key])
658
660
  return false;
659
661
  if (typeof o1[key] === "object") {
660
662
  if (!deepEquals(o1[key], o2[key]))
@@ -724,18 +726,18 @@ function isConsecutive(iterable) {
724
726
  return true;
725
727
  }
726
728
  class JetSet extends Set {
727
- add(...iterable) {
729
+ addMany(iterable) {
728
730
  for (const element of iterable) {
729
731
  super.add(element);
730
732
  }
731
733
  return this;
732
734
  }
733
- delete(...iterable) {
734
- let deleted = false;
735
+ deleteMany(iterable) {
736
+ let wasDeleted = false;
735
737
  for (const element of iterable) {
736
- deleted ||= super.delete(element);
738
+ wasDeleted ||= super.delete(element);
737
739
  }
738
- return deleted;
740
+ return wasDeleted;
739
741
  }
740
742
  }
741
743
  /**
@@ -1994,7 +1996,7 @@ class DispatchResult {
1994
1996
  * Static helper which returns a successful DispatchResult
1995
1997
  */
1996
1998
  static get Success() {
1997
- return new DispatchResult();
1999
+ return SUCCESS;
1998
2000
  }
1999
2001
  get isSuccessful() {
2000
2002
  return this.reasons.length === 0;
@@ -2007,6 +2009,7 @@ class DispatchResult {
2007
2009
  return this.reasons.includes(reason);
2008
2010
  }
2009
2011
  }
2012
+ const SUCCESS = new DispatchResult();
2010
2013
  var CommandResult;
2011
2014
  (function (CommandResult) {
2012
2015
  CommandResult["Success"] = "Success";
@@ -2888,6 +2891,9 @@ function parseFormat(formatString) {
2888
2891
  function formatValue(value, { format, locale }) {
2889
2892
  switch (typeof value) {
2890
2893
  case "string":
2894
+ if (value.includes('\\"')) {
2895
+ return value.replace(/\\"/g, '"');
2896
+ }
2891
2897
  return value;
2892
2898
  case "boolean":
2893
2899
  return value ? "TRUE" : "FALSE";
@@ -7379,7 +7385,7 @@ function tokenize(str, locale = DEFAULT_LOCALE) {
7379
7385
  while (!chars.isOver()) {
7380
7386
  let token = tokenizeSpace(chars) ||
7381
7387
  tokenizeArgsSeparator(chars, locale) ||
7382
- tokenizeMisc(chars) ||
7388
+ tokenizeParenthesis(chars) ||
7383
7389
  tokenizeOperator(chars) ||
7384
7390
  tokenizeString(chars) ||
7385
7391
  tokenizeDebugger(chars) ||
@@ -7400,15 +7406,14 @@ function tokenizeDebugger(chars) {
7400
7406
  }
7401
7407
  return null;
7402
7408
  }
7403
- const misc$1 = {
7404
- "(": "LEFT_PAREN",
7405
- ")": "RIGHT_PAREN",
7409
+ const parenthesis = {
7410
+ "(": { type: "LEFT_PAREN", value: "(" },
7411
+ ")": { type: "RIGHT_PAREN", value: ")" },
7406
7412
  };
7407
- function tokenizeMisc(chars) {
7408
- if (chars.current in misc$1) {
7413
+ function tokenizeParenthesis(chars) {
7414
+ if (chars.current === "(" || chars.current === ")") {
7409
7415
  const value = chars.shift();
7410
- const type = misc$1[value];
7411
- return { type, value };
7416
+ return parenthesis[value];
7412
7417
  }
7413
7418
  return null;
7414
7419
  }
@@ -7429,7 +7434,12 @@ function tokenizeOperator(chars) {
7429
7434
  }
7430
7435
  return null;
7431
7436
  }
7437
+ const FIRST_POSSIBLE_NUMBER_CHARS = new Set("0123456789");
7432
7438
  function tokenizeNumber(chars, locale) {
7439
+ if (!FIRST_POSSIBLE_NUMBER_CHARS.has(chars.current) &&
7440
+ chars.current !== locale.decimalSeparator) {
7441
+ return null;
7442
+ }
7433
7443
  const match = chars.remaining().match(getFormulaNumberRegex(locale.decimalSeparator));
7434
7444
  if (match) {
7435
7445
  chars.advanceBy(match[0].length);
@@ -7454,7 +7464,7 @@ function tokenizeString(chars) {
7454
7464
  }
7455
7465
  return null;
7456
7466
  }
7457
- const separatorRegexp = /\w|\.|!|\$/;
7467
+ const separatorRegexp = /^[\w\.!\$]+/;
7458
7468
  /**
7459
7469
  * A "Symbol" is just basically any word-like element that can appear in a
7460
7470
  * formula, which is not a string. So:
@@ -7494,8 +7504,11 @@ function tokenizeSymbol(chars) {
7494
7504
  };
7495
7505
  }
7496
7506
  }
7497
- while (chars.current && separatorRegexp.test(chars.current)) {
7498
- result += chars.shift();
7507
+ const match = chars.remaining().match(separatorRegexp);
7508
+ if (match) {
7509
+ const value = match[0];
7510
+ result += value;
7511
+ chars.advanceBy(value.length);
7499
7512
  }
7500
7513
  if (result.length) {
7501
7514
  const value = result;
@@ -7557,7 +7570,10 @@ class TokenizingChars {
7557
7570
  return this.text.substring(this.currentIndex);
7558
7571
  }
7559
7572
  currentStartsWith(str) {
7560
- for (let j = 0; j < str.length; j++) {
7573
+ if (this.current !== str[0]) {
7574
+ return false;
7575
+ }
7576
+ for (let j = 1; j < str.length; j++) {
7561
7577
  if (this.text[this.currentIndex + j] !== str[j]) {
7562
7578
  return false;
7563
7579
  }
@@ -7584,6 +7600,9 @@ function isValidLocale(locale) {
7584
7600
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7585
7601
  return false;
7586
7602
  }
7603
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
7604
+ return false;
7605
+ }
7587
7606
  try {
7588
7607
  formatValue(1, { locale, format: "#,##0.00" });
7589
7608
  formatValue(1, { locale, format: locale.dateFormat });
@@ -7674,7 +7693,7 @@ function canonicalizeNumberLiteral(content, locale) {
7674
7693
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7675
7694
  return content;
7676
7695
  }
7677
- return content.replace(locale.decimalSeparator, ".");
7696
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7678
7697
  }
7679
7698
  /**
7680
7699
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -10746,6 +10765,12 @@ const INSERT_LINK = (env) => {
10746
10765
  let { col, row } = env.model.getters.getActivePosition();
10747
10766
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
10748
10767
  };
10768
+ const INSERT_LINK_NAME = (env) => {
10769
+ const sheetId = env.model.getters.getActiveSheetId();
10770
+ const { col, row } = env.model.getters.getActivePosition();
10771
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
10772
+ return cell && cell.link ? _t("Edit link") : _t("Insert link");
10773
+ };
10749
10774
  //------------------------------------------------------------------------------
10750
10775
  // Filters action
10751
10776
  //------------------------------------------------------------------------------
@@ -19777,7 +19802,7 @@ cellMenuRegistry
19777
19802
  })
19778
19803
  .add("insert_link", {
19779
19804
  ...insertLink,
19780
- name: _t("Insert link"),
19805
+ name: INSERT_LINK_NAME,
19781
19806
  sequence: 150,
19782
19807
  separator: true,
19783
19808
  });
@@ -26030,6 +26055,17 @@ css /* scss */ `
26030
26055
  `;
26031
26056
  class TextValueProvider extends Component {
26032
26057
  static template = "o-spreadsheet-TextValueProvider";
26058
+ autoCompleteListRef = useRef("autoCompleteList");
26059
+ setup() {
26060
+ useEffect(() => {
26061
+ const selectedIndex = this.props.selectedIndex;
26062
+ if (selectedIndex === undefined) {
26063
+ return;
26064
+ }
26065
+ const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
26066
+ selectedElement?.scrollIntoView?.({ block: "nearest" });
26067
+ }, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
26068
+ }
26033
26069
  }
26034
26070
  TextValueProvider.props = {
26035
26071
  values: Array,
@@ -26481,6 +26517,7 @@ css /* scss */ `
26481
26517
  position: absolute;
26482
26518
  margin: 1px 4px;
26483
26519
  pointer-events: none;
26520
+ overflow: auto;
26484
26521
 
26485
26522
  .o-semi-bold {
26486
26523
  /** FIXME: to remove in favor of Bootstrap
@@ -26496,6 +26533,7 @@ class Composer extends Component {
26496
26533
  static components = { TextValueProvider, FunctionDescriptionProvider };
26497
26534
  static defaultProps = {
26498
26535
  inputStyle: "",
26536
+ isDefaultFocus: false,
26499
26537
  };
26500
26538
  composerRef = useRef("o_composer");
26501
26539
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -26526,7 +26564,10 @@ class Composer extends Component {
26526
26564
  if (this.props.delimitation && this.props.rect) {
26527
26565
  const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
26528
26566
  const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
26567
+ assistantStyle["max-height"] = `${remainingHeight}px`;
26529
26568
  if (cellY > remainingHeight) {
26569
+ const availableSpaceAbove = cellY;
26570
+ assistantStyle["max-height"] = `${availableSpaceAbove}px`;
26530
26571
  // render top
26531
26572
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
26532
26573
  assistantStyle.top = `-3px`;
@@ -26552,7 +26593,6 @@ class Composer extends Component {
26552
26593
  F2: () => console.warn("Not implemented"),
26553
26594
  F4: this.processF4Key,
26554
26595
  Tab: (ev) => this.processTabKey(ev),
26555
- " ": (ev) => this.processSpaceKey(ev),
26556
26596
  };
26557
26597
  keyCodeMapping = {
26558
26598
  NumpadDecimal: this.processNumpadDecimal,
@@ -26560,12 +26600,12 @@ class Composer extends Component {
26560
26600
  setup() {
26561
26601
  onMounted(() => {
26562
26602
  const el = this.composerRef.el;
26603
+ if (this.props.isDefaultFocus) {
26604
+ this.env.focusableElement.setFocusableElement(el);
26605
+ }
26563
26606
  this.contentHelper.updateEl(el);
26564
26607
  this.processTokenAtCursor();
26565
26608
  });
26566
- onWillUnmount(() => {
26567
- this.props.onComposerUnmounted?.();
26568
- });
26569
26609
  useEffect(() => {
26570
26610
  this.processContent();
26571
26611
  });
@@ -26574,7 +26614,8 @@ class Composer extends Component {
26574
26614
  // Handlers
26575
26615
  // ---------------------------------------------------------------------------
26576
26616
  processArrowKeys(ev) {
26577
- if (this.env.model.getters.isSelectingForComposer()) {
26617
+ if (this.env.model.getters.isSelectingForComposer() ||
26618
+ this.env.model.getters.getEditionMode() === "inactive") {
26578
26619
  this.functionDescriptionState.showDescription = false;
26579
26620
  // Prevent the default content editable behavior which moves the cursor
26580
26621
  ev.preventDefault();
@@ -26617,26 +26658,20 @@ class Composer extends Component {
26617
26658
  processTabKey(ev) {
26618
26659
  ev.preventDefault();
26619
26660
  ev.stopPropagation();
26620
- const state = this.autoCompleteState;
26621
- if (state.showProvider && state.selectedIndex !== undefined) {
26622
- const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
26623
- if (autoCompleteValue) {
26624
- this.autoComplete(autoCompleteValue);
26625
- return;
26661
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
26662
+ const state = this.autoCompleteState;
26663
+ if (state.showProvider && state.selectedIndex !== undefined) {
26664
+ const autoCompleteValue = this.autoCompleteState.values[state.selectedIndex]?.text;
26665
+ if (autoCompleteValue) {
26666
+ this.autoComplete(autoCompleteValue);
26667
+ return;
26668
+ }
26626
26669
  }
26670
+ interactiveStopEdition(this.env);
26627
26671
  }
26628
26672
  const direction = ev.shiftKey ? "left" : "right";
26629
- interactiveStopEdition(this.env);
26630
26673
  this.env.model.selection.moveAnchorCell(direction, 1);
26631
26674
  }
26632
- processSpaceKey(ev) {
26633
- if (ev.ctrlKey) {
26634
- ev.preventDefault();
26635
- ev.stopPropagation();
26636
- this.showFunctionAutocomplete("");
26637
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26638
- }
26639
- }
26640
26675
  processEnterKey(ev) {
26641
26676
  ev.preventDefault();
26642
26677
  ev.stopPropagation();
@@ -26689,6 +26724,9 @@ class Composer extends Component {
26689
26724
  this.compositionActive = false;
26690
26725
  }
26691
26726
  onKeydown(ev) {
26727
+ if (this.env.model.getters.getEditionMode() === "inactive") {
26728
+ return;
26729
+ }
26692
26730
  let handler = this.keyMapping[ev.key] || this.keyCodeMapping[ev.code];
26693
26731
  if (handler) {
26694
26732
  handler.call(this, ev);
@@ -26697,14 +26735,33 @@ class Composer extends Component {
26697
26735
  ev.stopPropagation();
26698
26736
  }
26699
26737
  }
26738
+ onPaste(ev) {
26739
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
26740
+ ev.stopPropagation();
26741
+ }
26742
+ }
26700
26743
  /*
26701
26744
  * Triggered automatically by the content-editable between the keydown and key up
26702
26745
  * */
26703
26746
  onInput(ev) {
26704
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
26747
+ if (!this.shouldProcessInputEvents) {
26705
26748
  return;
26706
26749
  }
26707
- let content = this.contentHelper.getText();
26750
+ if (ev.inputType === "insertFromPaste" &&
26751
+ this.env.model.getters.getEditionMode() === "inactive") {
26752
+ return;
26753
+ }
26754
+ ev.stopPropagation();
26755
+ let content;
26756
+ if (this.env.model.getters.getEditionMode() === "inactive") {
26757
+ content = ev.data || "";
26758
+ }
26759
+ else {
26760
+ content = this.contentHelper.getText();
26761
+ }
26762
+ if (this.props.focus === "inactive") {
26763
+ return this.props.onComposerCellFocused?.(content);
26764
+ }
26708
26765
  let selection = this.contentHelper.getCurrentSelection();
26709
26766
  if (ev.inputType === "insertParagraph") {
26710
26767
  const start = Math.min(selection.start, selection.end);
@@ -26786,9 +26843,8 @@ class Composer extends Component {
26786
26843
  }
26787
26844
  const newSelection = this.contentHelper.getCurrentSelection();
26788
26845
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
26789
- if (this.props.focus === "inactive") {
26790
- this.props.onComposerContentFocused(newSelection);
26791
- }
26846
+ this.props.onComposerContentFocused();
26847
+ if (this.props.focus === "inactive") ;
26792
26848
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
26793
26849
  this.processTokenAtCursor();
26794
26850
  }
@@ -26955,7 +27011,7 @@ class Composer extends Component {
26955
27011
  const highlights = this.env.model.getters.getHighlights();
26956
27012
  const refSheet = sheetName
26957
27013
  ? this.env.model.getters.getSheetIdByName(sheetName)
26958
- : this.env.model.getters.getCurrentEditedCell().sheetId;
27014
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
26959
27015
  const highlight = highlights.find((highlight) => {
26960
27016
  if (highlight.sheetId !== refSheet)
26961
27017
  return false;
@@ -27048,12 +27104,13 @@ class Composer extends Component {
27048
27104
  }
27049
27105
  }
27050
27106
  Composer.props = {
27051
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27052
- onComposerContentFocused: Function,
27053
27107
  inputStyle: { type: String, optional: true },
27054
27108
  rect: { type: Object, optional: true },
27055
27109
  delimitation: { type: Object, optional: true },
27056
- onComposerUnmounted: { type: Function, optional: true },
27110
+ focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27111
+ onComposerCellFocused: { type: Function, optional: true },
27112
+ onComposerContentFocused: Function,
27113
+ isDefaultFocus: { type: Boolean, optional: true },
27057
27114
  };
27058
27115
 
27059
27116
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
@@ -27087,51 +27144,25 @@ css /* scss */ `
27087
27144
  class GridComposer extends Component {
27088
27145
  static template = "o-spreadsheet-GridComposer";
27089
27146
  static components = { Composer };
27090
- gridComposerRef;
27091
- zone;
27092
- rect;
27147
+ rect = this.defaultRect;
27148
+ isEditing = false;
27093
27149
  isCellReferenceVisible;
27094
- composerState;
27150
+ get defaultRect() {
27151
+ return { x: 0, y: 0, width: 0, height: 0 };
27152
+ }
27095
27153
  setup() {
27096
- this.gridComposerRef = useRef("gridComposer");
27097
- this.composerState = useState({
27098
- rect: undefined,
27099
- delimitation: undefined,
27100
- });
27101
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
27102
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
27103
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
27104
- this.isCellReferenceVisible = false;
27105
- onMounted(() => {
27106
- const el = this.gridComposerRef.el;
27107
- this.composerState.rect = {
27108
- x: this.rect.x,
27109
- y: this.rect.y,
27110
- width: el.clientWidth,
27111
- height: el.clientHeight,
27112
- };
27113
- this.composerState.delimitation = {
27114
- width: this.props.gridDims.width,
27115
- height: this.props.gridDims.height,
27116
- };
27117
- });
27118
27154
  onWillUpdateProps(() => {
27119
- if (this.isCellReferenceVisible) {
27120
- return;
27121
- }
27122
- const sheetId = this.env.model.getters.getActiveSheetId();
27123
- const zone = this.env.model.getters.getSelectedZone();
27124
- const rect = this.env.model.getters.getVisibleRect(zone);
27125
- if (!deepEquals(rect, this.rect) ||
27126
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27127
- this.isCellReferenceVisible = true;
27128
- }
27155
+ this.updateComponentPosition();
27156
+ this.updateCellReferenceVisibility();
27129
27157
  });
27130
27158
  }
27131
27159
  get shouldDisplayCellReference() {
27132
27160
  return this.isCellReferenceVisible;
27133
27161
  }
27134
27162
  get cellReference() {
27163
+ if (!this.env.model.getters.getCurrentEditedCell()) {
27164
+ return "";
27165
+ }
27135
27166
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
27136
27167
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
27137
27168
  return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
@@ -27143,7 +27174,27 @@ class GridComposer extends Component {
27143
27174
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
27144
27175
  });
27145
27176
  }
27177
+ get composerProps() {
27178
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27179
+ return {
27180
+ rect: { ...this.rect },
27181
+ delimitation: {
27182
+ width,
27183
+ height,
27184
+ },
27185
+ focus: this.props.focus,
27186
+ isDefaultFocus: true,
27187
+ onComposerContentFocused: this.props.onComposerContentFocused,
27188
+ onComposerCellFocused: this.props.onComposerCellFocused,
27189
+ };
27190
+ }
27146
27191
  get containerStyle() {
27192
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
27193
+ return `
27194
+ position: absolute;
27195
+ z-index: -1000;
27196
+ `;
27197
+ }
27147
27198
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
27148
27199
  const cell = this.env.model.getters.getActiveCell();
27149
27200
  const position = this.env.model.getters.getActivePosition();
@@ -27163,6 +27214,8 @@ class GridComposer extends Component {
27163
27214
  if (!isFormula) {
27164
27215
  textAlign = style.align || cell.defaultAlign;
27165
27216
  }
27217
+ const maxHeight = this.props.gridDims.height - this.rect.y;
27218
+ const maxWidth = this.props.gridDims.width - this.rect.x;
27166
27219
  /**
27167
27220
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
27168
27221
  * including all the paddings/margins of the composer
@@ -27174,6 +27227,8 @@ class GridComposer extends Component {
27174
27227
  top: `${top}px`,
27175
27228
  "min-width": `${width + 1}px`,
27176
27229
  "min-height": `${height + 1}px`,
27230
+ "max-width": `${maxWidth}px`,
27231
+ "max-height": `${maxHeight}px`,
27177
27232
  background,
27178
27233
  color,
27179
27234
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -27183,20 +27238,38 @@ class GridComposer extends Component {
27183
27238
  "text-align": textAlign,
27184
27239
  });
27185
27240
  }
27186
- get composerStyle() {
27187
- const maxHeight = this.props.gridDims.height - this.rect.y;
27188
- const maxWidth = this.props.gridDims.width - this.rect.x;
27189
- return cssPropertiesToCss({
27190
- "max-width": `${maxWidth}px`,
27191
- "max-height": `${maxHeight}px`,
27192
- });
27241
+ updateComponentPosition() {
27242
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
27243
+ if (this.isEditing !== isEditing) {
27244
+ this.isEditing = isEditing;
27245
+ if (!isEditing) {
27246
+ this.rect = this.defaultRect;
27247
+ this.env.focusableElement.focus();
27248
+ return;
27249
+ }
27250
+ const position = this.env.model.getters.getActivePosition();
27251
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
27252
+ this.rect = this.env.model.getters.getVisibleRect(zone);
27253
+ }
27254
+ }
27255
+ updateCellReferenceVisibility() {
27256
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
27257
+ return;
27258
+ }
27259
+ const sheetId = this.env.model.getters.getActiveSheetId();
27260
+ const zone = this.env.model.getters.getSelectedZone();
27261
+ const rect = this.env.model.getters.getVisibleRect(zone);
27262
+ if (!deepEquals(rect, this.rect) ||
27263
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
27264
+ this.isCellReferenceVisible = true;
27265
+ }
27193
27266
  }
27194
27267
  }
27195
27268
  GridComposer.props = {
27196
27269
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27197
- onComposerUnmounted: Function,
27198
27270
  onComposerContentFocused: Function,
27199
27271
  gridDims: Object,
27272
+ onComposerCellFocused: Function,
27200
27273
  };
27201
27274
 
27202
27275
  const CSS$1 = css /* scss */ `
@@ -28287,7 +28360,6 @@ class GridOverlay extends Component {
28287
28360
  this.props.onCellDoubleClicked(col, row);
28288
28361
  }
28289
28362
  onContextMenu(ev) {
28290
- ev.preventDefault();
28291
28363
  const [col, row] = this.getCartesianCoordinates(ev);
28292
28364
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
28293
28365
  }
@@ -29372,7 +29444,6 @@ class Grid extends Component {
29372
29444
  HEADER_WIDTH = HEADER_WIDTH;
29373
29445
  menuState;
29374
29446
  gridRef;
29375
- hiddenInput;
29376
29447
  onMouseWheel;
29377
29448
  canvasPosition;
29378
29449
  hoveredCell;
@@ -29383,17 +29454,16 @@ class Grid extends Component {
29383
29454
  menuItems: [],
29384
29455
  });
29385
29456
  this.gridRef = useRef("grid");
29386
- this.hiddenInput = useRef("hiddenInput");
29387
29457
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
29388
29458
  this.hoveredCell = useState({ col: undefined, row: undefined });
29389
29459
  useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
29390
29460
  useExternalListener(document.body, "cut", this.copy.bind(this, true));
29391
29461
  useExternalListener(document.body, "copy", this.copy.bind(this, false));
29392
29462
  useExternalListener(document.body, "paste", this.paste);
29393
- onMounted(() => this.focus());
29394
- this.props.exposeFocus(() => this.focus());
29463
+ onMounted(() => this.focusDefaultElement());
29464
+ this.props.exposeFocus(() => this.focusDefaultElement());
29395
29465
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
29396
- useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
29466
+ useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
29397
29467
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
29398
29468
  this.moveCanvas(deltaX, deltaY);
29399
29469
  this.hoveredCell.col = undefined;
@@ -29416,7 +29486,7 @@ class Grid extends Component {
29416
29486
  if (this.env.model.getters.hasOpenedPopover()) {
29417
29487
  this.closeOpenedPopover();
29418
29488
  }
29419
- this.focus();
29489
+ this.focusDefaultElement();
29420
29490
  }
29421
29491
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
29422
29492
  // down itself
@@ -29595,10 +29665,10 @@ class Grid extends Component {
29595
29665
  "ALT+SHIFT+ARROWUP": () => this.processHeaderGroupingKey("up"),
29596
29666
  "ALT+SHIFT+ARROWDOWN": () => this.processHeaderGroupingKey("down"),
29597
29667
  };
29598
- focus() {
29668
+ focusDefaultElement() {
29599
29669
  if (!this.env.model.getters.getSelectedFigureId() &&
29600
29670
  this.env.model.getters.getEditionMode() === "inactive") {
29601
- this.hiddenInput.el?.focus();
29671
+ this.env.focusableElement.focus();
29602
29672
  }
29603
29673
  }
29604
29674
  get gridEl() {
@@ -29743,19 +29813,6 @@ class Grid extends Component {
29743
29813
  return;
29744
29814
  }
29745
29815
  }
29746
- onInput(ev) {
29747
- // the user meant to paste in the sheet, not open the composer with the pasted content
29748
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
29749
- return;
29750
- }
29751
- if (ev.data) {
29752
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
29753
- // character
29754
- ev.preventDefault();
29755
- ev.stopPropagation();
29756
- this.props.onGridComposerCellFocused(ev.data);
29757
- }
29758
- }
29759
29816
  // ---------------------------------------------------------------------------
29760
29817
  // Context Menu
29761
29818
  // ---------------------------------------------------------------------------
@@ -29870,7 +29927,7 @@ class Grid extends Component {
29870
29927
  }
29871
29928
  closeMenu() {
29872
29929
  this.menuState.isOpen = false;
29873
- this.focus();
29930
+ this.focusDefaultElement();
29874
29931
  }
29875
29932
  processHeaderGroupingKey(direction) {
29876
29933
  if (this.env.model.getters.getSelectedZones().length !== 1) {
@@ -33765,6 +33822,31 @@ const MIGRATIONS = [
33765
33822
  return data;
33766
33823
  },
33767
33824
  },
33825
+ {
33826
+ description: "Fix datafilter duplication",
33827
+ from: 12,
33828
+ to: 12.5,
33829
+ applyMigration(data) {
33830
+ for (let sheet of data.sheets || []) {
33831
+ let knownDataFilterZones = [];
33832
+ for (let filterTable of sheet.filterTables || []) {
33833
+ const zone = toZone(filterTable.range);
33834
+ // See commit message for the details
33835
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
33836
+ if (intersectZoneIndex !== -1) {
33837
+ knownDataFilterZones[intersectZoneIndex] = zone;
33838
+ }
33839
+ else {
33840
+ knownDataFilterZones.push(zone);
33841
+ }
33842
+ }
33843
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
33844
+ range: zoneToXc(zone),
33845
+ }));
33846
+ }
33847
+ return data;
33848
+ },
33849
+ },
33768
33850
  {
33769
33851
  description: "Change Border description structure",
33770
33852
  from: 12,
@@ -34604,22 +34686,7 @@ class BordersPlugin extends CorePlugin {
34604
34686
  }
34605
34687
  }
34606
34688
  export(data) {
34607
- // Borders
34608
- let borderId = 0;
34609
34689
  const borders = {};
34610
- /**
34611
- * Get the id of the given border. If the border does not exist, it creates
34612
- * one.
34613
- */
34614
- function getBorderId(border) {
34615
- for (let [key, value] of Object.entries(borders)) {
34616
- if (deepEquals(value, border)) {
34617
- return parseInt(key, 10);
34618
- }
34619
- }
34620
- borders[++borderId] = border;
34621
- return borderId;
34622
- }
34623
34690
  for (let sheet of data.sheets) {
34624
34691
  for (let col = 0; col < sheet.colNumber; col++) {
34625
34692
  for (let row = 0; row < sheet.rowNumber; row++) {
@@ -34627,7 +34694,7 @@ class BordersPlugin extends CorePlugin {
34627
34694
  if (border) {
34628
34695
  const xc = toXC(col, row);
34629
34696
  const cell = sheet.cells[xc];
34630
- const borderId = getBorderId(border);
34697
+ const borderId = getItemId(border, borders);
34631
34698
  if (cell) {
34632
34699
  cell.border = borderId;
34633
34700
  }
@@ -35432,9 +35499,9 @@ class CellPlugin extends CorePlugin {
35432
35499
  allowDispatch(cmd) {
35433
35500
  switch (cmd.type) {
35434
35501
  case "UPDATE_CELL":
35435
- return this.checkCellOutOfSheet(cmd);
35502
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35436
35503
  case "CLEAR_CELL":
35437
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
35504
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35438
35505
  default:
35439
35506
  return "Success" /* CommandResult.Success */;
35440
35507
  }
@@ -35567,9 +35634,6 @@ class CellPlugin extends CorePlugin {
35567
35634
  format: cell.format ? getItemId(cell.format, formats) : undefined,
35568
35635
  content: cell.content || undefined,
35569
35636
  };
35570
- if (cell instanceof FormulaCellWithDependencies) {
35571
- cells[xc].content = cell.contentWithFixedReferences || undefined;
35572
- }
35573
35637
  }
35574
35638
  _sheet.cells = cells;
35575
35639
  }
@@ -35587,8 +35651,8 @@ class CellPlugin extends CorePlugin {
35587
35651
  }
35588
35652
  removeDefaultStyleValues(style) {
35589
35653
  const cleanedStyle = { ...style };
35590
- for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
35591
- if (cleanedStyle[property] === defaultValue) {
35654
+ for (const property in DEFAULT_STYLE) {
35655
+ if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
35592
35656
  delete cleanedStyle[property];
35593
35657
  }
35594
35658
  }
@@ -35745,10 +35809,7 @@ class CellPlugin extends CorePlugin {
35745
35809
  else {
35746
35810
  style = before ? before.style : undefined;
35747
35811
  }
35748
- const locale = this.getters.getLocale();
35749
- let format = ("format" in after ? after.format : before && before.format) ||
35750
- detectDateFormat(afterContent, locale) ||
35751
- detectNumberFormat(afterContent);
35812
+ const format = "format" in after ? after.format : before && before.format;
35752
35813
  /* Read the following IF as:
35753
35814
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
35754
35815
  * - the command says the new content is empty and has no border/format/style
@@ -35789,12 +35850,11 @@ class CellPlugin extends CorePlugin {
35789
35850
  }
35790
35851
  createLiteralCell(id, content, format, style) {
35791
35852
  const locale = this.getters.getLocale();
35792
- content = parseLiteral(content, locale).toString();
35793
35853
  return {
35794
35854
  id,
35795
- content,
35855
+ content: parseLiteral(content, locale).toString(),
35796
35856
  style,
35797
- format,
35857
+ format: format || detectDateFormat(content, locale) || detectNumberFormat(content),
35798
35858
  isFormula: false,
35799
35859
  };
35800
35860
  }
@@ -35820,8 +35880,11 @@ class CellPlugin extends CorePlugin {
35820
35880
  * being a computed property to rebuild the dependencies XC.
35821
35881
  */
35822
35882
  createFormulaCellWithDependencies(id, compiledFormula, format, style, sheetId) {
35823
- const dependencies = compiledFormula.dependencies.map((xc) => this.getters.getRangeFromSheetXC(sheetId, xc));
35824
- return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString.bind(this));
35883
+ const dependencies = [];
35884
+ for (const xc of compiledFormula.dependencies) {
35885
+ dependencies.push(this.getters.getRangeFromSheetXC(sheetId, xc));
35886
+ }
35887
+ return new FormulaCellWithDependencies(id, compiledFormula, format, style, dependencies, sheetId, this.getters.getRangeString);
35825
35888
  }
35826
35889
  createErrorFormula(id, content, format, style, error) {
35827
35890
  return {
@@ -35856,6 +35919,18 @@ class CellPlugin extends CorePlugin {
35856
35919
  }
35857
35920
  return "Success" /* CommandResult.Success */;
35858
35921
  }
35922
+ checkUselessUpdateCell(cmd) {
35923
+ const cell = this.getters.getCell(cmd);
35924
+ const hasContent = "content" in cmd || "formula" in cmd;
35925
+ const hasStyle = "style" in cmd;
35926
+ const hasFormat = "format" in cmd;
35927
+ if ((!hasContent || cell?.content === cmd.content) &&
35928
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
35929
+ (!hasFormat || cell?.format === cmd.format)) {
35930
+ return "NoChanges" /* CommandResult.NoChanges */;
35931
+ }
35932
+ return "Success" /* CommandResult.Success */;
35933
+ }
35859
35934
  }
35860
35935
  class FormulaCellWithDependencies {
35861
35936
  id;
@@ -35875,7 +35950,7 @@ class FormulaCellWithDependencies {
35875
35950
  const tokens = compiledFormula.tokens.map((token) => {
35876
35951
  if (token.type === "REFERENCE") {
35877
35952
  const index = rangeIndex++;
35878
- return new RangeReferenceToken(() => this.getRangeString(dependencies[index], this.sheetId));
35953
+ return new RangeReferenceToken(dependencies, index, this.sheetId, this.getRangeString);
35879
35954
  }
35880
35955
  return token;
35881
35956
  });
@@ -35902,13 +35977,20 @@ class FormulaCellWithDependencies {
35902
35977
  }
35903
35978
  }
35904
35979
  class RangeReferenceToken {
35905
- getValue;
35980
+ ranges;
35981
+ rangeIndex;
35982
+ sheetId;
35983
+ getRangeString;
35906
35984
  type = "REFERENCE";
35907
- constructor(getValue) {
35908
- this.getValue = getValue;
35985
+ constructor(ranges, rangeIndex, sheetId, getRangeString) {
35986
+ this.ranges = ranges;
35987
+ this.rangeIndex = rangeIndex;
35988
+ this.sheetId = sheetId;
35989
+ this.getRangeString = getRangeString;
35909
35990
  }
35910
35991
  get value() {
35911
- return this.getValue();
35992
+ const range = this.ranges[this.rangeIndex];
35993
+ return this.getRangeString(range, this.sheetId);
35912
35994
  }
35913
35995
  }
35914
35996
 
@@ -40644,7 +40726,15 @@ class SpreadsheetRTree {
40644
40726
  if (!this.rTrees[sheetId]) {
40645
40727
  return;
40646
40728
  }
40647
- this.rTrees[sheetId].remove(item, deepEquals);
40729
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
40730
+ }
40731
+ rtreeItemComparer(left, right) {
40732
+ return (left.data == right.data &&
40733
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
40734
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
40735
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
40736
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
40737
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
40648
40738
  }
40649
40739
  }
40650
40740
  /**
@@ -40722,7 +40812,7 @@ class FormulaDependencyGraph {
40722
40812
  const queue = Array.from(ranges).reverse();
40723
40813
  while (queue.length > 0) {
40724
40814
  const range = queue.pop();
40725
- visited.add(...this.encoder.encodeBoundingBox(range));
40815
+ visited.addMany(this.encoder.encodeBoundingBox(range));
40726
40816
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
40727
40817
  for (const positionId of impactedPositionIds) {
40728
40818
  if (!visited.has(positionId)) {
@@ -40730,7 +40820,7 @@ class FormulaDependencyGraph {
40730
40820
  }
40731
40821
  }
40732
40822
  }
40733
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
40823
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
40734
40824
  return visited;
40735
40825
  }
40736
40826
  }
@@ -40868,9 +40958,9 @@ class Evaluator {
40868
40958
  const cells = positions.map((p) => this.encoder.encode(p));
40869
40959
  const cellsToCompute = new JetSet(cells);
40870
40960
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
40871
- cellsToCompute.add(...this.getCellsDependingOn(cells));
40872
- cellsToCompute.add(...arrayFormulasPositionIds);
40873
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
40961
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
40962
+ cellsToCompute.addMany(arrayFormulasPositionIds);
40963
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
40874
40964
  this.evaluate(cellsToCompute);
40875
40965
  }
40876
40966
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -40884,7 +40974,7 @@ class Evaluator {
40884
40974
  }
40885
40975
  if (!content) {
40886
40976
  // The previous content could have blocked some array formulas
40887
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
40977
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
40888
40978
  }
40889
40979
  }
40890
40980
  return impactedPositionIds;
@@ -40933,7 +41023,7 @@ class Evaluator {
40933
41023
  }
40934
41024
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
40935
41025
  const cells = new JetSet(arrayFormulas);
40936
- cells.add(...this.getCellsDependingOn(arrayFormulas));
41026
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
40937
41027
  return cells;
40938
41028
  }
40939
41029
  nextPositionsToUpdate = new JetSet();
@@ -41082,7 +41172,7 @@ class Evaluator {
41082
41172
  this.setEvaluatedCell(positionId, evaluatedCell);
41083
41173
  // check if formula dependencies present in the spread zone
41084
41174
  // if so, they need to be recomputed
41085
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
41175
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41086
41176
  };
41087
41177
  }
41088
41178
  invalidateSpreading(positionId) {
@@ -41097,8 +41187,8 @@ class Evaluator {
41097
41187
  continue;
41098
41188
  }
41099
41189
  this.evaluatedCells.delete(child);
41100
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
41101
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
41190
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
41191
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
41102
41192
  }
41103
41193
  this.spreadingRelations.removeNode(positionId);
41104
41194
  }
@@ -41447,7 +41537,13 @@ class EvaluationPlugin extends UIPlugin {
41447
41537
  const format = newFormat
41448
41538
  ? getItemId(newFormat, data.formats)
41449
41539
  : exportedCellData.format;
41450
- const content = !isExported ? newContent : exportedCellData.content;
41540
+ let content;
41541
+ if (formulaCell instanceof FormulaCellWithDependencies) {
41542
+ content = this.getters.getFormulaCellContent(exportedSheetData.id, formulaCell.compiledFormula, formulaCell.compiledFormula.dependencies, true);
41543
+ }
41544
+ else {
41545
+ content = !isExported ? newContent : exportedCellData.content;
41546
+ }
41451
41547
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
41452
41548
  }
41453
41549
  }
@@ -50763,6 +50859,16 @@ class ImageProvider {
50763
50859
  }
50764
50860
  }
50765
50861
 
50862
+ class FocusableElement {
50863
+ focusableElement = undefined;
50864
+ setFocusableElement(element) {
50865
+ this.focusableElement = element;
50866
+ }
50867
+ focus() {
50868
+ this.focusableElement?.focus();
50869
+ }
50870
+ }
50871
+
50766
50872
  const RIPPLE_KEY_FRAMES = [
50767
50873
  { transform: "scale(0)" },
50768
50874
  { transform: "scale(0.8)", offset: 0.33 },
@@ -52919,6 +53025,7 @@ class Spreadsheet extends Component {
52919
53025
  toggleSidePanel: this.toggleSidePanel.bind(this),
52920
53026
  clipboard: this.env.clipboard || instantiateClipboard(),
52921
53027
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
53028
+ focusableElement: new FocusableElement(),
52922
53029
  });
52923
53030
  useExternalListener(window, "resize", () => this.render(true));
52924
53031
  useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -53025,16 +53132,17 @@ class Spreadsheet extends Component {
53025
53132
  }
53026
53133
  this.composer.topBarFocus = "contentFocus";
53027
53134
  this.composer.gridFocusMode = "inactive";
53028
- this.setComposerContent({ selection } || {});
53135
+ this.setComposerContent({ selection });
53029
53136
  }
53030
- onGridComposerContentFocused() {
53137
+ onGridComposerContentFocused(selection) {
53031
53138
  if (this.model.getters.isReadonly()) {
53032
53139
  return;
53033
53140
  }
53034
53141
  this.composer.topBarFocus = "inactive";
53035
53142
  this.composer.gridFocusMode = "contentFocus";
53036
- this.setComposerContent({});
53143
+ this.setComposerContent({ selection });
53037
53144
  }
53145
+ // TODO: either both are defined or none of them. change those args to an object
53038
53146
  onGridComposerCellFocused(content, selection) {
53039
53147
  if (this.model.getters.isReadonly()) {
53040
53148
  return;
@@ -54460,11 +54568,10 @@ class SelectionStreamProcessorImpl {
54460
54568
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
54461
54569
  */
54462
54570
  getEndOfCluster(startPosition, dim, dir) {
54463
- const sheet = this.getters.getActiveSheet();
54464
54571
  let currentPosition = startPosition;
54465
54572
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
54466
54573
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
54467
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
54574
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
54468
54575
  ? "endOfCluster"
54469
54576
  : "nextCluster";
54470
54577
  while (true) {
@@ -54474,7 +54581,7 @@ class SelectionStreamProcessorImpl {
54474
54581
  currentPosition.row === nextCellPosition.row) {
54475
54582
  break;
54476
54583
  }
54477
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
54584
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
54478
54585
  if (mode === "endOfCluster" && isNextCellEmpty) {
54479
54586
  break;
54480
54587
  }
@@ -54488,10 +54595,20 @@ class SelectionStreamProcessorImpl {
54488
54595
  return dim === "cols" ? currentPosition.col : currentPosition.row;
54489
54596
  }
54490
54597
  /**
54491
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
54492
- * check if the merge containing the cell is empty.
54598
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
54599
+ * the check applies to the main cell of the merge.
54600
+ */
54601
+ isCellEmpty({ col, row }) {
54602
+ const sheetId = this.getters.getActiveSheetId();
54603
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
54604
+ return !(this.getters.getCorrespondingFormulaCell(position) || this.getters.getCell(position)?.content);
54605
+ }
54606
+ /**
54607
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
54608
+ * the check applies to the main cell of the merge.
54493
54609
  */
54494
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
54610
+ isEvaluatedCellEmpty({ col, row }) {
54611
+ const sheetId = this.getters.getActiveSheetId();
54495
54612
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
54496
54613
  const cell = this.getters.getEvaluatedCell(position);
54497
54614
  return cell.type === CellValueType.empty;
@@ -56762,6 +56879,6 @@ const constants = {
56762
56879
  export { AbstractChart, 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 };
56763
56880
 
56764
56881
 
56765
- __info__.version = "17.0.10";
56766
- __info__.date = "2024-02-02T13:48:28.727Z";
56767
- __info__.hash = "be06aa9";
56882
+ __info__.version = "17.0.12";
56883
+ __info__.date = "2024-02-16T14:24:02.984Z";
56884
+ __info__.hash = "8fa2bab";