@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.
@@ -705,18 +705,18 @@ function isConsecutive(iterable) {
705
705
  return true;
706
706
  }
707
707
  class JetSet extends Set {
708
- add(...iterable) {
708
+ addMany(iterable) {
709
709
  for (const element of iterable) {
710
710
  super.add(element);
711
711
  }
712
712
  return this;
713
713
  }
714
- delete(...iterable) {
715
- let deleted = false;
714
+ deleteMany(iterable) {
715
+ let wasDeleted = false;
716
716
  for (const element of iterable) {
717
- deleted ||= super.delete(element);
717
+ wasDeleted ||= super.delete(element);
718
718
  }
719
- return deleted;
719
+ return wasDeleted;
720
720
  }
721
721
  }
722
722
  /**
@@ -1842,6 +1842,9 @@ function parseFormat(formatString) {
1842
1842
  function formatValue(value, { format, locale }) {
1843
1843
  switch (typeof value) {
1844
1844
  case "string":
1845
+ if (value.includes('\\"')) {
1846
+ return value.replace(/\\"/g, '"');
1847
+ }
1845
1848
  return value;
1846
1849
  case "boolean":
1847
1850
  return value ? "TRUE" : "FALSE";
@@ -15239,6 +15242,9 @@ function isValidLocale(locale) {
15239
15242
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
15240
15243
  return false;
15241
15244
  }
15245
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
15246
+ return false;
15247
+ }
15242
15248
  try {
15243
15249
  formatValue(1, { locale, format: "#,##0.00" });
15244
15250
  formatValue(1, { locale, format: locale.dateFormat });
@@ -15329,7 +15335,7 @@ function canonicalizeNumberLiteral(content, locale) {
15329
15335
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
15330
15336
  return content;
15331
15337
  }
15332
- return content.replace(locale.decimalSeparator, ".");
15338
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
15333
15339
  }
15334
15340
  /**
15335
15341
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -15975,14 +15981,8 @@ function createDataSets(getters, dataSetsString, sheetId, dataSetsHaveTitle) {
15975
15981
  : undefined));
15976
15982
  }
15977
15983
  }
15978
- else if (zone.left === zone.right && zone.top === zone.bottom) {
15979
- // A single cell. If it's only the title, the dataset is not added.
15980
- if (!dataSetsHaveTitle) {
15981
- dataSets.push(createDataSet(getters, dataSetSheetId, zone, undefined));
15982
- }
15983
- }
15984
15984
  else {
15985
- /* 1 row or 1 column */
15985
+ /* 1 cell, 1 row or 1 column */
15986
15986
  dataSets.push(createDataSet(getters, dataSetSheetId, zone, dataSetsHaveTitle
15987
15987
  ? {
15988
15988
  top: zone.top,
@@ -16031,8 +16031,10 @@ function toExcelDataset(getters, ds) {
16031
16031
  }
16032
16032
  const dataRange = ds.dataRange.clone({ zone: dataZone });
16033
16033
  return {
16034
- label: ds.labelCell ? getters.getRangeString(ds.labelCell, "forceSheetReference") : undefined,
16035
- range: getters.getRangeString(dataRange, "forceSheetReference"),
16034
+ label: ds.labelCell
16035
+ ? getters.getRangeString(ds.labelCell, "forceSheetReference", true)
16036
+ : undefined,
16037
+ range: getters.getRangeString(dataRange, "forceSheetReference", true),
16036
16038
  };
16037
16039
  }
16038
16040
  function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
@@ -16045,7 +16047,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
16045
16047
  zone.top = zone.top + 1;
16046
16048
  }
16047
16049
  const range = labelRange.clone({ zone });
16048
- return getters.getRangeString(range, "forceSheetReference");
16050
+ return getters.getRangeString(range, "forceSheetReference", true);
16049
16051
  }
16050
16052
  /**
16051
16053
  * Transform a chart definition which supports dataSets (dataSets and LabelRange)
@@ -16582,7 +16584,7 @@ class BarChart extends AbstractChart {
16582
16584
  return undefined;
16583
16585
  const dataSets = this.dataSets
16584
16586
  .map((ds) => toExcelDataset(this.getters, ds))
16585
- .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
16587
+ .filter((ds) => ds.range !== "" && ds.range !== INCORRECT_RANGE_STRING);
16586
16588
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
16587
16589
  return {
16588
16590
  ...this.getDefinition(),
@@ -17170,7 +17172,7 @@ class LineChart extends AbstractChart {
17170
17172
  return undefined;
17171
17173
  const dataSets = this.dataSets
17172
17174
  .map((ds) => toExcelDataset(this.getters, ds))
17173
- .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
17175
+ .filter((ds) => ds.range !== "" && ds.range !== INCORRECT_RANGE_STRING);
17174
17176
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
17175
17177
  return {
17176
17178
  ...this.getDefinition(),
@@ -17457,7 +17459,7 @@ class PieChart extends AbstractChart {
17457
17459
  return undefined;
17458
17460
  const dataSets = this.dataSets
17459
17461
  .map((ds) => toExcelDataset(this.getters, ds))
17460
- .filter((ds) => ds.range !== ""); // && range !== INCORRECT_RANGE_STRING ? show incorrect #ref ?
17462
+ .filter((ds) => ds.range !== "" && ds.range !== INCORRECT_RANGE_STRING);
17461
17463
  const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
17462
17464
  return {
17463
17465
  ...this.getDefinition(),
@@ -18635,6 +18637,12 @@ const INSERT_LINK = (env) => {
18635
18637
  let { col, row } = env.model.getters.getActivePosition();
18636
18638
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
18637
18639
  };
18640
+ const INSERT_LINK_NAME = (env) => {
18641
+ const sheetId = env.model.getters.getActiveSheetId();
18642
+ const { col, row } = env.model.getters.getActivePosition();
18643
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
18644
+ return cell && cell.link ? _lt("Edit link") : _lt("Insert link");
18645
+ };
18638
18646
  //------------------------------------------------------------------------------
18639
18647
  // Filters action
18640
18648
  //------------------------------------------------------------------------------
@@ -19175,7 +19183,7 @@ cellMenuRegistry
19175
19183
  })
19176
19184
  .add("insert_link", {
19177
19185
  ...insertLink,
19178
- name: _lt("Insert link"),
19186
+ name: INSERT_LINK_NAME,
19179
19187
  sequence: 150,
19180
19188
  separator: true,
19181
19189
  });
@@ -24086,6 +24094,7 @@ class Composer extends owl.Component {
24086
24094
  static components = { TextValueProvider, FunctionDescriptionProvider };
24087
24095
  static defaultProps = {
24088
24096
  inputStyle: "",
24097
+ isDefaultFocus: false,
24089
24098
  };
24090
24099
  composerRef = owl.useRef("o_composer");
24091
24100
  contentHelper = new ContentEditableHelper(this.composerRef.el);
@@ -24138,7 +24147,6 @@ class Composer extends owl.Component {
24138
24147
  F2: () => console.warn("Not implemented"),
24139
24148
  F4: this.processF4Key,
24140
24149
  Tab: (ev) => this.processTabKey(ev),
24141
- " ": (ev) => this.processSpaceKey(ev),
24142
24150
  };
24143
24151
  keyCodeMapping = {
24144
24152
  NumpadDecimal: this.processNumpadDecimal,
@@ -24146,11 +24154,11 @@ class Composer extends owl.Component {
24146
24154
  setup() {
24147
24155
  owl.onMounted(() => {
24148
24156
  const el = this.composerRef.el;
24157
+ if (this.props.isDefaultFocus) {
24158
+ this.env.focusableElement.setFocusableElement(el);
24159
+ }
24149
24160
  this.contentHelper.updateEl(el);
24150
24161
  });
24151
- owl.onWillUnmount(() => {
24152
- this.props.onComposerUnmounted?.();
24153
- });
24154
24162
  owl.useEffect(() => {
24155
24163
  this.processContent();
24156
24164
  });
@@ -24159,7 +24167,8 @@ class Composer extends owl.Component {
24159
24167
  // Handlers
24160
24168
  // ---------------------------------------------------------------------------
24161
24169
  processArrowKeys(ev) {
24162
- if (this.env.model.getters.isSelectingForComposer()) {
24170
+ if (this.env.model.getters.isSelectingForComposer() ||
24171
+ this.env.model.getters.getEditionMode() === "inactive") {
24163
24172
  this.functionDescriptionState.showDescription = false;
24164
24173
  // Prevent the default content editable behavior which moves the cursor
24165
24174
  ev.preventDefault();
@@ -24198,25 +24207,19 @@ class Composer extends owl.Component {
24198
24207
  processTabKey(ev) {
24199
24208
  ev.preventDefault();
24200
24209
  ev.stopPropagation();
24201
- if (this.autoCompleteState.showProvider) {
24202
- const autoCompleteValue = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]?.text;
24203
- if (autoCompleteValue) {
24204
- this.autoComplete(autoCompleteValue);
24205
- return;
24210
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
24211
+ if (this.autoCompleteState.showProvider) {
24212
+ const autoCompleteValue = this.autoCompleteState.values[this.autoCompleteState.selectedIndex]?.text;
24213
+ if (autoCompleteValue) {
24214
+ this.autoComplete(autoCompleteValue);
24215
+ return;
24216
+ }
24206
24217
  }
24218
+ this.env.model.dispatch("STOP_EDITION");
24207
24219
  }
24208
24220
  const direction = ev.shiftKey ? "left" : "right";
24209
- this.env.model.dispatch("STOP_EDITION");
24210
24221
  this.env.model.selection.moveAnchorCell(direction, 1);
24211
24222
  }
24212
- processSpaceKey(ev) {
24213
- if (ev.ctrlKey) {
24214
- ev.preventDefault();
24215
- ev.stopPropagation();
24216
- this.showAutocomplete("");
24217
- this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
24218
- }
24219
- }
24220
24223
  processEnterKey(ev) {
24221
24224
  ev.preventDefault();
24222
24225
  ev.stopPropagation();
@@ -24268,6 +24271,9 @@ class Composer extends owl.Component {
24268
24271
  this.compositionActive = false;
24269
24272
  }
24270
24273
  onKeydown(ev) {
24274
+ if (this.env.model.getters.getEditionMode() === "inactive") {
24275
+ return;
24276
+ }
24271
24277
  let handler = this.keyMapping[ev.key] || this.keyCodeMapping[ev.code];
24272
24278
  if (handler) {
24273
24279
  handler.call(this, ev);
@@ -24276,14 +24282,33 @@ class Composer extends owl.Component {
24276
24282
  ev.stopPropagation();
24277
24283
  }
24278
24284
  }
24285
+ onPaste(ev) {
24286
+ if (this.env.model.getters.getEditionMode() !== "inactive") {
24287
+ ev.stopPropagation();
24288
+ }
24289
+ }
24279
24290
  /*
24280
24291
  * Triggered automatically by the content-editable between the keydown and key up
24281
24292
  * */
24282
24293
  onInput(ev) {
24283
- if (this.props.focus === "inactive" || !this.shouldProcessInputEvents) {
24294
+ if (!this.shouldProcessInputEvents) {
24295
+ return;
24296
+ }
24297
+ if (ev.inputType === "insertFromPaste" &&
24298
+ this.env.model.getters.getEditionMode() === "inactive") {
24284
24299
  return;
24285
24300
  }
24286
- let content = this.contentHelper.getText();
24301
+ ev.stopPropagation();
24302
+ let content;
24303
+ if (this.env.model.getters.getEditionMode() === "inactive") {
24304
+ content = ev.data || "";
24305
+ }
24306
+ else {
24307
+ content = this.contentHelper.getText();
24308
+ }
24309
+ if (this.props.focus === "inactive") {
24310
+ return this.props.onComposerCellFocused?.(content);
24311
+ }
24287
24312
  let selection = this.contentHelper.getCurrentSelection();
24288
24313
  if (ev.inputType === "insertParagraph") {
24289
24314
  const start = Math.min(selection.start, selection.end);
@@ -24361,9 +24386,8 @@ class Composer extends owl.Component {
24361
24386
  }
24362
24387
  const newSelection = this.contentHelper.getCurrentSelection();
24363
24388
  this.env.model.dispatch("STOP_COMPOSER_RANGE_SELECTION");
24364
- if (this.props.focus === "inactive") {
24365
- this.props.onComposerContentFocused(newSelection);
24366
- }
24389
+ this.props.onComposerContentFocused();
24390
+ if (this.props.focus === "inactive") ;
24367
24391
  this.env.model.dispatch("CHANGE_COMPOSER_CURSOR_SELECTION", newSelection);
24368
24392
  this.processTokenAtCursor();
24369
24393
  }
@@ -24530,7 +24554,7 @@ class Composer extends owl.Component {
24530
24554
  const highlights = this.env.model.getters.getHighlights();
24531
24555
  const refSheet = sheetName
24532
24556
  ? this.env.model.getters.getSheetIdByName(sheetName)
24533
- : this.env.model.getters.getCurrentEditedCell().sheetId;
24557
+ : this.env.model.getters.getCurrentEditedCell()?.sheetId;
24534
24558
  const highlight = highlights.find((highlight) => {
24535
24559
  if (highlight.sheetId !== refSheet)
24536
24560
  return false;
@@ -24610,12 +24634,13 @@ class Composer extends owl.Component {
24610
24634
  }
24611
24635
  }
24612
24636
  Composer.props = {
24613
- focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
24614
- onComposerContentFocused: Function,
24615
24637
  inputStyle: { type: String, optional: true },
24616
24638
  rect: { type: Object, optional: true },
24617
24639
  delimitation: { type: Object, optional: true },
24618
- onComposerUnmounted: { type: Function, optional: true },
24640
+ focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
24641
+ onComposerCellFocused: { type: Function, optional: true },
24642
+ onComposerContentFocused: Function,
24643
+ isDefaultFocus: { type: Boolean, optional: true },
24619
24644
  };
24620
24645
 
24621
24646
  const COMPOSER_BORDER_WIDTH = 3 * 0.4 * window.devicePixelRatio || 1;
@@ -24649,51 +24674,26 @@ css /* scss */ `
24649
24674
  class GridComposer extends owl.Component {
24650
24675
  static template = "o-spreadsheet-GridComposer";
24651
24676
  static components = { Composer };
24652
- gridComposerRef;
24653
- zone;
24654
- rect;
24677
+ // TODORAR see if we can keep it undefined
24678
+ rect = this.defaultRect;
24679
+ isEditing = false;
24655
24680
  isCellReferenceVisible;
24656
- composerState;
24681
+ get defaultRect() {
24682
+ return { x: 0, y: 0, width: 0, height: 0 };
24683
+ }
24657
24684
  setup() {
24658
- this.gridComposerRef = owl.useRef("gridComposer");
24659
- this.composerState = owl.useState({
24660
- rect: undefined,
24661
- delimitation: undefined,
24662
- });
24663
- const { sheetId, col, row } = this.env.model.getters.getActivePosition();
24664
- this.zone = this.env.model.getters.expandZone(sheetId, positionToZone({ col, row }));
24665
- this.rect = this.env.model.getters.getVisibleRect(this.zone);
24666
- this.isCellReferenceVisible = false;
24667
- owl.onMounted(() => {
24668
- const el = this.gridComposerRef.el;
24669
- this.composerState.rect = {
24670
- x: this.rect.x,
24671
- y: this.rect.y,
24672
- width: el.clientWidth,
24673
- height: el.clientHeight,
24674
- };
24675
- this.composerState.delimitation = {
24676
- width: this.props.gridDims.width,
24677
- height: this.props.gridDims.height,
24678
- };
24679
- });
24680
24685
  owl.onWillUpdateProps(() => {
24681
- if (this.isCellReferenceVisible) {
24682
- return;
24683
- }
24684
- const sheetId = this.env.model.getters.getActiveSheetId();
24685
- const zone = this.env.model.getters.getSelectedZone();
24686
- const rect = this.env.model.getters.getVisibleRect(zone);
24687
- if (!deepEquals(rect, this.rect) ||
24688
- sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
24689
- this.isCellReferenceVisible = true;
24690
- }
24686
+ this.updateComponentPosition();
24687
+ this.updateCellReferenceVisibility();
24691
24688
  });
24692
24689
  }
24693
24690
  get shouldDisplayCellReference() {
24694
24691
  return this.isCellReferenceVisible;
24695
24692
  }
24696
24693
  get cellReference() {
24694
+ if (!this.env.model.getters.getCurrentEditedCell()) {
24695
+ return "";
24696
+ }
24697
24697
  const { col, row, sheetId } = this.env.model.getters.getCurrentEditedCell();
24698
24698
  const prefixSheet = sheetId !== this.env.model.getters.getActiveSheetId();
24699
24699
  return `${prefixSheet ? getCanonicalSheetName(this.env.model.getters.getSheetName(sheetId)) + "!" : ""}${toXC(col, row)}`;
@@ -24705,7 +24705,27 @@ class GridComposer extends owl.Component {
24705
24705
  top: `${top - GRID_CELL_REFERENCE_TOP_OFFSET}px`,
24706
24706
  });
24707
24707
  }
24708
+ get composerProps() {
24709
+ const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
24710
+ return {
24711
+ rect: { ...this.rect },
24712
+ delimitation: {
24713
+ width,
24714
+ height,
24715
+ },
24716
+ focus: this.props.focus,
24717
+ isDefaultFocus: true,
24718
+ onComposerContentFocused: this.props.onComposerContentFocused,
24719
+ onComposerCellFocused: this.props.onComposerCellFocused,
24720
+ };
24721
+ }
24708
24722
  get containerStyle() {
24723
+ if (this.env.model.getters.getEditionMode() === "inactive" || !this.rect) {
24724
+ return `
24725
+ position: absolute;
24726
+ z-index: -1000;
24727
+ `;
24728
+ }
24709
24729
  const isFormula = this.env.model.getters.getCurrentContent().startsWith("=");
24710
24730
  const cell = this.env.model.getters.getActiveCell();
24711
24731
  const position = this.env.model.getters.getActivePosition();
@@ -24725,6 +24745,8 @@ class GridComposer extends owl.Component {
24725
24745
  if (!isFormula) {
24726
24746
  textAlign = style.align || cell.defaultAlign;
24727
24747
  }
24748
+ const maxHeight = this.props.gridDims.height - this.rect.y;
24749
+ const maxWidth = this.props.gridDims.width - this.rect.x;
24728
24750
  /**
24729
24751
  * min-size is on the container, not the composer element, because we want to have the same size as the cell by default,
24730
24752
  * including all the paddings/margins of the composer
@@ -24736,6 +24758,8 @@ class GridComposer extends owl.Component {
24736
24758
  top: `${top}px`,
24737
24759
  "min-width": `${width + 1}px`,
24738
24760
  "min-height": `${height + 1}px`,
24761
+ "max-width": `${maxWidth}px`,
24762
+ "max-height": `${maxHeight}px`,
24739
24763
  background,
24740
24764
  color,
24741
24765
  "font-size": `${fontSizeInPixels(fontSize)}px`,
@@ -24745,20 +24769,38 @@ class GridComposer extends owl.Component {
24745
24769
  "text-align": textAlign,
24746
24770
  });
24747
24771
  }
24748
- get composerStyle() {
24749
- const maxHeight = this.props.gridDims.height - this.rect.y;
24750
- const maxWidth = this.props.gridDims.width - this.rect.x;
24751
- return cssPropertiesToCss({
24752
- "max-width": `${maxWidth}px`,
24753
- "max-height": `${maxHeight}px`,
24754
- });
24772
+ updateComponentPosition() {
24773
+ const isEditing = this.env.model.getters.getEditionMode() !== "inactive";
24774
+ if (this.isEditing !== isEditing) {
24775
+ this.isEditing = isEditing;
24776
+ if (!isEditing) {
24777
+ this.rect = this.defaultRect;
24778
+ this.env.focusableElement.focus();
24779
+ return;
24780
+ }
24781
+ const position = this.env.model.getters.getActivePosition();
24782
+ const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
24783
+ this.rect = this.env.model.getters.getVisibleRect(zone);
24784
+ }
24785
+ }
24786
+ updateCellReferenceVisibility() {
24787
+ if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
24788
+ return;
24789
+ }
24790
+ const sheetId = this.env.model.getters.getActiveSheetId();
24791
+ const zone = this.env.model.getters.getSelectedZone();
24792
+ const rect = this.env.model.getters.getVisibleRect(zone);
24793
+ if (!deepEquals(rect, this.rect) ||
24794
+ sheetId !== this.env.model.getters.getCurrentEditedCell().sheetId) {
24795
+ this.isCellReferenceVisible = true;
24796
+ }
24755
24797
  }
24756
24798
  }
24757
24799
  GridComposer.props = {
24758
24800
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
24759
- onComposerUnmounted: Function,
24760
24801
  onComposerContentFocused: Function,
24761
24802
  gridDims: Object,
24803
+ onComposerCellFocused: Function,
24762
24804
  };
24763
24805
 
24764
24806
  const CSS$1 = css /* scss */ `
@@ -25639,7 +25681,6 @@ class GridOverlay extends owl.Component {
25639
25681
  this.props.onCellDoubleClicked(col, row);
25640
25682
  }
25641
25683
  onContextMenu(ev) {
25642
- ev.preventDefault();
25643
25684
  const [col, row] = this.getCartesianCoordinates(ev);
25644
25685
  this.props.onCellRightClicked(col, row, { x: ev.clientX, y: ev.clientY });
25645
25686
  }
@@ -26732,7 +26773,6 @@ class Grid extends owl.Component {
26732
26773
  HEADER_WIDTH = HEADER_WIDTH;
26733
26774
  menuState;
26734
26775
  gridRef;
26735
- hiddenInput;
26736
26776
  onMouseWheel;
26737
26777
  canvasPosition;
26738
26778
  hoveredCell;
@@ -26743,17 +26783,16 @@ class Grid extends owl.Component {
26743
26783
  menuItems: [],
26744
26784
  });
26745
26785
  this.gridRef = owl.useRef("grid");
26746
- this.hiddenInput = owl.useRef("hiddenInput");
26747
26786
  this.canvasPosition = useAbsoluteBoundingRect(this.gridRef);
26748
26787
  this.hoveredCell = owl.useState({ col: undefined, row: undefined });
26749
26788
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
26750
26789
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
26751
26790
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
26752
26791
  owl.useExternalListener(document.body, "paste", this.paste);
26753
- owl.onMounted(() => this.focus());
26754
- this.props.exposeFocus(() => this.focus());
26792
+ owl.onMounted(() => this.focusDefaultElement());
26793
+ this.props.exposeFocus(() => this.focusDefaultElement());
26755
26794
  useGridDrawing("canvas", this.env.model, () => this.env.model.getters.getSheetViewDimensionWithHeaders());
26756
- owl.useEffect(() => this.focus(), () => [this.env.model.getters.getActiveSheetId()]);
26795
+ owl.useEffect(() => this.focusDefaultElement(), () => [this.env.model.getters.getActiveSheetId()]);
26757
26796
  this.onMouseWheel = useWheelHandler((deltaX, deltaY) => {
26758
26797
  this.moveCanvas(deltaX, deltaY);
26759
26798
  this.hoveredCell.col = undefined;
@@ -26776,7 +26815,7 @@ class Grid extends owl.Component {
26776
26815
  if (this.env.model.getters.hasOpenedPopover()) {
26777
26816
  this.closeOpenedPopover();
26778
26817
  }
26779
- this.focus();
26818
+ this.focusDefaultElement();
26780
26819
  }
26781
26820
  // this map will handle most of the actions that should happen on key down. The arrow keys are managed in the key
26782
26821
  // down itself
@@ -26949,10 +26988,10 @@ class Grid extends owl.Component {
26949
26988
  PAGEUP: () => this.env.model.dispatch("SHIFT_VIEWPORT_UP"),
26950
26989
  "CTRL+K": () => INSERT_LINK(this.env),
26951
26990
  };
26952
- focus() {
26991
+ focusDefaultElement() {
26953
26992
  if (!this.env.model.getters.getSelectedFigureId() &&
26954
26993
  this.env.model.getters.getEditionMode() === "inactive") {
26955
- this.hiddenInput.el?.focus();
26994
+ this.env.focusableElement.focus();
26956
26995
  }
26957
26996
  }
26958
26997
  get gridEl() {
@@ -27096,19 +27135,6 @@ class Grid extends owl.Component {
27096
27135
  return;
27097
27136
  }
27098
27137
  }
27099
- onInput(ev) {
27100
- // the user meant to paste in the sheet, not open the composer with the pasted content
27101
- if (!ev.isComposing && ev.inputType === "insertFromPaste") {
27102
- return;
27103
- }
27104
- if (ev.data) {
27105
- // if the user types a character on the grid, it means he wants to start composing the selected cell with that
27106
- // character
27107
- ev.preventDefault();
27108
- ev.stopPropagation();
27109
- this.props.onGridComposerCellFocused(ev.data);
27110
- }
27111
- }
27112
27138
  // ---------------------------------------------------------------------------
27113
27139
  // Context Menu
27114
27140
  // ---------------------------------------------------------------------------
@@ -27221,7 +27247,7 @@ class Grid extends owl.Component {
27221
27247
  }
27222
27248
  closeMenu() {
27223
27249
  this.menuState.isOpen = false;
27224
- this.focus();
27250
+ this.focusDefaultElement();
27225
27251
  }
27226
27252
  }
27227
27253
  Grid.props = {
@@ -30881,6 +30907,31 @@ const MIGRATIONS = [
30881
30907
  return data;
30882
30908
  },
30883
30909
  },
30910
+ {
30911
+ description: "Fix datafilter duplication",
30912
+ from: 12,
30913
+ to: 12.5,
30914
+ applyMigration(data) {
30915
+ for (let sheet of data.sheets || []) {
30916
+ let knownDataFilterZones = [];
30917
+ for (let filterTable of sheet.filterTables || []) {
30918
+ const zone = toZone(filterTable.range);
30919
+ // See commit message for the details
30920
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
30921
+ if (intersectZoneIndex !== -1) {
30922
+ knownDataFilterZones[intersectZoneIndex] = zone;
30923
+ }
30924
+ else {
30925
+ knownDataFilterZones.push(zone);
30926
+ }
30927
+ }
30928
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
30929
+ range: zoneToXc(zone),
30930
+ }));
30931
+ }
30932
+ return data;
30933
+ },
30934
+ },
30884
30935
  {
30885
30936
  description: "Change Border description structure",
30886
30937
  from: 12,
@@ -31800,9 +31851,9 @@ class CellPlugin extends CorePlugin {
31800
31851
  allowDispatch(cmd) {
31801
31852
  switch (cmd.type) {
31802
31853
  case "UPDATE_CELL":
31803
- return this.checkCellOutOfSheet(cmd);
31854
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
31804
31855
  case "CLEAR_CELL":
31805
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
31856
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
31806
31857
  default:
31807
31858
  return 0 /* CommandResult.Success */;
31808
31859
  }
@@ -31968,13 +32019,13 @@ class CellPlugin extends CorePlugin {
31968
32019
  /*
31969
32020
  * Reconstructs the original formula string based on a normalized form and its dependencies
31970
32021
  */
31971
- buildFormulaContent(sheetId, cell, dependencies) {
32022
+ buildFormulaContent(sheetId, cell, dependencies, useFixedZone = false) {
31972
32023
  const ranges = dependencies || cell.dependencies;
31973
32024
  let rangeIndex = 0;
31974
32025
  return concat(cell.compiledFormula.tokens.map((token) => {
31975
32026
  if (token.type === "REFERENCE") {
31976
32027
  const range = ranges[rangeIndex++];
31977
- return this.getters.getRangeString(range, sheetId);
32028
+ return this.getters.getRangeString(range, sheetId, useFixedZone);
31978
32029
  }
31979
32030
  return token.value;
31980
32031
  }));
@@ -32096,10 +32147,7 @@ class CellPlugin extends CorePlugin {
32096
32147
  else {
32097
32148
  style = before ? before.style : undefined;
32098
32149
  }
32099
- const locale = this.getters.getLocale();
32100
- let format = ("format" in after ? after.format : before && before.format) ||
32101
- detectDateFormat(afterContent, locale) ||
32102
- detectNumberFormat(afterContent);
32150
+ const format = "format" in after ? after.format : before && before.format;
32103
32151
  /* Read the following IF as:
32104
32152
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
32105
32153
  * - the command says the new content is empty and has no border/format/style
@@ -32140,12 +32188,11 @@ class CellPlugin extends CorePlugin {
32140
32188
  }
32141
32189
  createLiteralCell(id, content, format, style) {
32142
32190
  const locale = this.getters.getLocale();
32143
- content = parseLiteral(content, locale).toString();
32144
32191
  return {
32145
32192
  id,
32146
- content,
32193
+ content: parseLiteral(content, locale).toString(),
32147
32194
  style,
32148
- format,
32195
+ format: format || detectDateFormat(content, locale) || detectNumberFormat(content),
32149
32196
  isFormula: false,
32150
32197
  };
32151
32198
  }
@@ -32206,6 +32253,18 @@ class CellPlugin extends CorePlugin {
32206
32253
  }
32207
32254
  return 0 /* CommandResult.Success */;
32208
32255
  }
32256
+ checkUselessUpdateCell(cmd) {
32257
+ const cell = this.getters.getCell(cmd);
32258
+ const hasContent = "content" in cmd || "formula" in cmd;
32259
+ const hasStyle = "style" in cmd;
32260
+ const hasFormat = "format" in cmd;
32261
+ if ((!hasContent || cell?.content === cmd.content) &&
32262
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
32263
+ (!hasFormat || cell?.format === cmd.format)) {
32264
+ return 98 /* CommandResult.NoChanges */;
32265
+ }
32266
+ return 0 /* CommandResult.Success */;
32267
+ }
32209
32268
  }
32210
32269
  class FormulaCellWithDependencies {
32211
32270
  id;
@@ -32543,7 +32602,13 @@ class ConditionalFormatPlugin extends CorePlugin {
32543
32602
  }
32544
32603
  }
32545
32604
  exportForExcel(data) {
32546
- this.export(data);
32605
+ if (data.sheets) {
32606
+ for (let sheet of data.sheets) {
32607
+ if (this.cfRules[sheet.id]) {
32608
+ sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useFixedReference: true }));
32609
+ }
32610
+ }
32611
+ }
32547
32612
  }
32548
32613
  // ---------------------------------------------------------------------------
32549
32614
  // Getters
@@ -32608,11 +32673,11 @@ class ConditionalFormatPlugin extends CorePlugin {
32608
32673
  // ---------------------------------------------------------------------------
32609
32674
  // Private
32610
32675
  // ---------------------------------------------------------------------------
32611
- mapToConditionalFormat(sheetId, cf) {
32676
+ mapToConditionalFormat(sheetId, cf, options = { useFixedReference: false }) {
32612
32677
  return {
32613
32678
  ...cf,
32614
32679
  ranges: cf.ranges.map((range) => {
32615
- return this.getters.getRangeString(range, sheetId);
32680
+ return this.getters.getRangeString(range, sheetId, options.useFixedReference);
32616
32681
  }),
32617
32682
  };
32618
32683
  }
@@ -34449,8 +34514,9 @@ class RangeAdapter {
34449
34514
  *
34450
34515
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
34451
34516
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
34517
+ * @param [useFixedZone=false] if true, the range will be returned with fixed row and column
34452
34518
  */
34453
- getRangeString(range, forSheetId) {
34519
+ getRangeString(range, forSheetId, useFixedZone = false) {
34454
34520
  if (!range) {
34455
34521
  return INCORRECT_RANGE_STRING;
34456
34522
  }
@@ -34477,7 +34543,7 @@ class RangeAdapter {
34477
34543
  if (prefixSheet && !sheetName) {
34478
34544
  return INCORRECT_RANGE_STRING;
34479
34545
  }
34480
- let rangeString = this.getRangePartString(rangeImpl, 0);
34546
+ let rangeString = this.getRangePartString(rangeImpl, 0, useFixedZone);
34481
34547
  if (rangeImpl.parts && rangeImpl.parts.length === 2) {
34482
34548
  // this if converts A2:A2 into A2 except if any part of the original range had fixed row or column (with $)
34483
34549
  if (rangeImpl.zone.top !== rangeImpl.zone.bottom ||
@@ -34487,7 +34553,7 @@ class RangeAdapter {
34487
34553
  rangeImpl.parts[1].rowFixed ||
34488
34554
  rangeImpl.parts[1].colFixed) {
34489
34555
  rangeString += ":";
34490
- rangeString += this.getRangePartString(rangeImpl, 1);
34556
+ rangeString += this.getRangePartString(rangeImpl, 1, useFixedZone);
34491
34557
  }
34492
34558
  }
34493
34559
  return `${prefixSheet ? sheetName + "!" : ""}${rangeString}`;
@@ -34528,13 +34594,13 @@ class RangeAdapter {
34528
34594
  /**
34529
34595
  * Get a Xc string that represent a part of a range
34530
34596
  */
34531
- getRangePartString(range, part) {
34597
+ getRangePartString(range, part, useFixedZone = false) {
34532
34598
  const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
34533
34599
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
34534
34600
  const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
34535
34601
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
34536
34602
  let str = "";
34537
- if (range.isFullCol) {
34603
+ if (range.isFullCol && !useFixedZone) {
34538
34604
  if (part === 0 && range.unboundedZone.hasHeader) {
34539
34605
  str = colFixed + col + rowFixed + row;
34540
34606
  }
@@ -34542,7 +34608,7 @@ class RangeAdapter {
34542
34608
  str = colFixed + col;
34543
34609
  }
34544
34610
  }
34545
- else if (range.isFullRow) {
34611
+ else if (range.isFullRow && !useFixedZone) {
34546
34612
  if (part === 0 && range.unboundedZone.hasHeader) {
34547
34613
  str = colFixed + col + rowFixed + row;
34548
34614
  }
@@ -36339,7 +36405,15 @@ class SpreadsheetRTree {
36339
36405
  if (!this.rTrees[sheetId]) {
36340
36406
  return;
36341
36407
  }
36342
- this.rTrees[sheetId].remove(item, deepEquals);
36408
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
36409
+ }
36410
+ rtreeItemComparer(left, right) {
36411
+ return (left.data == right.data &&
36412
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
36413
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
36414
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
36415
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
36416
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
36343
36417
  }
36344
36418
  }
36345
36419
  /**
@@ -36417,7 +36491,7 @@ class FormulaDependencyGraph {
36417
36491
  const queue = Array.from(ranges).reverse();
36418
36492
  while (queue.length > 0) {
36419
36493
  const range = queue.pop();
36420
- visited.add(...this.encoder.encodeBoundingBox(range));
36494
+ visited.addMany(this.encoder.encodeBoundingBox(range));
36421
36495
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
36422
36496
  for (const positionId of impactedPositionIds) {
36423
36497
  if (!visited.has(positionId)) {
@@ -36425,7 +36499,7 @@ class FormulaDependencyGraph {
36425
36499
  }
36426
36500
  }
36427
36501
  }
36428
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
36502
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
36429
36503
  return visited;
36430
36504
  }
36431
36505
  }
@@ -36556,9 +36630,9 @@ class Evaluator {
36556
36630
  const cells = positions.map((p) => this.encoder.encode(p));
36557
36631
  const cellsToCompute = new JetSet(cells);
36558
36632
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
36559
- cellsToCompute.add(...this.getCellsDependingOn(cells));
36560
- cellsToCompute.add(...arrayFormulasPositionIds);
36561
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
36633
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
36634
+ cellsToCompute.addMany(arrayFormulasPositionIds);
36635
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
36562
36636
  this.evaluate(cellsToCompute);
36563
36637
  }
36564
36638
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -36572,7 +36646,7 @@ class Evaluator {
36572
36646
  }
36573
36647
  if (!content) {
36574
36648
  // The previous content could have blocked some array formulas
36575
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
36649
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
36576
36650
  }
36577
36651
  }
36578
36652
  return impactedPositionIds;
@@ -36620,7 +36694,7 @@ class Evaluator {
36620
36694
  }
36621
36695
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
36622
36696
  const cells = new JetSet(arrayFormulas);
36623
- cells.add(...this.getCellsDependingOn(arrayFormulas));
36697
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
36624
36698
  return cells;
36625
36699
  }
36626
36700
  nextPositionsToUpdate = new JetSet();
@@ -36771,7 +36845,7 @@ class Evaluator {
36771
36845
  this.setEvaluatedCell(positionId, evaluatedCell);
36772
36846
  // check if formula dependencies present in the spread zone
36773
36847
  // if so, they need to be recomputed
36774
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
36848
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
36775
36849
  };
36776
36850
  }
36777
36851
  invalidateSpreading(positionId) {
@@ -36786,8 +36860,8 @@ class Evaluator {
36786
36860
  continue;
36787
36861
  }
36788
36862
  this.evaluatedCells.delete(child);
36789
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
36790
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
36863
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
36864
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
36791
36865
  }
36792
36866
  this.spreadingRelations.removeNode(positionId);
36793
36867
  }
@@ -37147,7 +37221,13 @@ class EvaluationPlugin extends UIPlugin {
37147
37221
  const format = newFormat
37148
37222
  ? getItemId(newFormat, data.formats)
37149
37223
  : exportedCellData.format;
37150
- const content = !isExported ? newContent : exportedCellData.content;
37224
+ let content;
37225
+ if (formulaCell instanceof FormulaCellWithDependencies) {
37226
+ content = this.getters.buildFormulaContent(exportedSheetData.id, formulaCell, formulaCell.dependencies, true);
37227
+ }
37228
+ else {
37229
+ content = !isExported ? newContent : exportedCellData.content;
37230
+ }
37151
37231
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
37152
37232
  }
37153
37233
  }
@@ -43666,7 +43746,7 @@ class EditionPlugin extends UIPlugin {
43666
43746
  this.replaceText(ref, this.selectionInitialStart, this.selectionEnd);
43667
43747
  }
43668
43748
  getZoneReference(zone) {
43669
- const inputSheetId = this.getters.getCurrentEditedCell().sheetId;
43749
+ const inputSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43670
43750
  const sheetId = this.getters.getActiveSheetId();
43671
43751
  const range = this.getters.getRangeFromZone(sheetId, zone);
43672
43752
  return this.getters.getSelectionRangeString(range, inputSheetId);
@@ -43680,7 +43760,7 @@ class EditionPlugin extends UIPlugin {
43680
43760
  _fixedParts.pop();
43681
43761
  }
43682
43762
  const newRange = range.clone({ parts: _fixedParts });
43683
- return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell().sheetId);
43763
+ return this.getters.getSelectionRangeString(newRange, this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId());
43684
43764
  }
43685
43765
  /**
43686
43766
  * Replace the current selection by a new text.
@@ -43713,7 +43793,7 @@ class EditionPlugin extends UIPlugin {
43713
43793
  if (!this.currentContent.startsWith("=") || this.mode === "inactive") {
43714
43794
  return;
43715
43795
  }
43716
- const editionSheetId = this.getters.getCurrentEditedCell().sheetId;
43796
+ const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43717
43797
  const XCs = this.getReferencedRanges().map((range) => this.getters.getRangeString(range, editionSheetId));
43718
43798
  const colorsToKeep = {};
43719
43799
  for (const xc of XCs) {
@@ -43742,7 +43822,7 @@ class EditionPlugin extends UIPlugin {
43742
43822
  if (!this.currentContent.startsWith("=") || this.mode === "inactive") {
43743
43823
  return [];
43744
43824
  }
43745
- const editionSheetId = this.getters.getCurrentEditedCell().sheetId;
43825
+ const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43746
43826
  const rangeColor = (rangeString) => {
43747
43827
  const colorIndex = this.colorIndexByRange[rangeString];
43748
43828
  return colors$1[colorIndex % colors$1.length];
@@ -43760,7 +43840,7 @@ class EditionPlugin extends UIPlugin {
43760
43840
  * Return ranges currently referenced in the composer
43761
43841
  */
43762
43842
  getReferencedRanges() {
43763
- const editionSheetId = this.getters.getCurrentEditedCell().sheetId;
43843
+ const editionSheetId = this.getters.getCurrentEditedCell()?.sheetId || this.getters.getActiveSheetId();
43764
43844
  const referenceRanges = this.currentTokens
43765
43845
  .filter((token) => token.type === "REFERENCE")
43766
43846
  .map((token) => this.getters.getRangeFromSheetXC(editionSheetId, token.value));
@@ -45724,6 +45804,16 @@ class ImageProvider {
45724
45804
  }
45725
45805
  }
45726
45806
 
45807
+ class FocusableElement {
45808
+ focusableElement = undefined;
45809
+ setFocusableElement(element) {
45810
+ this.focusableElement = element;
45811
+ }
45812
+ focus() {
45813
+ this.focusableElement?.focus();
45814
+ }
45815
+ }
45816
+
45727
45817
  const RIPPLE_KEY_FRAMES = [
45728
45818
  { transform: "scale(0)" },
45729
45819
  { transform: "scale(0.8)", offset: 0.33 },
@@ -47766,6 +47856,7 @@ class Spreadsheet extends owl.Component {
47766
47856
  _t: Spreadsheet._t,
47767
47857
  clipboard: this.env.clipboard || instantiateClipboard(),
47768
47858
  startCellEdition: (content) => this.onGridComposerCellFocused(content),
47859
+ focusableElement: new FocusableElement(),
47769
47860
  });
47770
47861
  owl.useExternalListener(window, "resize", () => this.render(true));
47771
47862
  owl.useExternalListener(window, "beforeunload", this.unbindModelEvents.bind(this));
@@ -47872,17 +47963,18 @@ class Spreadsheet extends owl.Component {
47872
47963
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
47873
47964
  this.composer.topBarFocus = "contentFocus";
47874
47965
  this.composer.gridFocusMode = "inactive";
47875
- this.setComposerContent({ selection } || {});
47966
+ this.setComposerContent({ selection });
47876
47967
  }
47877
- onGridComposerContentFocused() {
47968
+ onGridComposerContentFocused(selection) {
47878
47969
  if (this.model.getters.isReadonly()) {
47879
47970
  return;
47880
47971
  }
47881
47972
  this.model.dispatch("UNFOCUS_SELECTION_INPUT");
47882
47973
  this.composer.topBarFocus = "inactive";
47883
47974
  this.composer.gridFocusMode = "contentFocus";
47884
- this.setComposerContent({});
47975
+ this.setComposerContent({ selection });
47885
47976
  }
47977
+ // TODO: either both are defined or none of them. change those args to an object
47886
47978
  onGridComposerCellFocused(content, selection) {
47887
47979
  if (this.model.getters.isReadonly()) {
47888
47980
  return;
@@ -49294,11 +49386,10 @@ class SelectionStreamProcessorImpl {
49294
49386
  * next cluster if the given cell is outside a cluster or at the border of a cluster in the given direction.
49295
49387
  */
49296
49388
  getEndOfCluster(startPosition, dim, dir) {
49297
- const sheet = this.getters.getActiveSheet();
49298
49389
  let currentPosition = startPosition;
49299
49390
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
49300
49391
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
49301
- let mode = !this.isCellEmpty(currentPosition, sheet.id) && !this.isCellEmpty(nextCellPosition, sheet.id)
49392
+ let mode = !this.isEvaluatedCellEmpty(currentPosition) && !this.isEvaluatedCellEmpty(nextCellPosition)
49302
49393
  ? "endOfCluster"
49303
49394
  : "nextCluster";
49304
49395
  while (true) {
@@ -49308,7 +49399,7 @@ class SelectionStreamProcessorImpl {
49308
49399
  currentPosition.row === nextCellPosition.row) {
49309
49400
  break;
49310
49401
  }
49311
- const isNextCellEmpty = this.isCellEmpty(nextCellPosition, sheet.id);
49402
+ const isNextCellEmpty = this.isEvaluatedCellEmpty(nextCellPosition);
49312
49403
  if (mode === "endOfCluster" && isNextCellEmpty) {
49313
49404
  break;
49314
49405
  }
@@ -49322,10 +49413,20 @@ class SelectionStreamProcessorImpl {
49322
49413
  return dim === "cols" ? currentPosition.col : currentPosition.row;
49323
49414
  }
49324
49415
  /**
49325
- * Check if a cell is empty or undefined in the model. If the cell is part of a merge,
49326
- * check if the merge containing the cell is empty.
49416
+ * Checks if a cell is empty (i.e. does not have a content). If the cell is part of a merge,
49417
+ * the check applies to the main cell of the merge.
49327
49418
  */
49328
- isCellEmpty({ col, row }, sheetId = this.getters.getActiveSheetId()) {
49419
+ isCellEmpty({ col, row }) {
49420
+ const sheetId = this.getters.getActiveSheetId();
49421
+ const position = this.getters.getMainCellPosition({ sheetId, col, row });
49422
+ return !(this.getters.getCorrespondingFormulaCell(position) || this.getters.getCell(position)?.content);
49423
+ }
49424
+ /**
49425
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
49426
+ * the check applies to the main cell of the merge.
49427
+ */
49428
+ isEvaluatedCellEmpty({ col, row }) {
49429
+ const sheetId = this.getters.getActiveSheetId();
49329
49430
  const position = this.getters.getMainCellPosition({ sheetId, col, row });
49330
49431
  const cell = this.getters.getEvaluatedCell(position);
49331
49432
  return cell.type === CellValueType.empty;
@@ -49803,12 +49904,7 @@ function addFormula(cell) {
49803
49904
  attrs.push(["t", "str"]);
49804
49905
  cycle = escapeXml /*xml*/ `<v>${cell.value}</v>`;
49805
49906
  }
49806
- node = escapeXml /*xml*/ `
49807
- <f>
49808
- ${XlsxFormula}
49809
- </f>
49810
- ${cycle}
49811
- `;
49907
+ node = escapeXml /*xml*/ ` <f> ${XlsxFormula} </f> ${cycle} `;
49812
49908
  return { attrs, node };
49813
49909
  }
49814
49910
  function addContent(content, sharedStrings, forceString = false) {
@@ -50627,11 +50723,10 @@ function addRows(construct, data, sheet) {
50627
50723
  ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader));
50628
50724
  }
50629
50725
  attributes.push(...additionalAttrs);
50630
- cellNodes.push(escapeXml /*xml*/ `
50631
- <c ${formatAttributes(attributes)}>
50632
- ${cellNode}
50633
- </c>
50634
- `);
50726
+ // prettier-ignore
50727
+ cellNodes.push(escapeXml /*xml*/ `<c ${formatAttributes(attributes)}>
50728
+ ${cellNode}
50729
+ </c>`);
50635
50730
  }
50636
50731
  }
50637
50732
  if (cellNodes.length || row.size !== DEFAULT_CELL_HEIGHT || row.isHidden) {
@@ -51612,6 +51707,6 @@ exports.setTranslationMethod = setTranslationMethod;
51612
51707
  exports.tokenize = tokenize;
51613
51708
 
51614
51709
 
51615
- __info__.version = '16.4.20';
51616
- __info__.date = '2024-01-29T13:50:12.345Z';
51617
- __info__.hash = '06c0ecf';
51710
+ __info__.version = '16.4.23';
51711
+ __info__.date = '2024-02-16T14:23:29.521Z';
51712
+ __info__.hash = 'cb5db1a';