@odoo/o-spreadsheet 16.4.20 → 16.4.23

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.
@@ -701,18 +701,18 @@ function isConsecutive(iterable) {
701
701
  return true;
702
702
  }
703
703
  class JetSet extends Set {
704
- add(...iterable) {
704
+ addMany(iterable) {
705
705
  for (const element of iterable) {
706
706
  super.add(element);
707
707
  }
708
708
  return this;
709
709
  }
710
- delete(...iterable) {
711
- let deleted = false;
710
+ deleteMany(iterable) {
711
+ let wasDeleted = false;
712
712
  for (const element of iterable) {
713
- deleted ||= super.delete(element);
713
+ wasDeleted ||= super.delete(element);
714
714
  }
715
- return deleted;
715
+ return wasDeleted;
716
716
  }
717
717
  }
718
718
  /**
@@ -1838,6 +1838,9 @@ function parseFormat(formatString) {
1838
1838
  function formatValue(value, { format, locale }) {
1839
1839
  switch (typeof value) {
1840
1840
  case "string":
1841
+ if (value.includes('\\"')) {
1842
+ return value.replace(/\\"/g, '"');
1843
+ }
1841
1844
  return value;
1842
1845
  case "boolean":
1843
1846
  return value ? "TRUE" : "FALSE";
@@ -15235,6 +15238,9 @@ function isValidLocale(locale) {
15235
15238
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
15236
15239
  return false;
15237
15240
  }
15241
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
15242
+ return false;
15243
+ }
15238
15244
  try {
15239
15245
  formatValue(1, { locale, format: "#,##0.00" });
15240
15246
  formatValue(1, { locale, format: locale.dateFormat });
@@ -15325,7 +15331,7 @@ function canonicalizeNumberLiteral(content, locale) {
15325
15331
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
15326
15332
  return content;
15327
15333
  }
15328
- return content.replace(locale.decimalSeparator, ".");
15334
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
15329
15335
  }
15330
15336
  /**
15331
15337
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -15971,14 +15977,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
15971
15977
  : undefined));
15972
15978
  }
15973
15979
  }
15974
- else if (zone.left === zone.right && zone.top === zone.bottom) {
15975
- // A single cell. If it's only the title, the dataset is not added.
15976
- if (!dataSetsHaveTitle) {
15977
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
15978
- }
15979
- }
15980
15980
  else {
15981
- /* 1 row or 1 column */
15981
+ /* 1 cell, 1 row or 1 column */
15982
15982
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
15983
15983
  ? {
15984
15984
  top: zone.top,
@@ -16027,8 +16027,10 @@ function toExcelDataset(getters, ds) {
16027
16027
  }
16028
16028
  const dataRange = ds.dataRange.clone({ zone: dataZone });
16029
16029
  return {
16030
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
16031
- range: getters.getRangeString(dataRange, "forceSheetReference"),
16030
+ label: ds.labelCell
16031
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", true)
16032
+ : undefined,
16033
+ range: getters.getRangeString(dataRange, "forceSheetReference", true),
16032
16034
  };
16033
16035
  }
16034
16036
  function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
@@ -16041,7 +16043,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
16041
16043
  zone.top = zone.top + 1;
16042
16044
  }
16043
16045
  const range = labelRange.clone({ zone });
16044
- return getters.getRangeString(range, "forceSheetReference");
16046
+ return getters.getRangeString(range, "forceSheetReference", true);
16045
16047
  }
16046
16048
  /**
16047
16049
  * Transform a chart definition which supports dataSets (dataSets and LabelRange)
@@ -16578,7 +16580,7 @@ class BarChart extends AbstractChart {
16578
16580
  return undefined;
16579
16581
  const dataSets = this.dataSets
16580
16582
  .map((ds) => toExcelDataset(this.getters, ds))
16581
- .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
16583
+ .filter((ds) => ds.range !== "" && ds.range !== INCORRECT_RANGE_STRING);
16582
16584
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
16583
16585
  return {
16584
16586
  ...this.getDefinition(),
@@ -17166,7 +17168,7 @@ class LineChart extends AbstractChart {
17166
17168
  return undefined;
17167
17169
  const dataSets = this.dataSets
17168
17170
  .map((ds) => toExcelDataset(this.getters, ds))
17169
- .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
17171
+ .filter((ds) => ds.range !== "" && ds.range !== INCORRECT_RANGE_STRING);
17170
17172
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
17171
17173
  return {
17172
17174
  ...this.getDefinition(),
@@ -17453,7 +17455,7 @@ class PieChart extends AbstractChart {
17453
17455
  return undefined;
17454
17456
  const dataSets = this.dataSets
17455
17457
  .map((ds) => toExcelDataset(this.getters, ds))
17456
- .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
17458
+ .filter((ds) => ds.range !== "" && ds.range !== INCORRECT_RANGE_STRING);
17457
17459
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
17458
17460
  return {
17459
17461
  ...this.getDefinition(),
@@ -18631,6 +18633,12 @@ const INSERT_LINK = (env) => {
18631
18633
  let { col, row } = env.model.getters.getActivePosition();
18632
18634
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
18633
18635
  };
18636
+ const INSERT_LINK_NAME = (env) => {
18637
+ const sheetId = env.model.getters.getActiveSheetId();
18638
+ const { col, row } = env.model.getters.getActivePosition();
18639
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
18640
+ return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
18641
+ };
18634
18642
  //------------------------------------------------------------------------------
18635
18643
  // Filters action
18636
18644
  //------------------------------------------------------------------------------
@@ -19171,7 +19179,7 @@ cellMenuRegistry
19171
19179
  })
19172
19180
  .add("insert_link", {
19173
19181
  ...insertLink,
19174
- name: _lt("Insert link"),
19182
+ name: INSERT_LINK_NAME,
19175
19183
  sequence: 150,
19176
19184
  separator: true,
19177
19185
  });
@@ -24082,6 +24090,7 @@ class Composer extends Component {
24082
24090
  static components = { TextValueProvider, FunctionDescriptionProvider };
24083
24091
  static defaultProps = {
24084
24092
  inputStyle: "",
24093
+ isDefaultFocus: false,
24085
24094
  };
24086
24095
  composerRef = useRef("o_composer");
24087
24096
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -24134,7 +24143,6 @@ class Composer extends Component {
24134
24143
  F2: () => console.warn("Not implemented"),
24135
24144
  F4: this.processF4Key,
24136
24145
  Tab: (ev) => this.processTabKey(ev),
24137
- " ": (ev) => this.processSpaceKey(ev),
24138
24146
  };
24139
24147
  keyCodeMapping = {
24140
24148
  NumpadDecimal: this.processNumpadDecimal,
@@ -24142,11 +24150,11 @@ class Composer extends Component {
24142
24150
  setup() {
24143
24151
  onMounted(() => {
24144
24152
  const el = this.composerRef.el;
24153
+ if (this.props.isDefaultFocus) {
24154
+ this.env.focusableElement.setFocusableElement(el);
24155
+ }
24145
24156
  this.contentHelper.updateEl(el);
24146
24157
  });
24147
- onWillUnmount(() => {
24148
- this.props.onComposerUnmounted?.();
24149
- });
24150
24158
  useEffect(() => {
24151
24159
  this.processContent();
24152
24160
  });
@@ -24155,7 +24163,8 @@ class Composer extends Component {
24155
24163
  // Handlers
24156
24164
  // ---------------------------------------------------------------------------
24157
24165
  processArrowKeys(ev) {
24158
- if (this.env.model.getters.isSelectingForComposer()) {
24166
+ if (this.env.model.getters.isSelectingForComposer() ||
24167
+ this.env.model.getters.getEditionMode() === "inactive") {
24159
24168
  this.functionDescriptionState.showDescription = false;
24160
24169
  // Prevent the default content editable behavior which moves the cursor
24161
24170
  ev.preventDefault();
@@ -24194,25 +24203,19 @@ class Composer extends Component {
24194
24203
  processTabKey(ev) {
24195
24204
  ev.preventDefault();
24196
24205
  ev.stopPropagation();
24197
- if (this.autoCompleteState.showProvider) {
24198
- const autoCompleteValue = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]?.text;
24199
- if (autoCompleteValue) {
24200
- this.autoComplete(autoCompleteValue);
24201
- return;
24206
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
24207
+ if (this.autoCompleteState.showProvider) {
24208
+ const autoCompleteValue = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]?.text;
24209
+ if (autoCompleteValue) {
24210
+ this.autoComplete(autoCompleteValue);
24211
+ return;
24212
+ }
24202
24213
  }
24214
+ this.env.model.dispatch("STOP_EDITION");
24203
24215
  }
24204
24216
  const direction = ev.shiftKey ? "left" : "right";
24205
- this.env.model.dispatch("STOP_EDITION");
24206
24217
  this.env.model.selection.moveAnchorCell(direction, 1);
24207
24218
  }
24208
- processSpaceKey(ev) {
24209
- if (ev.ctrlKey) {
24210
- ev.preventDefault();
24211
- ev.stopPropagation();
24212
- this.showAutocomplete("");
24213
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
24214
- }
24215
- }
24216
24219
  processEnterKey(ev) {
24217
24220
  ev.preventDefault();
24218
24221
  ev.stopPropagation();
@@ -24264,6 +24267,9 @@ class Composer extends Component {
24264
24267
  this.compositionActive = false;
24265
24268
  }
24266
24269
  onKeydown(ev) {
24270
+ if (this.env.model.getters.getEditionMode() === "inactive") {
24271
+ return;
24272
+ }
24267
24273
  let handler = this.keyMapping[ev.key] || this.keyCodeMapping[ev.code];
24268
24274
  if (handler) {
24269
24275
  handler.call(this, ev);
@@ -24272,14 +24278,33 @@ class Composer extends Component {
24272
24278
  ev.stopPropagation();
24273
24279
  }
24274
24280
  }
24281
+ onPaste(ev) {
24282
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
24283
+ ev.stopPropagation();
24284
+ }
24285
+ }
24275
24286
  /*
24276
24287
  * Triggered automatically by the content-editable between the keydown and key up
24277
24288
  * */
24278
24289
  onInput(ev) {
24279
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
24290
+ if (!this.shouldProcessInputEvents) {
24291
+ return;
24292
+ }
24293
+ if (ev.inputType === "insertFromPaste" &&
24294
+ this.env.model.getters.getEditionMode() === "inactive") {
24280
24295
  return;
24281
24296
  }
24282
- let content = this.contentHelper.getText();
24297
+ ev.stopPropagation();
24298
+ let content;
24299
+ if (this.env.model.getters.getEditionMode() === "inactive") {
24300
+ content = ev.data || "";
24301
+ }
24302
+ else {
24303
+ content = this.contentHelper.getText();
24304
+ }
24305
+ if (this.props.focus === "inactive") {
24306
+ return this.props.onComposerCellFocused?.(content);
24307
+ }
24283
24308
  let selection = this.contentHelper.getCurrentSelection();
24284
24309
  if (ev.inputType === "insertParagraph") {
24285
24310
  const start = Math.min(selection.start, selection.end);
@@ -24357,9 +24382,8 @@ class Composer extends Component {
24357
24382
  }
24358
24383
  const newSelection = this.contentHelper.getCurrentSelection();
24359
24384
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
24360
- if (this.props.focus === "inactive") {
24361
- this.props.onComposerContentFocused(newSelection);
24362
- }
24385
+ this.props.onComposerContentFocused();
24386
+ if (this.props.focus === "inactive") ;
24363
24387
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
24364
24388
  this.processTokenAtCursor();
24365
24389
  }
@@ -24526,7 +24550,7 @@ class Composer extends Component {
24526
24550
  const highlights = this.env.model.getters.getHighlights();
24527
24551
  const refSheet = sheetName
24528
24552
  ? this.env.model.getters.getSheetIdByName(sheetName)
24529
- : this.env.model.getters.getCurrentEditedCell().sheetId;
24553
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
24530
24554
  const highlight = highlights.find((highlight) => {
24531
24555
  if (highlight.sheetId !== refSheet)
24532
24556
  return false;
@@ -24606,12 +24630,13 @@ class Composer extends Component {
24606
24630
  }
24607
24631
  }
24608
24632
  Composer.props = {
24609
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
24610
- onComposerContentFocused: Function,
24611
24633
  inputStyle: { type: String, optional: true },
24612
24634
  rect: { type: Object, optional: true },
24613
24635
  delimitation: { type: Object, optional: true },
24614
- onComposerUnmounted: { type: Function, optional: true },
24636
+ focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
24637
+ onComposerCellFocused: { type: Function, optional: true },
24638
+ onComposerContentFocused: Function,
24639
+ isDefaultFocus: { type: Boolean, optional: true },
24615
24640
  };
24616
24641
 
24617
24642
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
@@ -24645,51 +24670,26 @@ css /* scss */ `
24645
24670
  class GridComposer extends Component {
24646
24671
  static template = "o-spreadsheet-GridComposer";
24647
24672
  static components = { Composer };
24648
- gridComposerRef;
24649
- zone;
24650
- rect;
24673
+ // TODORAR see if we can keep it undefined
24674
+ rect = this.defaultRect;
24675
+ isEditing = false;
24651
24676
  isCellReferenceVisible;
24652
- composerState;
24677
+ get defaultRect() {
24678
+ return { x: 0, y: 0, width: 0, height: 0 };
24679
+ }
24653
24680
  setup() {
24654
- this.gridComposerRef = useRef("gridComposer");
24655
- this.composerState = useState({
24656
- rect: undefined,
24657
- delimitation: undefined,
24658
- });
24659
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
24660
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
24661
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
24662
- this.isCellReferenceVisible = false;
24663
- onMounted(() => {
24664
- const el = this.gridComposerRef.el;
24665
- this.composerState.rect = {
24666
- x: this.rect.x,
24667
- y: this.rect.y,
24668
- width: el.clientWidth,
24669
- height: el.clientHeight,
24670
- };
24671
- this.composerState.delimitation = {
24672
- width: this.props.gridDims.width,
24673
- height: this.props.gridDims.height,
24674
- };
24675
- });
24676
24681
  onWillUpdateProps(() => {
24677
- if (this.isCellReferenceVisible) {
24678
- return;
24679
- }
24680
- const sheetId = this.env.model.getters.getActiveSheetId();
24681
- const zone = this.env.model.getters.getSelectedZone();
24682
- const rect = this.env.model.getters.getVisibleRect(zone);
24683
- if (!deepEquals(rect, this.rect) ||
24684
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
24685
- this.isCellReferenceVisible = true;
24686
- }
24682
+ this.updateComponentPosition();
24683
+ this.updateCellReferenceVisibility();
24687
24684
  });
24688
24685
  }
24689
24686
  get shouldDisplayCellReference() {
24690
24687
  return this.isCellReferenceVisible;
24691
24688
  }
24692
24689
  get cellReference() {
24690
+ if (!this.env.model.getters.getCurrentEditedCell()) {
24691
+ return "";
24692
+ }
24693
24693
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
24694
24694
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
24695
24695
  return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
@@ -24701,7 +24701,27 @@ class GridComposer extends Component {
24701
24701
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
24702
24702
  });
24703
24703
  }
24704
+ get composerProps() {
24705
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
24706
+ return {
24707
+ rect: { ...this.rect },
24708
+ delimitation: {
24709
+ width,
24710
+ height,
24711
+ },
24712
+ focus: this.props.focus,
24713
+ isDefaultFocus: true,
24714
+ onComposerContentFocused: this.props.onComposerContentFocused,
24715
+ onComposerCellFocused: this.props.onComposerCellFocused,
24716
+ };
24717
+ }
24704
24718
  get containerStyle() {
24719
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
24720
+ return `
24721
+ position: absolute;
24722
+ z-index: -1000;
24723
+ `;
24724
+ }
24705
24725
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
24706
24726
  const cell = this.env.model.getters.getActiveCell();
24707
24727
  const position = this.env.model.getters.getActivePosition();
@@ -24721,6 +24741,8 @@ class GridComposer extends Component {
24721
24741
  if (!isFormula) {
24722
24742
  textAlign = style.align || cell.defaultAlign;
24723
24743
  }
24744
+ const maxHeight = this.props.gridDims.height - this.rect.y;
24745
+ const maxWidth = this.props.gridDims.width - this.rect.x;
24724
24746
  /**
24725
24747
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
24726
24748
  * including all the paddings/margins of the composer
@@ -24732,6 +24754,8 @@ class GridComposer extends Component {
24732
24754
  top: `${top}px`,
24733
24755
  "min-width": `${width + 1}px`,
24734
24756
  "min-height": `${height + 1}px`,
24757
+ "max-width": `${maxWidth}px`,
24758
+ "max-height": `${maxHeight}px`,
24735
24759
  background,
24736
24760
  color,
24737
24761
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -24741,20 +24765,38 @@ class GridComposer extends Component {
24741
24765
  "text-align": textAlign,
24742
24766
  });
24743
24767
  }
24744
- get composerStyle() {
24745
- const maxHeight = this.props.gridDims.height - this.rect.y;
24746
- const maxWidth = this.props.gridDims.width - this.rect.x;
24747
- return cssPropertiesToCss({
24748
- "max-width": `${maxWidth}px`,
24749
- "max-height": `${maxHeight}px`,
24750
- });
24768
+ updateComponentPosition() {
24769
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
24770
+ if (this.isEditing !== isEditing) {
24771
+ this.isEditing = isEditing;
24772
+ if (!isEditing) {
24773
+ this.rect = this.defaultRect;
24774
+ this.env.focusableElement.focus();
24775
+ return;
24776
+ }
24777
+ const position = this.env.model.getters.getActivePosition();
24778
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
24779
+ this.rect = this.env.model.getters.getVisibleRect(zone);
24780
+ }
24781
+ }
24782
+ updateCellReferenceVisibility() {
24783
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
24784
+ return;
24785
+ }
24786
+ const sheetId = this.env.model.getters.getActiveSheetId();
24787
+ const zone = this.env.model.getters.getSelectedZone();
24788
+ const rect = this.env.model.getters.getVisibleRect(zone);
24789
+ if (!deepEquals(rect, this.rect) ||
24790
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
24791
+ this.isCellReferenceVisible = true;
24792
+ }
24751
24793
  }
24752
24794
  }
24753
24795
  GridComposer.props = {
24754
24796
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
24755
- onComposerUnmounted: Function,
24756
24797
  onComposerContentFocused: Function,
24757
24798
  gridDims: Object,
24799
+ onComposerCellFocused: Function,
24758
24800
  };
24759
24801
 
24760
24802
  const CSS$1 = css /* scss */ `
@@ -25635,7 +25677,6 @@ class GridOverlay extends Component {
25635
25677
  this.props.onCellDoubleClicked(col, row);
25636
25678
  }
25637
25679
  onContextMenu(ev) {
25638
- ev.preventDefault();
25639
25680
  const [col, row] = this.getCartesianCoordinates(ev);
25640
25681
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
25641
25682
  }
@@ -26728,7 +26769,6 @@ class Grid extends Component {
26728
26769
  HEADER_WIDTH = HEADER_WIDTH;
26729
26770
  menuState;
26730
26771
  gridRef;
26731
- hiddenInput;
26732
26772
  onMouseWheel;
26733
26773
  canvasPosition;
26734
26774
  hoveredCell;
@@ -26739,17 +26779,16 @@ class Grid extends Component {
26739
26779
  menuItems: [],
26740
26780
  });
26741
26781
  this.gridRef = useRef("grid");
26742
- this.hiddenInput = useRef("hiddenInput");
26743
26782
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
26744
26783
  this.hoveredCell = useState({ col: undefined, row: undefined });
26745
26784
  useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
26746
26785
  useExternalListener(document.body, "cut", this.copy.bind(this, true));
26747
26786
  useExternalListener(document.body, "copy", this.copy.bind(this, false));
26748
26787
  useExternalListener(document.body, "paste", this.paste);
26749
- onMounted(() => this.focus());
26750
- this.props.exposeFocus(() => this.focus());
26788
+ onMounted(() => this.focusDefaultElement());
26789
+ this.props.exposeFocus(() => this.focusDefaultElement());
26751
26790
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
26752
- useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
26791
+ useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
26753
26792
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
26754
26793
  this.moveCanvas(deltaX, deltaY);
26755
26794
  this.hoveredCell.col = undefined;
@@ -26772,7 +26811,7 @@ class Grid extends Component {
26772
26811
  if (this.env.model.getters.hasOpenedPopover()) {
26773
26812
  this.closeOpenedPopover();
26774
26813
  }
26775
- this.focus();
26814
+ this.focusDefaultElement();
26776
26815
  }
26777
26816
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
26778
26817
  // down itself
@@ -26945,10 +26984,10 @@ class Grid extends Component {
26945
26984
  PAGEUP: () => this.env.model.dispatch("SHIFT_VIEWPORT_UP"),
26946
26985
  "CTRL+K": () => INSERT_LINK(this.env),
26947
26986
  };
26948
- focus() {
26987
+ focusDefaultElement() {
26949
26988
  if (!this.env.model.getters.getSelectedFigureId() &&
26950
26989
  this.env.model.getters.getEditionMode() === "inactive") {
26951
- this.hiddenInput.el?.focus();
26990
+ this.env.focusableElement.focus();
26952
26991
  }
26953
26992
  }
26954
26993
  get gridEl() {
@@ -27092,19 +27131,6 @@ class Grid extends Component {
27092
27131
  return;
27093
27132
  }
27094
27133
  }
27095
- onInput(ev) {
27096
- // the user meant to paste in the sheet, not open the composer with the pasted content
27097
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
27098
- return;
27099
- }
27100
- if (ev.data) {
27101
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
27102
- // character
27103
- ev.preventDefault();
27104
- ev.stopPropagation();
27105
- this.props.onGridComposerCellFocused(ev.data);
27106
- }
27107
- }
27108
27134
  // ---------------------------------------------------------------------------
27109
27135
  // Context Menu
27110
27136
  // ---------------------------------------------------------------------------
@@ -27217,7 +27243,7 @@ class Grid extends Component {
27217
27243
  }
27218
27244
  closeMenu() {
27219
27245
  this.menuState.isOpen = false;
27220
- this.focus();
27246
+ this.focusDefaultElement();
27221
27247
  }
27222
27248
  }
27223
27249
  Grid.props = {
@@ -30877,6 +30903,31 @@ const MIGRATIONS = [
30877
30903
  return data;
30878
30904
  },
30879
30905
  },
30906
+ {
30907
+ description: "Fix datafilter duplication",
30908
+ from: 12,
30909
+ to: 12.5,
30910
+ applyMigration(data) {
30911
+ for (let sheet of data.sheets || []) {
30912
+ let knownDataFilterZones = [];
30913
+ for (let filterTable of sheet.filterTables || []) {
30914
+ const zone = toZone(filterTable.range);
30915
+ // See commit message for the details
30916
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
30917
+ if (intersectZoneIndex !== -1) {
30918
+ knownDataFilterZones[intersectZoneIndex] = zone;
30919
+ }
30920
+ else {
30921
+ knownDataFilterZones.push(zone);
30922
+ }
30923
+ }
30924
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
30925
+ range: zoneToXc(zone),
30926
+ }));
30927
+ }
30928
+ return data;
30929
+ },
30930
+ },
30880
30931
  {
30881
30932
  description: "Change Border description structure",
30882
30933
  from: 12,
@@ -31796,9 +31847,9 @@ class CellPlugin extends CorePlugin {
31796
31847
  allowDispatch(cmd) {
31797
31848
  switch (cmd.type) {
31798
31849
  case "UPDATE_CELL":
31799
- return this.checkCellOutOfSheet(cmd);
31850
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
31800
31851
  case "CLEAR_CELL":
31801
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
31852
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
31802
31853
  default:
31803
31854
  return 0 /* CommandResult.Success */;
31804
31855
  }
@@ -31964,13 +32015,13 @@ class CellPlugin extends CorePlugin {
31964
32015
  /*
31965
32016
  * Reconstructs the original formula string based on a normalized form and its dependencies
31966
32017
  */
31967
- buildFormulaContent(sheetId, cell, dependencies) {
32018
+ buildFormulaContent(sheetId, cell, dependencies, useFixedZone = false) {
31968
32019
  const ranges = dependencies || cell.dependencies;
31969
32020
  let rangeIndex = 0;
31970
32021
  return concat(cell.compiledFormula.tokens.map((token) => {
31971
32022
  if (token.type === "REFERENCE") {
31972
32023
  const range = ranges[rangeIndex++];
31973
- return this.getters.getRangeString(range, sheetId);
32024
+ return this.getters.getRangeString(range, sheetId, useFixedZone);
31974
32025
  }
31975
32026
  return token.value;
31976
32027
  }));
@@ -32092,10 +32143,7 @@ class CellPlugin extends CorePlugin {
32092
32143
  else {
32093
32144
  style = before ? before.style : undefined;
32094
32145
  }
32095
- const locale = this.getters.getLocale();
32096
- let format = ("format" in after ? after.format : before && before.format) ||
32097
- detectDateFormat(afterContent, locale) ||
32098
- detectNumberFormat(afterContent);
32146
+ const format = "format" in after ? after.format : before && before.format;
32099
32147
  /* Read the following IF as:
32100
32148
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
32101
32149
  * - the command says the new content is empty and has no border/format/style
@@ -32136,12 +32184,11 @@ class CellPlugin extends CorePlugin {
32136
32184
  }
32137
32185
  createLiteralCell(id, content, format, style) {
32138
32186
  const locale = this.getters.getLocale();
32139
- content = parseLiteral(content, locale).toString();
32140
32187
  return {
32141
32188
  id,
32142
- content,
32189
+ content: parseLiteral(content, locale).toString(),
32143
32190
  style,
32144
- format,
32191
+ format: format || detectDateFormat(content, locale) || detectNumberFormat(content),
32145
32192
  isFormula: false,
32146
32193
  };
32147
32194
  }
@@ -32202,6 +32249,18 @@ class CellPlugin extends CorePlugin {
32202
32249
  }
32203
32250
  return 0 /* CommandResult.Success */;
32204
32251
  }
32252
+ checkUselessUpdateCell(cmd) {
32253
+ const cell = this.getters.getCell(cmd);
32254
+ const hasContent = "content" in cmd || "formula" in cmd;
32255
+ const hasStyle = "style" in cmd;
32256
+ const hasFormat = "format" in cmd;
32257
+ if ((!hasContent || cell?.content === cmd.content) &&
32258
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
32259
+ (!hasFormat || cell?.format === cmd.format)) {
32260
+ return 98 /* CommandResult.NoChanges */;
32261
+ }
32262
+ return 0 /* CommandResult.Success */;
32263
+ }
32205
32264
  }
32206
32265
  class FormulaCellWithDependencies {
32207
32266
  id;
@@ -32539,7 +32598,13 @@ class ConditionalFormatPlugin extends CorePlugin {
32539
32598
  }
32540
32599
  }
32541
32600
  exportForExcel(data) {
32542
- this.export(data);
32601
+ if (data.sheets) {
32602
+ for (let sheet of data.sheets) {
32603
+ if (this.cfRules[sheet.id]) {
32604
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
32605
+ }
32606
+ }
32607
+ }
32543
32608
  }
32544
32609
  // ---------------------------------------------------------------------------
32545
32610
  // Getters
@@ -32604,11 +32669,11 @@ class ConditionalFormatPlugin extends CorePlugin {
32604
32669
  // ---------------------------------------------------------------------------
32605
32670
  // Private
32606
32671
  // ---------------------------------------------------------------------------
32607
- mapToConditionalFormat(sheetId, cf) {
32672
+ mapToConditionalFormat(sheetId, cf, options = { useFixedReference: false }) {
32608
32673
  return {
32609
32674
  ...cf,
32610
32675
  ranges: cf.ranges.map((range) => {
32611
- return this.getters.getRangeString(range, sheetId);
32676
+ return this.getters.getRangeString(range, sheetId, options.useFixedReference);
32612
32677
  }),
32613
32678
  };
32614
32679
  }
@@ -34445,8 +34510,9 @@ class RangeAdapter {
34445
34510
  *
34446
34511
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
34447
34512
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
34513
+ * @param [useFixedZone=false] if true, the range will be returned with fixed row and column
34448
34514
  */
34449
- getRangeString(range, forSheetId) {
34515
+ getRangeString(range, forSheetId, useFixedZone = false) {
34450
34516
  if (!range) {
34451
34517
  return INCORRECT_RANGE_STRING;
34452
34518
  }
@@ -34473,7 +34539,7 @@ class RangeAdapter {
34473
34539
  if (prefixSheet && !sheetName) {
34474
34540
  return INCORRECT_RANGE_STRING;
34475
34541
  }
34476
- let rangeString = this.getRangePartString(rangeImpl, 0);
34542
+ let rangeString = this.getRangePartString(rangeImpl, 0, useFixedZone);
34477
34543
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
34478
34544
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
34479
34545
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -34483,7 +34549,7 @@ class RangeAdapter {
34483
34549
  rangeImpl.parts[1].rowFixed ||
34484
34550
  rangeImpl.parts[1].colFixed) {
34485
34551
  rangeString += ":";
34486
- rangeString += this.getRangePartString(rangeImpl, 1);
34552
+ rangeString += this.getRangePartString(rangeImpl, 1, useFixedZone);
34487
34553
  }
34488
34554
  }
34489
34555
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -34524,13 +34590,13 @@ class RangeAdapter {
34524
34590
  /**
34525
34591
  * Get a Xc string that represent a part of a range
34526
34592
  */
34527
- getRangePartString(range, part) {
34593
+ getRangePartString(range, part, useFixedZone = false) {
34528
34594
  const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
34529
34595
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
34530
34596
  const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
34531
34597
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
34532
34598
  let str = "";
34533
- if (range.isFullCol) {
34599
+ if (range.isFullCol && !useFixedZone) {
34534
34600
  if (part === 0 && range.unboundedZone.hasHeader) {
34535
34601
  str = colFixed + col + rowFixed + row;
34536
34602
  }
@@ -34538,7 +34604,7 @@ class RangeAdapter {
34538
34604
  str = colFixed + col;
34539
34605
  }
34540
34606
  }
34541
- else if (range.isFullRow) {
34607
+ else if (range.isFullRow && !useFixedZone) {
34542
34608
  if (part === 0 && range.unboundedZone.hasHeader) {
34543
34609
  str = colFixed + col + rowFixed + row;
34544
34610
  }
@@ -36335,7 +36401,15 @@ class SpreadsheetRTree {
36335
36401
  if (!this.rTrees[sheetId]) {
36336
36402
  return;
36337
36403
  }
36338
- this.rTrees[sheetId].remove(item, deepEquals);
36404
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
36405
+ }
36406
+ rtreeItemComparer(left, right) {
36407
+ return (left.data == right.data &&
36408
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
36409
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
36410
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
36411
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
36412
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
36339
36413
  }
36340
36414
  }
36341
36415
  /**
@@ -36413,7 +36487,7 @@ class FormulaDependencyGraph {
36413
36487
  const queue = Array.from(ranges).reverse();
36414
36488
  while (queue.length > 0) {
36415
36489
  const range = queue.pop();
36416
- visited.add(...this.encoder.encodeBoundingBox(range));
36490
+ visited.addMany(this.encoder.encodeBoundingBox(range));
36417
36491
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
36418
36492
  for (const positionId of impactedPositionIds) {
36419
36493
  if (!visited.has(positionId)) {
@@ -36421,7 +36495,7 @@ class FormulaDependencyGraph {
36421
36495
  }
36422
36496
  }
36423
36497
  }
36424
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
36498
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
36425
36499
  return visited;
36426
36500
  }
36427
36501
  }
@@ -36552,9 +36626,9 @@ class Evaluator {
36552
36626
  const cells = positions.map((p) => this.encoder.encode(p));
36553
36627
  const cellsToCompute = new JetSet(cells);
36554
36628
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
36555
- cellsToCompute.add(...this.getCellsDependingOn(cells));
36556
- cellsToCompute.add(...arrayFormulasPositionIds);
36557
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
36629
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
36630
+ cellsToCompute.addMany(arrayFormulasPositionIds);
36631
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
36558
36632
  this.evaluate(cellsToCompute);
36559
36633
  }
36560
36634
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -36568,7 +36642,7 @@ class Evaluator {
36568
36642
  }
36569
36643
  if (!content) {
36570
36644
  // The previous content could have blocked some array formulas
36571
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
36645
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
36572
36646
  }
36573
36647
  }
36574
36648
  return impactedPositionIds;
@@ -36616,7 +36690,7 @@ class Evaluator {
36616
36690
  }
36617
36691
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
36618
36692
  const cells = new JetSet(arrayFormulas);
36619
- cells.add(...this.getCellsDependingOn(arrayFormulas));
36693
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
36620
36694
  return cells;
36621
36695
  }
36622
36696
  nextPositionsToUpdate = new JetSet();
@@ -36767,7 +36841,7 @@ class Evaluator {
36767
36841
  this.setEvaluatedCell(positionId, evaluatedCell);
36768
36842
  // check if formula dependencies present in the spread zone
36769
36843
  // if so, they need to be recomputed
36770
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
36844
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
36771
36845
  };
36772
36846
  }
36773
36847
  invalidateSpreading(positionId) {
@@ -36782,8 +36856,8 @@ class Evaluator {
36782
36856
  continue;
36783
36857
  }
36784
36858
  this.evaluatedCells.delete(child);
36785
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
36786
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
36859
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
36860
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
36787
36861
  }
36788
36862
  this.spreadingRelations.removeNode(positionId);
36789
36863
  }
@@ -37143,7 +37217,13 @@ class EvaluationPlugin extends UIPlugin {
37143
37217
  const format = newFormat
37144
37218
  ? getItemId(newFormat, data.formats)
37145
37219
  : exportedCellData.format;
37146
- const content = !isExported ? newContent : exportedCellData.content;
37220
+ let content;
37221
+ if (formulaCell instanceof FormulaCellWithDependencies) {
37222
+ content = this.getters.buildFormulaContent(exportedSheetData.id, formulaCell, formulaCell.dependencies, true);
37223
+ }
37224
+ else {
37225
+ content = !isExported ? newContent : exportedCellData.content;
37226
+ }
37147
37227
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
37148
37228
  }
37149
37229
  }
@@ -43662,7 +43742,7 @@ class EditionPlugin extends UIPlugin {
43662
43742
  this.replaceText(ref, this.selectionInitialStart, this.selectionEnd);
43663
43743
  }
43664
43744
  getZoneReference(zone) {
43665
- const inputSheetId = this.getters.getCurrentEditedCell().sheetId;
43745
+ const inputSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43666
43746
  const sheetId = this.getters.getActiveSheetId();
43667
43747
  const range = this.getters.getRangeFromZone(sheetId, zone);
43668
43748
  return this.getters.getSelectionRangeString(range, inputSheetId);
@@ -43676,7 +43756,7 @@ class EditionPlugin extends UIPlugin {
43676
43756
  _fixedParts.pop();
43677
43757
  }
43678
43758
  const newRange = range.clone({ parts: _fixedParts });
43679
- return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell().sheetId);
43759
+ return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId());
43680
43760
  }
43681
43761
  /**
43682
43762
  * Replace the current selection by a new text.
@@ -43709,7 +43789,7 @@ class EditionPlugin extends UIPlugin {
43709
43789
  if (!this.currentContent.startsWith("=") || this.mode === "inactive") {
43710
43790
  return;
43711
43791
  }
43712
- const editionSheetId = this.getters.getCurrentEditedCell().sheetId;
43792
+ const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43713
43793
  const XCs = this.getReferencedRanges().map((range) => this.getters.getRangeString(range, editionSheetId));
43714
43794
  const colorsToKeep = {};
43715
43795
  for (const xc of XCs) {
@@ -43738,7 +43818,7 @@ class EditionPlugin extends UIPlugin {
43738
43818
  if (!this.currentContent.startsWith("=") || this.mode === "inactive") {
43739
43819
  return [];
43740
43820
  }
43741
- const editionSheetId = this.getters.getCurrentEditedCell().sheetId;
43821
+ const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43742
43822
  const rangeColor = (rangeString) => {
43743
43823
  const colorIndex = this.colorIndexByRange[rangeString];
43744
43824
  return colors$1[colorIndex % colors$1.length];
@@ -43756,7 +43836,7 @@ class EditionPlugin extends UIPlugin {
43756
43836
  * Return ranges currently referenced in the composer
43757
43837
  */
43758
43838
  getReferencedRanges() {
43759
- const editionSheetId = this.getters.getCurrentEditedCell().sheetId;
43839
+ const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43760
43840
  const referenceRanges = this.currentTokens
43761
43841
  .filter((token) => token.type === "REFERENCE")
43762
43842
  .map((token) => this.getters.getRangeFromSheetXC(editionSheetId, token.value));
@@ -45720,6 +45800,16 @@ class ImageProvider {
45720
45800
  }
45721
45801
  }
45722
45802
 
45803
+ class FocusableElement {
45804
+ focusableElement = undefined;
45805
+ setFocusableElement(element) {
45806
+ this.focusableElement = element;
45807
+ }
45808
+ focus() {
45809
+ this.focusableElement?.focus();
45810
+ }
45811
+ }
45812
+
45723
45813
  const RIPPLE_KEY_FRAMES = [
45724
45814
  { transform: "scale(0)" },
45725
45815
  { transform: "scale(0.8)", offset: 0.33 },
@@ -47762,6 +47852,7 @@ class Spreadsheet extends Component {
47762
47852
  _t: Spreadsheet._t,
47763
47853
  clipboard: this.env.clipboard || instantiateClipboard(),
47764
47854
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
47855
+ focusableElement: new FocusableElement(),
47765
47856
  });
47766
47857
  useExternalListener(window, "resize", () => this.render(true));
47767
47858
  useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -47868,17 +47959,18 @@ class Spreadsheet extends Component {
47868
47959
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
47869
47960
  this.composer.topBarFocus = "contentFocus";
47870
47961
  this.composer.gridFocusMode = "inactive";
47871
- this.setComposerContent({ selection } || {});
47962
+ this.setComposerContent({ selection });
47872
47963
  }
47873
- onGridComposerContentFocused() {
47964
+ onGridComposerContentFocused(selection) {
47874
47965
  if (this.model.getters.isReadonly()) {
47875
47966
  return;
47876
47967
  }
47877
47968
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
47878
47969
  this.composer.topBarFocus = "inactive";
47879
47970
  this.composer.gridFocusMode = "contentFocus";
47880
- this.setComposerContent({});
47971
+ this.setComposerContent({ selection });
47881
47972
  }
47973
+ // TODO: either both are defined or none of them. change those args to an object
47882
47974
  onGridComposerCellFocused(content, selection) {
47883
47975
  if (this.model.getters.isReadonly()) {
47884
47976
  return;
@@ -49290,11 +49382,10 @@ class SelectionStreamProcessorImpl {
49290
49382
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
49291
49383
  */
49292
49384
  getEndOfCluster(startPosition, dim, dir) {
49293
- const sheet = this.getters.getActiveSheet();
49294
49385
  let currentPosition = startPosition;
49295
49386
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
49296
49387
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
49297
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
49388
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
49298
49389
  ? "endOfCluster"
49299
49390
  : "nextCluster";
49300
49391
  while (true) {
@@ -49304,7 +49395,7 @@ class SelectionStreamProcessorImpl {
49304
49395
  currentPosition.row === nextCellPosition.row) {
49305
49396
  break;
49306
49397
  }
49307
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
49398
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
49308
49399
  if (mode === "endOfCluster" && isNextCellEmpty) {
49309
49400
  break;
49310
49401
  }
@@ -49318,10 +49409,20 @@ class SelectionStreamProcessorImpl {
49318
49409
  return dim === "cols" ? currentPosition.col : currentPosition.row;
49319
49410
  }
49320
49411
  /**
49321
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
49322
- * check if the merge containing the cell is empty.
49412
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
49413
+ * the check applies to the main cell of the merge.
49323
49414
  */
49324
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
49415
+ isCellEmpty({ col, row }) {
49416
+ const sheetId = this.getters.getActiveSheetId();
49417
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
49418
+ return !(this.getters.getCorrespondingFormulaCell(position) || this.getters.getCell(position)?.content);
49419
+ }
49420
+ /**
49421
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
49422
+ * the check applies to the main cell of the merge.
49423
+ */
49424
+ isEvaluatedCellEmpty({ col, row }) {
49425
+ const sheetId = this.getters.getActiveSheetId();
49325
49426
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
49326
49427
  const cell = this.getters.getEvaluatedCell(position);
49327
49428
  return cell.type === CellValueType.empty;
@@ -49799,12 +49900,7 @@ function addFormula(cell) {
49799
49900
  attrs.push(["t", "str"]);
49800
49901
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
49801
49902
  }
49802
- node = escapeXml /*xml*/ `
49803
- <f>
49804
- ${XlsxFormula}
49805
- </f>
49806
- ${cycle}
49807
- `;
49903
+ node = escapeXml /*xml*/ ` <f> ${XlsxFormula} </f> ${cycle} `;
49808
49904
  return { attrs, node };
49809
49905
  }
49810
49906
  function addContent(content, sharedStrings, forceString = false) {
@@ -50623,11 +50719,10 @@ function addRows(construct, data, sheet) {
50623
50719
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
50624
50720
  }
50625
50721
  attributes.push(...additionalAttrs);
50626
- cellNodes.push(escapeXml /*xml*/ `
50627
- <c ${formatAttributes(attributes)}>
50628
- ${cellNode}
50629
- </c>
50630
- `);
50722
+ // prettier-ignore
50723
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
50724
+ ${cellNode}
50725
+ </c>`);
50631
50726
  }
50632
50727
  }
50633
50728
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -51576,6 +51671,6 @@ const constants = {
51576
51671
  export { AbstractChart, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
51577
51672
 
51578
51673
 
51579
- __info__.version = '16.4.20';
51580
- __info__.date = '2024-01-29T13:50:12.345Z';
51581
- __info__.hash = '06c0ecf';
51674
+ __info__.version = '16.4.23';
51675
+ __info__.date = '2024-02-16T14:23:29.521Z';
51676
+ __info__.hash = 'cb5db1a';