@odoo/o-spreadsheet 17.1.4 → 17.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.1.4
6
- * @date 2024-02-09T13:09:13.430Z
7
- * @hash 16d3ece
5
+ * @version 17.1.7
6
+ * @date 2024-03-12T07:56:10.783Z
7
+ * @hash 3a9f334
8
8
  */
9
9
 
10
10
  'use strict';
@@ -453,8 +453,8 @@ function isObjectEmptyRecursive(argument) {
453
453
  * If the given item does not exist in the dictionary, it creates one with a new id.
454
454
  */
455
455
  function getItemId(item, itemsDic) {
456
- for (let [key, value] of Object.entries(itemsDic)) {
457
- if (deepEquals(value, item)) {
456
+ for (const key in itemsDic) {
457
+ if (deepEquals(itemsDic[key], item)) {
458
458
  return parseInt(key, 10);
459
459
  }
460
460
  }
@@ -553,11 +553,13 @@ function deepEquals(o1, o2) {
553
553
  if (typeof o1 !== "object")
554
554
  return o1 === o2;
555
555
  // Objects can have different keys if the values are undefined
556
- const keys = new Set();
557
- Object.keys(o1).forEach((key) => keys.add(key));
558
- Object.keys(o2).forEach((key) => keys.add(key));
559
- for (let key of keys) {
560
- if (typeof o1[key] !== typeof o1[key])
556
+ for (const key in o2) {
557
+ if (!(key in o1) && o2[key] !== undefined) {
558
+ return false;
559
+ }
560
+ }
561
+ for (const key in o1) {
562
+ if (typeof o1[key] !== typeof o2[key])
561
563
  return false;
562
564
  if (typeof o1[key] === "object") {
563
565
  if (!deepEquals(o1[key], o2[key]))
@@ -627,18 +629,18 @@ function isConsecutive(iterable) {
627
629
  return true;
628
630
  }
629
631
  class JetSet extends Set {
630
- add(...iterable) {
632
+ addMany(iterable) {
631
633
  for (const element of iterable) {
632
634
  super.add(element);
633
635
  }
634
636
  return this;
635
637
  }
636
- delete(...iterable) {
637
- let deleted = false;
638
+ deleteMany(iterable) {
639
+ let wasDeleted = false;
638
640
  for (const element of iterable) {
639
- deleted ||= super.delete(element);
641
+ wasDeleted ||= super.delete(element);
640
642
  }
641
- return deleted;
643
+ return wasDeleted;
642
644
  }
643
645
  }
644
646
  /**
@@ -1936,7 +1938,7 @@ class DispatchResult {
1936
1938
  * Static helper which returns a successful DispatchResult
1937
1939
  */
1938
1940
  static get Success() {
1939
- return new DispatchResult();
1941
+ return SUCCESS;
1940
1942
  }
1941
1943
  get isSuccessful() {
1942
1944
  return this.reasons.length === 0;
@@ -1949,6 +1951,7 @@ class DispatchResult {
1949
1951
  return this.reasons.includes(reason);
1950
1952
  }
1951
1953
  }
1954
+ const SUCCESS = new DispatchResult();
1952
1955
  exports.CommandResult = void 0;
1953
1956
  (function (CommandResult) {
1954
1957
  CommandResult["Success"] = "Success";
@@ -6097,8 +6100,10 @@ function getGroup(cell, cells, filter) {
6097
6100
  if (x === cell) {
6098
6101
  found = true;
6099
6102
  }
6100
- const cellValue = evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
6101
- if (filter(cellValue)) {
6103
+ const cellValue = x?.isFormula
6104
+ ? undefined
6105
+ : evaluateLiteral(x?.content, { locale: DEFAULT_LOCALE });
6106
+ if (cellValue && filter(cellValue)) {
6102
6107
  group.push(cellValue);
6103
6108
  }
6104
6109
  else {
@@ -6322,11 +6327,14 @@ function cellTextStyleToCss(style) {
6322
6327
  * Transform CSS properties into a CSS string.
6323
6328
  */
6324
6329
  function cssPropertiesToCss(attributes) {
6325
- const str = Object.entries(attributes)
6326
- .filter(([attName, attValue]) => attValue !== undefined)
6327
- .map(([attName, attValue]) => `${attName}:${attValue};`)
6328
- .join(" ");
6329
- return str;
6330
+ let styleStr = "";
6331
+ for (const attName in attributes) {
6332
+ if (!attributes[attName]) {
6333
+ continue;
6334
+ }
6335
+ styleStr += `${attName}:${attributes[attName]}; `;
6336
+ }
6337
+ return styleStr;
6330
6338
  }
6331
6339
  function getElementMargins(el) {
6332
6340
  const style = window.getComputedStyle(el);
@@ -7655,6 +7663,9 @@ function isValidLocale(locale) {
7655
7663
  if (locale.formulaArgSeparator === locale.decimalSeparator) {
7656
7664
  return false;
7657
7665
  }
7666
+ if (locale.thousandsSeparator === locale.decimalSeparator) {
7667
+ return false;
7668
+ }
7658
7669
  try {
7659
7670
  formatValue(1, { locale, format: "#,##0.00" });
7660
7671
  formatValue(1, { locale, format: locale.dateFormat });
@@ -7745,7 +7756,7 @@ function canonicalizeNumberLiteral(content, locale) {
7745
7756
  if (locale.decimalSeparator === "." || !isNumber(content, locale)) {
7746
7757
  return content;
7747
7758
  }
7748
- return content.replace(locale.decimalSeparator, ".");
7759
+ return content.replace(locale.thousandsSeparator, "").replace(locale.decimalSeparator, ".");
7749
7760
  }
7750
7761
  /**
7751
7762
  * Change a content string from the given locale to its canonical form (en_US locale). Also convert date string.
@@ -10840,6 +10851,12 @@ const INSERT_LINK = (env) => {
10840
10851
  let { col, row } = env.model.getters.getActivePosition();
10841
10852
  env.model.dispatch("OPEN_CELL_POPOVER", { col, row, popoverType: "LinkEditor" });
10842
10853
  };
10854
+ const INSERT_LINK_NAME = (env) => {
10855
+ const sheetId = env.model.getters.getActiveSheetId();
10856
+ const { col, row } = env.model.getters.getActivePosition();
10857
+ const cell = env.model.getters.getEvaluatedCell({ sheetId, col, row });
10858
+ return cell && cell.link ? _t("Edit link") : _t("Insert link");
10859
+ };
10843
10860
  //------------------------------------------------------------------------------
10844
10861
  // Filters action
10845
10862
  //------------------------------------------------------------------------------
@@ -13041,6 +13058,18 @@ const TRUNC = {
13041
13058
  },
13042
13059
  isExported: true,
13043
13060
  };
13061
+ // -----------------------------------------------------------------------------
13062
+ // INT
13063
+ // -----------------------------------------------------------------------------
13064
+ const INT = {
13065
+ description: _t("Rounds a number down to the nearest integer that is less than or equal to it."),
13066
+ args: [arg("value (number)", _t("The number to round down to the nearest integer."))],
13067
+ returns: ["NUMBER"],
13068
+ compute: function (value) {
13069
+ return Math.floor(toNumber(value, this.locale));
13070
+ },
13071
+ isExported: true,
13072
+ };
13044
13073
 
13045
13074
  var math = /*#__PURE__*/Object.freeze({
13046
13075
  __proto__: null,
@@ -13074,6 +13103,7 @@ var math = /*#__PURE__*/Object.freeze({
13074
13103
  FLOOR: FLOOR,
13075
13104
  FLOOR_MATH: FLOOR_MATH,
13076
13105
  FLOOR_PRECISE: FLOOR_PRECISE,
13106
+ INT: INT,
13077
13107
  ISEVEN: ISEVEN,
13078
13108
  ISODD: ISODD,
13079
13109
  ISO_CEILING: ISO_CEILING,
@@ -18600,10 +18630,11 @@ const DEFAULT_IS_SORTED = true;
18600
18630
  const DEFAULT_MATCH_MODE = 0;
18601
18631
  const DEFAULT_SEARCH_MODE = 1;
18602
18632
  const DEFAULT_ABSOLUTE_RELATIVE_MODE = 1;
18603
- function assertAvailable(variable, searchKey) {
18604
- if (variable === undefined) {
18605
- throw new NotAvailableError(_t("Did not find value '%s' in [[FUNCTION_NAME]] evaluation.", toString(searchKey)));
18606
- }
18633
+ function valueNotAvailable(searchKey) {
18634
+ return {
18635
+ value: CellErrorType.NotAvailable,
18636
+ message: _t("Did not find value '%s' in [[FUNCTION_NAME]] evaluation.", toString(searchKey)),
18637
+ };
18607
18638
  }
18608
18639
  // -----------------------------------------------------------------------------
18609
18640
  // ADDRESS
@@ -18700,7 +18731,9 @@ const HLOOKUP = {
18700
18731
  ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18701
18732
  : linearSearch(range, searchKey, "strict", range.length, getValueFromRange);
18702
18733
  const col = range[colIndex];
18703
- assertAvailable(col, searchKey?.value);
18734
+ if (col === undefined) {
18735
+ return valueNotAvailable(searchKey);
18736
+ }
18704
18737
  return col[_index - 1];
18705
18738
  },
18706
18739
  isExported: true,
@@ -18757,11 +18790,11 @@ const LOOKUP = {
18757
18790
  : (range, index) => range[index][0].value;
18758
18791
  const rangeLength = verticalSearch ? nbRow : nbCol;
18759
18792
  const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18760
- if (index === -1)
18761
- assertAvailable(undefined, searchKey?.value);
18762
- verticalSearch
18763
- ? assertAvailable(searchArray[0][index], searchKey?.value)
18764
- : assertAvailable(searchArray[index][nbRow - 1], searchKey?.value);
18793
+ if (index === -1 ||
18794
+ (verticalSearch && searchArray[0][index] === undefined) ||
18795
+ (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18796
+ return valueNotAvailable(searchKey);
18797
+ }
18765
18798
  if (resultRange === undefined) {
18766
18799
  return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18767
18800
  }
@@ -18811,7 +18844,10 @@ const MATCH = {
18811
18844
  index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18812
18845
  break;
18813
18846
  }
18814
- assertAvailable(nbCol === 1 ? range[0][index] : range[index], searchKey);
18847
+ if ((nbCol === 1 && range[0][index] === undefined) ||
18848
+ (nbCol !== 1 && range[index] === undefined)) {
18849
+ return valueNotAvailable(searchKey);
18850
+ }
18815
18851
  return index + 1;
18816
18852
  },
18817
18853
  isExported: true,
@@ -18870,7 +18906,9 @@ const VLOOKUP = {
18870
18906
  ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18871
18907
  : linearSearch(range, searchKey, "strict", range[0].length, getValueFromRange);
18872
18908
  const value = range[_index - 1][rowIndex];
18873
- assertAvailable(value, searchKey);
18909
+ if (value === undefined) {
18910
+ return valueNotAvailable(searchKey);
18911
+ }
18874
18912
  return value;
18875
18913
  },
18876
18914
  isExported: true,
@@ -18920,7 +18958,9 @@ const XLOOKUP = {
18920
18958
  ? returnRange.map((col) => [col[index]])
18921
18959
  : [returnRange[index]];
18922
18960
  }
18923
- assertAvailable(defaultValue, searchKey);
18961
+ if (defaultValue === undefined) {
18962
+ return valueNotAvailable(searchKey);
18963
+ }
18924
18964
  return [[defaultValue]];
18925
18965
  },
18926
18966
  isExported: true,
@@ -19692,8 +19732,7 @@ class FunctionRegistry extends Registry {
19692
19732
  function addErrorHandling(compute, functionName) {
19693
19733
  return function (...args) {
19694
19734
  try {
19695
- const computeFormula = compute.bind(this);
19696
- return computeFormula(...args);
19735
+ return compute.apply(this, args);
19697
19736
  }
19698
19737
  catch (e) {
19699
19738
  return handleError(e, functionName);
@@ -19727,8 +19766,7 @@ function hasStringMessage(obj) {
19727
19766
  }
19728
19767
  function addResultHandling(compute) {
19729
19768
  return function (...args) {
19730
- const computeResult = compute.bind(this);
19731
- const result = computeResult(...args);
19769
+ const result = compute.apply(this, args);
19732
19770
  if (!isMatrix(result)) {
19733
19771
  if (typeof result === "object" && result !== null && "value" in result) {
19734
19772
  return result;
@@ -20084,7 +20122,7 @@ cellMenuRegistry
20084
20122
  })
20085
20123
  .add("insert_link", {
20086
20124
  ...insertLink,
20087
- name: _t("Insert link"),
20125
+ name: INSERT_LINK_NAME,
20088
20126
  sequence: 150,
20089
20127
  separator: true,
20090
20128
  });
@@ -21695,7 +21733,7 @@ class SelectionInput extends owl.Component {
21695
21733
  ? existingSelectionRanges
21696
21734
  : this.props.ranges().map((xc, id) => ({
21697
21735
  xc,
21698
- id,
21736
+ id: id + 1,
21699
21737
  isFocused: false,
21700
21738
  }));
21701
21739
  return ranges.map((range) => ({
@@ -26527,6 +26565,17 @@ class TextValueProvider extends owl.Component {
26527
26565
  onValueSelected: Function,
26528
26566
  onValueHovered: Function,
26529
26567
  };
26568
+ autoCompleteListRef = owl.useRef("autoCompleteList");
26569
+ setup() {
26570
+ owl.useEffect(() => {
26571
+ const selectedIndex = this.props.selectedIndex;
26572
+ if (selectedIndex === undefined) {
26573
+ return;
26574
+ }
26575
+ const selectedElement = this.autoCompleteListRef.el?.children[selectedIndex];
26576
+ selectedElement?.scrollIntoView?.({ block: "nearest" });
26577
+ }, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
26578
+ }
26530
26579
  }
26531
26580
 
26532
26581
  class ContentEditableHelper {
@@ -26971,6 +27020,7 @@ css /* scss */ `
26971
27020
  position: absolute;
26972
27021
  margin: 1px 4px;
26973
27022
  pointer-events: none;
27023
+ overflow: auto;
26974
27024
 
26975
27025
  .o-semi-bold {
26976
27026
  /** FIXME: to remove in favor of Bootstrap
@@ -27028,7 +27078,10 @@ class Composer extends owl.Component {
27028
27078
  if (this.props.delimitation && this.props.rect) {
27029
27079
  const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
27030
27080
  const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
27081
+ assistantStyle["max-height"] = `${remainingHeight}px`;
27031
27082
  if (cellY > remainingHeight) {
27083
+ const availableSpaceAbove = cellY;
27084
+ assistantStyle["max-height"] = `${availableSpaceAbove}px`;
27032
27085
  // render top
27033
27086
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
27034
27087
  assistantStyle.top = `-3px`;
@@ -27720,7 +27773,11 @@ class GridComposer extends owl.Component {
27720
27773
  }
27721
27774
  }
27722
27775
  updateCellReferenceVisibility() {
27723
- if (this.isCellReferenceVisible || this.env.model.getters.getEditionMode() === "inactive") {
27776
+ if (this.env.model.getters.getEditionMode() === "inactive") {
27777
+ this.isCellReferenceVisible = false;
27778
+ return;
27779
+ }
27780
+ if (this.isCellReferenceVisible) {
27724
27781
  return;
27725
27782
  }
27726
27783
  const sheetId = this.env.model.getters.getActiveSheetId();
@@ -27749,19 +27806,21 @@ class GridCellIcon extends owl.Component {
27749
27806
  slots: Object,
27750
27807
  };
27751
27808
  get iconStyle() {
27752
- const x = this.getIconHorizontalPosition();
27753
- const y = this.getIconVerticalPosition();
27809
+ const cellPosition = this.props.cellPosition;
27810
+ const merge = this.env.model.getters.getMerge(cellPosition);
27811
+ const zone = merge || positionToZone(cellPosition);
27812
+ const rect = this.env.model.getters.getVisibleRectWithoutHeaders(zone);
27813
+ const x = this.getIconHorizontalPosition(rect, cellPosition);
27814
+ const y = this.getIconVerticalPosition(rect, cellPosition);
27754
27815
  return cssPropertiesToCss({
27755
27816
  top: `${y + (this.props.offset?.y || 0)}px`,
27756
27817
  left: `${x + (this.props.offset?.x || 0)}px`,
27757
27818
  });
27758
27819
  }
27759
- getIconVerticalPosition() {
27760
- const { sheetId, row } = this.props.cellPosition;
27761
- const merge = this.env.model.getters.getMerge(this.props.cellPosition);
27762
- const start = this.env.model.getters.getRowDimensionsInViewport(sheetId, row).start;
27763
- const end = this.env.model.getters.getRowDimensionsInViewport(sheetId, merge ? merge.bottom : row).end;
27764
- const cell = this.env.model.getters.getCell(this.props.cellPosition);
27820
+ getIconVerticalPosition(rect, cellPosition) {
27821
+ const start = rect.y;
27822
+ const end = rect.y + rect.height;
27823
+ const cell = this.env.model.getters.getCell(cellPosition);
27765
27824
  const align = this.props.verticalAlign || cell?.style?.verticalAlign || DEFAULT_VERTICAL_ALIGN;
27766
27825
  switch (align) {
27767
27826
  case "bottom":
@@ -27773,13 +27832,11 @@ class GridCellIcon extends owl.Component {
27773
27832
  return end - GRID_ICON_EDGE_LENGTH - centeringOffset;
27774
27833
  }
27775
27834
  }
27776
- getIconHorizontalPosition() {
27777
- const { sheetId, col } = this.props.cellPosition;
27778
- const merge = this.env.model.getters.getMerge(this.props.cellPosition);
27779
- const start = this.env.model.getters.getColDimensionsInViewport(sheetId, col).start;
27780
- const end = this.env.model.getters.getColDimensionsInViewport(sheetId, merge ? merge.right : col).end;
27781
- const cell = this.env.model.getters.getCell(this.props.cellPosition);
27782
- const evaluatedCell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
27835
+ getIconHorizontalPosition(rect, cellPosition) {
27836
+ const start = rect.x;
27837
+ const end = rect.x + rect.width;
27838
+ const cell = this.env.model.getters.getCell(cellPosition);
27839
+ const evaluatedCell = this.env.model.getters.getEvaluatedCell(cellPosition);
27783
27840
  const align = this.props.horizontalAlign || cell?.style?.align || evaluatedCell.defaultAlign;
27784
27841
  switch (align) {
27785
27842
  case "right":
@@ -27863,6 +27920,8 @@ css /* scss */ `
27863
27920
  height: ${CHECKBOX_WIDTH}px;
27864
27921
  accent-color: #808080;
27865
27922
  margin: ${MARGIN}px;
27923
+ /** required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
27924
+ position: absolute;
27866
27925
  }
27867
27926
  `;
27868
27927
  class DataValidationCheckbox extends owl.Component {
@@ -27881,7 +27940,7 @@ class DataValidationCheckbox extends owl.Component {
27881
27940
  }
27882
27941
  get isDisabled() {
27883
27942
  const cell = this.env.model.getters.getCell(this.props.cellPosition);
27884
- return !!cell?.isFormula;
27943
+ return this.env.model.getters.isReadonly() || !!cell?.isFormula;
27885
27944
  }
27886
27945
  }
27887
27946
 
@@ -27921,12 +27980,17 @@ class DataValidationOverlay extends owl.Component {
27921
27980
  static props = {};
27922
27981
  static components = { GridCellIcon, DataValidationCheckbox, DataValidationListIcon };
27923
27982
  get checkBoxCellPositions() {
27924
- return this.env.model.getters.getDataValidationCheckBoxCellPositions();
27983
+ return this.env.model.getters
27984
+ .getVisibleCellPositions()
27985
+ .filter(this.env.model.getters.isCellValidCheckbox);
27925
27986
  }
27926
27987
  get listIconsCellPositions() {
27927
- return this.env.model.getters.isReadonly()
27928
- ? []
27929
- : this.env.model.getters.getDataValidationListCellsPositions();
27988
+ if (this.env.model.getters.isReadonly()) {
27989
+ return [];
27990
+ }
27991
+ return this.env.model.getters
27992
+ .getVisibleCellPositions()
27993
+ .filter(this.env.model.getters.cellHasListDataValidationIcon);
27930
27994
  }
27931
27995
  }
27932
27996
 
@@ -34288,29 +34352,12 @@ const MIGRATIONS = [
34288
34352
  from: 12,
34289
34353
  to: 12.5,
34290
34354
  applyMigration(data) {
34291
- for (let sheet of data.sheets || []) {
34292
- let knownDataFilterZones = [];
34293
- for (let filterTable of sheet.filterTables || []) {
34294
- const zone = toZone(filterTable.range);
34295
- // See commit message for the details
34296
- const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
34297
- if (intersectZoneIndex !== -1) {
34298
- knownDataFilterZones[intersectZoneIndex] = zone;
34299
- }
34300
- else {
34301
- knownDataFilterZones.push(zone);
34302
- }
34303
- }
34304
- sheet.filterTables = knownDataFilterZones.map((zone) => ({
34305
- range: zoneToXc(zone),
34306
- }));
34307
- }
34308
- return data;
34355
+ return fixOverlappingFilters(data);
34309
34356
  },
34310
34357
  },
34311
34358
  {
34312
34359
  description: "Change Border description structure",
34313
- from: 12,
34360
+ from: 12.5,
34314
34361
  to: 13,
34315
34362
  applyMigration(data) {
34316
34363
  for (const borderId in data.borders) {
@@ -34341,6 +34388,14 @@ const MIGRATIONS = [
34341
34388
  return data;
34342
34389
  },
34343
34390
  },
34391
+ {
34392
+ description: "Fix datafilter duplication (post saas-16.4)",
34393
+ from: 14,
34394
+ to: 14.5,
34395
+ applyMigration(data) {
34396
+ return fixOverlappingFilters(data);
34397
+ },
34398
+ },
34344
34399
  ];
34345
34400
  /**
34346
34401
  * This function is used to repair faulty data independently of the migration.
@@ -34498,6 +34553,26 @@ function fixChartDefinitions(data, initialMessages) {
34498
34553
  }
34499
34554
  return messages;
34500
34555
  }
34556
+ function fixOverlappingFilters(data) {
34557
+ for (let sheet of data.sheets || []) {
34558
+ let knownDataFilterZones = [];
34559
+ for (let filterTable of sheet.filterTables || []) {
34560
+ const zone = toZone(filterTable.range);
34561
+ // See commit message of https://github.com/odoo/o-spreadsheet/pull/3632 of more details
34562
+ const intersectZoneIndex = knownDataFilterZones.findIndex((knownZone) => overlap(knownZone, zone));
34563
+ if (intersectZoneIndex !== -1) {
34564
+ knownDataFilterZones[intersectZoneIndex] = zone;
34565
+ }
34566
+ else {
34567
+ knownDataFilterZones.push(zone);
34568
+ }
34569
+ }
34570
+ sheet.filterTables = knownDataFilterZones.map((zone) => ({
34571
+ range: zoneToXc(zone),
34572
+ }));
34573
+ }
34574
+ return data;
34575
+ }
34501
34576
  // -----------------------------------------------------------------------------
34502
34577
  // Helpers
34503
34578
  // -----------------------------------------------------------------------------
@@ -35147,22 +35222,7 @@ class BordersPlugin extends CorePlugin {
35147
35222
  }
35148
35223
  }
35149
35224
  export(data) {
35150
- // Borders
35151
- let borderId = 0;
35152
35225
  const borders = {};
35153
- /**
35154
- * Get the id of the given border. If the border does not exist, it creates
35155
- * one.
35156
- */
35157
- function getBorderId(border) {
35158
- for (let [key, value] of Object.entries(borders)) {
35159
- if (deepEquals(value, border)) {
35160
- return parseInt(key, 10);
35161
- }
35162
- }
35163
- borders[++borderId] = border;
35164
- return borderId;
35165
- }
35166
35226
  for (let sheet of data.sheets) {
35167
35227
  for (let col = 0; col < sheet.colNumber; col++) {
35168
35228
  for (let row = 0; row < sheet.rowNumber; row++) {
@@ -35170,7 +35230,7 @@ class BordersPlugin extends CorePlugin {
35170
35230
  if (border) {
35171
35231
  const xc = toXC(col, row);
35172
35232
  const cell = sheet.cells[xc];
35173
- const borderId = getBorderId(border);
35233
+ const borderId = getItemId(border, borders);
35174
35234
  if (cell) {
35175
35235
  cell.border = borderId;
35176
35236
  }
@@ -35959,9 +36019,9 @@ class CellPlugin extends CorePlugin {
35959
36019
  allowDispatch(cmd) {
35960
36020
  switch (cmd.type) {
35961
36021
  case "UPDATE_CELL":
35962
- return this.checkCellOutOfSheet(cmd);
36022
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessUpdateCell);
35963
36023
  case "CLEAR_CELL":
35964
- return this.checkValidations(cmd, this.chainValidations(this.checkCellOutOfSheet, this.checkUselessClearCell));
36024
+ return this.checkValidations(cmd, this.checkCellOutOfSheet, this.checkUselessClearCell);
35965
36025
  default:
35966
36026
  return "Success" /* CommandResult.Success */;
35967
36027
  }
@@ -36111,8 +36171,8 @@ class CellPlugin extends CorePlugin {
36111
36171
  }
36112
36172
  removeDefaultStyleValues(style) {
36113
36173
  const cleanedStyle = { ...style };
36114
- for (const [property, defaultValue] of Object.entries(DEFAULT_STYLE)) {
36115
- if (cleanedStyle[property] === defaultValue) {
36174
+ for (const property in DEFAULT_STYLE) {
36175
+ if (cleanedStyle[property] === DEFAULT_STYLE[property]) {
36116
36176
  delete cleanedStyle[property];
36117
36177
  }
36118
36178
  }
@@ -36268,10 +36328,7 @@ class CellPlugin extends CorePlugin {
36268
36328
  else {
36269
36329
  style = before ? before.style : undefined;
36270
36330
  }
36271
- const locale = this.getters.getLocale();
36272
- let format = ("format" in after ? after.format : before && before.format) ||
36273
- detectDateFormat(afterContent, locale) ||
36274
- detectNumberFormat(afterContent);
36331
+ const format = "format" in after ? after.format : before && before.format;
36275
36332
  /* Read the following IF as:
36276
36333
  * we need to remove the cell if it is completely empty, but we can know if it completely empty if:
36277
36334
  * - the command says the new content is empty and has no border/format/style
@@ -36312,6 +36369,7 @@ class CellPlugin extends CorePlugin {
36312
36369
  }
36313
36370
  createLiteralCell(id, content, format, style) {
36314
36371
  const locale = this.getters.getLocale();
36372
+ format = format || detectDateFormat(content, locale) || detectNumberFormat(content);
36315
36373
  if (format !== PLAIN_TEXT_FORMAT && !isEvaluationError(content)) {
36316
36374
  content = toString(parseLiteral(content, locale));
36317
36375
  }
@@ -36384,6 +36442,18 @@ class CellPlugin extends CorePlugin {
36384
36442
  }
36385
36443
  return "Success" /* CommandResult.Success */;
36386
36444
  }
36445
+ checkUselessUpdateCell(cmd) {
36446
+ const cell = this.getters.getCell(cmd);
36447
+ const hasContent = "content" in cmd || "formula" in cmd;
36448
+ const hasStyle = "style" in cmd;
36449
+ const hasFormat = "format" in cmd;
36450
+ if ((!hasContent || cell?.content === cmd.content) &&
36451
+ (!hasStyle || deepEquals(cell?.style, cmd.style)) &&
36452
+ (!hasFormat || cell?.format === cmd.format)) {
36453
+ return "NoChanges" /* CommandResult.NoChanges */;
36454
+ }
36455
+ return "Success" /* CommandResult.Success */;
36456
+ }
36387
36457
  }
36388
36458
  class FormulaCellWithDependencies {
36389
36459
  id;
@@ -39024,7 +39094,6 @@ class SheetPlugin extends CorePlugin {
39024
39094
  "getNumberHeaders",
39025
39095
  "getGridLinesVisibility",
39026
39096
  "getNextSheetName",
39027
- "isEmpty",
39028
39097
  "getSheetSize",
39029
39098
  "getSheetZone",
39030
39099
  "getPaneDivisions",
@@ -39399,14 +39468,6 @@ class SheetPlugin extends CorePlugin {
39399
39468
  // ---------------------------------------------------------------------------
39400
39469
  // Row/Col manipulation
39401
39470
  // ---------------------------------------------------------------------------
39402
- /**
39403
- * Check if a zone only contains empty cells
39404
- */
39405
- isEmpty(sheetId, zone) {
39406
- return positions(zone)
39407
- .map(({ col, row }) => this.getCell({ sheetId, col, row }))
39408
- .every((cell) => !cell || cell.content === "");
39409
- }
39410
39471
  getCommandZones(cmd) {
39411
39472
  const zones = [];
39412
39473
  if ("zone" in cmd) {
@@ -41168,7 +41229,15 @@ class SpreadsheetRTree {
41168
41229
  if (!this.rTrees[sheetId]) {
41169
41230
  return;
41170
41231
  }
41171
- this.rTrees[sheetId].remove(item, deepEquals);
41232
+ this.rTrees[sheetId].remove(item, this.rtreeItemComparer);
41233
+ }
41234
+ rtreeItemComparer(left, right) {
41235
+ return (left.data == right.data &&
41236
+ left.boundingBox.sheetId === right.boundingBox.sheetId &&
41237
+ left.boundingBox?.zone.left === right.boundingBox.zone.left &&
41238
+ left.boundingBox?.zone.top === right.boundingBox.zone.top &&
41239
+ left.boundingBox?.zone.right === right.boundingBox.zone.right &&
41240
+ left.boundingBox?.zone.bottom === right.boundingBox.zone.bottom);
41172
41241
  }
41173
41242
  }
41174
41243
  /**
@@ -41246,7 +41315,7 @@ class FormulaDependencyGraph {
41246
41315
  const queue = Array.from(ranges).reverse();
41247
41316
  while (queue.length > 0) {
41248
41317
  const range = queue.pop();
41249
- visited.add(...this.encoder.encodeBoundingBox(range));
41318
+ visited.addMany(this.encoder.encodeBoundingBox(range));
41250
41319
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
41251
41320
  for (const positionId of impactedPositionIds) {
41252
41321
  if (!visited.has(positionId)) {
@@ -41254,7 +41323,7 @@ class FormulaDependencyGraph {
41254
41323
  }
41255
41324
  }
41256
41325
  }
41257
- visited.delete(...ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41326
+ visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41258
41327
  return visited;
41259
41328
  }
41260
41329
  }
@@ -41394,9 +41463,9 @@ class Evaluator {
41394
41463
  const cells = positions.map((p) => this.encoder.encode(p));
41395
41464
  const cellsToCompute = new JetSet(cells);
41396
41465
  const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
41397
- cellsToCompute.add(...this.getCellsDependingOn(cells));
41398
- cellsToCompute.add(...arrayFormulasPositionIds);
41399
- cellsToCompute.add(...this.getCellsDependingOn(arrayFormulasPositionIds));
41466
+ cellsToCompute.addMany(this.getCellsDependingOn(cells));
41467
+ cellsToCompute.addMany(arrayFormulasPositionIds);
41468
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
41400
41469
  this.evaluate(cellsToCompute);
41401
41470
  }
41402
41471
  getArrayFormulasImpactedByChangesOf(positionIds) {
@@ -41410,7 +41479,7 @@ class Evaluator {
41410
41479
  }
41411
41480
  if (!content) {
41412
41481
  // The previous content could have blocked some array formulas
41413
- impactedPositionIds.add(...this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41482
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41414
41483
  }
41415
41484
  }
41416
41485
  return impactedPositionIds;
@@ -41462,7 +41531,7 @@ class Evaluator {
41462
41531
  }
41463
41532
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
41464
41533
  const cells = new JetSet(arrayFormulas);
41465
- cells.add(...this.getCellsDependingOn(arrayFormulas));
41534
+ cells.addMany(this.getCellsDependingOn(arrayFormulas));
41466
41535
  return cells;
41467
41536
  }
41468
41537
  nextPositionsToUpdate = new JetSet();
@@ -41513,7 +41582,7 @@ class Evaluator {
41513
41582
  : evaluateLiteral(cell.content, localeFormat);
41514
41583
  }
41515
41584
  catch (e) {
41516
- return createEvaluatedCell(e.value, localeFormat, e.message);
41585
+ return createEvaluatedCell(e?.value || CellErrorType.GenericError, localeFormat, e?.message || implementationErrorMessage);
41517
41586
  }
41518
41587
  finally {
41519
41588
  this.cellsBeingComputed.delete(cellId);
@@ -41600,7 +41669,7 @@ class Evaluator {
41600
41669
  this.setEvaluatedCell(positionId, evaluatedCell);
41601
41670
  // check if formula dependencies present in the spread zone
41602
41671
  // if so, they need to be recomputed
41603
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([positionId]));
41672
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
41604
41673
  };
41605
41674
  }
41606
41675
  invalidateSpreading(positionId) {
@@ -41615,8 +41684,8 @@ class Evaluator {
41615
41684
  continue;
41616
41685
  }
41617
41686
  this.evaluatedCells.delete(child);
41618
- this.nextPositionsToUpdate.add(...this.getCellsDependingOn([child]));
41619
- this.nextPositionsToUpdate.add(...this.getArrayFormulasBlockedByOrSpreadingOn(child));
41687
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
41688
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
41620
41689
  }
41621
41690
  this.spreadingRelations.removeNode(positionId);
41622
41691
  }
@@ -41847,6 +41916,7 @@ class EvaluationPlugin extends UIPlugin {
41847
41916
  "getEvaluatedCellsInZone",
41848
41917
  "getSpreadPositionsOf",
41849
41918
  "getArrayFormulaSpreadingOn",
41919
+ "isEmpty",
41850
41920
  ];
41851
41921
  shouldRebuildDependenciesGraph = true;
41852
41922
  evaluator;
@@ -41952,6 +42022,14 @@ class EvaluationPlugin extends UIPlugin {
41952
42022
  getArrayFormulaSpreadingOn(position) {
41953
42023
  return this.evaluator.getArrayFormulaSpreadingOn(position);
41954
42024
  }
42025
+ /**
42026
+ * Check if a zone only contains empty cells
42027
+ */
42028
+ isEmpty(sheetId, zone) {
42029
+ return positions(zone)
42030
+ .map(({ col, row }) => this.getEvaluatedCell({ sheetId, col, row }))
42031
+ .every((cell) => cell.type === CellValueType.empty);
42032
+ }
41955
42033
  // ---------------------------------------------------------------------------
41956
42034
  // Export
41957
42035
  // ---------------------------------------------------------------------------
@@ -42599,8 +42677,6 @@ const VALID_RESULT = { isValid: true };
42599
42677
  class EvaluationDataValidationPlugin extends UIPlugin {
42600
42678
  static getters = [
42601
42679
  "getDataValidationInvalidCriterionValueMessage",
42602
- "getDataValidationCheckBoxCellPositions",
42603
- "getDataValidationListCellsPositions",
42604
42680
  "getInvalidDataValidationMessage",
42605
42681
  "getValidationResultForCellValue",
42606
42682
  "isCellValidCheckbox",
@@ -42664,19 +42740,6 @@ class EvaluationDataValidationPlugin extends UIPlugin {
42664
42740
  const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
42665
42741
  return error ? { error, rule, isValid: false } : VALID_RESULT;
42666
42742
  }
42667
- getDataValidationCheckBoxCellPositions() {
42668
- const rules = this.getters
42669
- .getDataValidationRules(this.getters.getActiveSheetId())
42670
- .filter((rule) => rule.criterion.type === "isBoolean");
42671
- return getCellPositionsInRanges(rules.map((rule) => rule.ranges).flat()).filter((position) => this.isCellValidCheckbox(position));
42672
- }
42673
- getDataValidationListCellsPositions() {
42674
- const rules = this.getters
42675
- .getDataValidationRules(this.getters.getActiveSheetId())
42676
- .filter((rule) => (rule.criterion.type === "isValueInList" || rule.criterion.type === "isValueInRange") &&
42677
- rule.criterion.displayStyle === "arrow");
42678
- return getCellPositionsInRanges(rules.map((rule) => rule.ranges).flat());
42679
- }
42680
42743
  getValidationResultForCell(cellPosition) {
42681
42744
  const { col, row, sheetId } = cellPosition;
42682
42745
  if (!this.validationResults[sheetId]) {
@@ -45485,7 +45548,7 @@ class FindAndReplacePlugin extends UIPlugin {
45485
45548
  }
45486
45549
  finalize() {
45487
45550
  if (this.isSearchDirty) {
45488
- this.refreshSearch();
45551
+ this.refreshSearch(false);
45489
45552
  this.isSearchDirty = false;
45490
45553
  }
45491
45554
  }
@@ -45529,10 +45592,10 @@ class FindAndReplacePlugin extends UIPlugin {
45529
45592
  /**
45530
45593
  * refresh the matches according to the current search options
45531
45594
  */
45532
- refreshSearch() {
45595
+ refreshSearch(jumpToMatchSheet = true) {
45533
45596
  this.selectedMatchIndex = null;
45534
45597
  this.findMatches();
45535
- this.selectNextCell(Direction.current);
45598
+ this.selectNextCell(Direction.current, jumpToMatchSheet);
45536
45599
  }
45537
45600
  /**
45538
45601
  * Updates the regex based on the current searchOptions and
@@ -45614,7 +45677,7 @@ class FindAndReplacePlugin extends UIPlugin {
45614
45677
  * It is also used to keep coherence between the selected searchMatch
45615
45678
  * and selectedMatchIndex.
45616
45679
  */
45617
- selectNextCell(indexChange) {
45680
+ selectNextCell(indexChange, jumpToMatchSheet = true) {
45618
45681
  const matches = this.searchMatches;
45619
45682
  if (!matches.length) {
45620
45683
  this.selectedMatchIndex = null;
@@ -45640,11 +45703,13 @@ class FindAndReplacePlugin extends UIPlugin {
45640
45703
  this.selectedMatchIndex = nextIndex;
45641
45704
  const selectedMatch = matches[nextIndex];
45642
45705
  // Switch to the sheet where the match is located
45643
- if (this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
45706
+ if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
45644
45707
  this.dispatch("ACTIVATE_SHEET", {
45645
45708
  sheetIdFrom: this.getters.getActiveSheetId(),
45646
45709
  sheetIdTo: selectedMatch.sheetId,
45647
45710
  });
45711
+ // We do not want to reset the selection at finalize in this case
45712
+ this.isSearchDirty = false;
45648
45713
  }
45649
45714
  // we want grid selection to capture the selection stream
45650
45715
  this.selection.getBackToDefault();
@@ -46946,19 +47011,17 @@ class SelectionInputsManagerPlugin extends UIPlugin {
46946
47011
  break;
46947
47012
  case "FOCUS_RANGE":
46948
47013
  case "CHANGE_RANGE":
46949
- if (cmd.id !== this.focusedInputId) {
46950
- const input = this.inputs[cmd.id];
46951
- const range = input.ranges.find((range) => range.id === cmd.rangeId);
46952
- if (this.isRangeValid(range?.xc || "A1")) {
46953
- const sheetId = this.getters.getActiveSheetId();
46954
- const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
46955
- this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, {
46956
- handleEvent: input.handleEvent.bind(input),
46957
- release: () => (this.focusedInputId = null),
46958
- });
46959
- }
46960
- this.focusedInputId = cmd.id;
47014
+ const input = this.inputs[cmd.id];
47015
+ const range = input.ranges.find((range) => range.id === cmd.rangeId);
47016
+ if (range) {
47017
+ const sheetId = this.getters.getActiveSheetId();
47018
+ const zone = this.getters.getRangeFromSheetXC(sheetId, range?.xc || "A1").zone;
47019
+ this.selection.capture(input, { cell: { col: zone.left, row: zone.top }, zone }, {
47020
+ handleEvent: input.handleEvent.bind(input),
47021
+ release: () => (this.focusedInputId = null),
47022
+ });
46961
47023
  }
47024
+ this.focusedInputId = cmd.id;
46962
47025
  break;
46963
47026
  }
46964
47027
  this.currentInput?.handle(cmd);
@@ -47212,7 +47275,7 @@ class SheetUIPlugin extends UIPlugin {
47212
47275
  "getCellText",
47213
47276
  "getCellMultiLineText",
47214
47277
  "getContiguousZone",
47215
- "isCellEmpty",
47278
+ "isEvaluatedCellEmpty",
47216
47279
  ];
47217
47280
  ctx = document.createElement("canvas").getContext("2d");
47218
47281
  // ---------------------------------------------------------------------------
@@ -47340,14 +47403,23 @@ class SheetUIPlugin extends UIPlugin {
47340
47403
  return zone;
47341
47404
  }
47342
47405
  /**
47343
- * Check if a cell is empty. If the cell is part of a merge,
47344
- * check if the merge containing the cell is empty.
47406
+ * Checks if a cell evaluated value is empty. If the cell is part of a merge,
47407
+ * the check applies to the main cell of the merge.
47345
47408
  */
47346
- isCellEmpty(position) {
47409
+ isEvaluatedCellEmpty(position) {
47347
47410
  const mainPosition = this.getters.getMainCellPosition(position);
47348
47411
  const cell = this.getters.getEvaluatedCell(mainPosition);
47349
47412
  return cell.type === CellValueType.empty;
47350
47413
  }
47414
+ /**
47415
+ * Checks if a cell is empty (i.e. does not have a content or a formula does not spread over it).
47416
+ * If the cell is part of a merge, the check applies to the main cell of the merge.
47417
+ */
47418
+ isCellEmpty(position) {
47419
+ const mainPosition = this.getters.getMainCellPosition(position);
47420
+ return !(this.getters.getCorrespondingFormulaCell(mainPosition) ||
47421
+ this.getters.getCell(mainPosition)?.content);
47422
+ }
47351
47423
  getColMaxWidth(sheetId, index) {
47352
47424
  const cellsPositions = positions(this.getters.getColsZone(sheetId, index, index));
47353
47425
  const sizes = cellsPositions.map((position) => this.getCellWidth({ sheetId, ...position }));
@@ -50368,6 +50440,8 @@ class SheetViewPlugin extends UIPlugin {
50368
50440
  "getEdgeScrollRow",
50369
50441
  "getVisibleFigures",
50370
50442
  "getVisibleRect",
50443
+ "getVisibleRectWithoutHeaders",
50444
+ "getVisibleCellPositions",
50371
50445
  "getColRowOffsetInViewport",
50372
50446
  "getMainViewportCoordinates",
50373
50447
  "getActiveSheetScrollInfo",
@@ -50611,6 +50685,26 @@ class SheetViewPlugin extends UIPlugin {
50611
50685
  const viewports = this.getSubViewports(sheetId);
50612
50686
  return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
50613
50687
  }
50688
+ /**
50689
+ * Get the positions of all the cells that are visible in the viewport, taking merges into account.
50690
+ */
50691
+ getVisibleCellPositions() {
50692
+ const visibleCols = this.getSheetViewVisibleCols();
50693
+ const visibleRows = this.getSheetViewVisibleRows();
50694
+ const sheetId = this.getters.getActiveSheetId();
50695
+ const positions = [];
50696
+ for (const col of visibleCols) {
50697
+ for (const row of visibleRows) {
50698
+ const position = { sheetId, col, row };
50699
+ const mainPosition = this.getters.getMainCellPosition(position);
50700
+ if (mainPosition.row !== row || mainPosition.col !== col) {
50701
+ continue;
50702
+ }
50703
+ positions.push(position);
50704
+ }
50705
+ }
50706
+ return positions;
50707
+ }
50614
50708
  /**
50615
50709
  * Return the main viewport maximum size relative to the client size.
50616
50710
  */
@@ -50730,6 +50824,13 @@ class SheetViewPlugin extends UIPlugin {
50730
50824
  * Computes the coordinates and size to draw the zone on the canvas
50731
50825
  */
50732
50826
  getVisibleRect(zone) {
50827
+ const rect = this.getVisibleRectWithoutHeaders(zone);
50828
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
50829
+ }
50830
+ /**
50831
+ * Computes the coordinates and size to draw the zone without taking the grid offset into account
50832
+ */
50833
+ getVisibleRectWithoutHeaders(zone) {
50733
50834
  const sheetId = this.getters.getActiveSheetId();
50734
50835
  const viewportRects = this.getSubViewports(sheetId)
50735
50836
  .map((viewport) => viewport.getRect(zone))
@@ -50741,12 +50842,7 @@ class SheetViewPlugin extends UIPlugin {
50741
50842
  const y = Math.min(...viewportRects.map((rect) => rect.y));
50742
50843
  const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
50743
50844
  const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
50744
- return {
50745
- x: x + this.gridOffsetX,
50746
- y: y + this.gridOffsetY,
50747
- width,
50748
- height,
50749
- };
50845
+ return { x, y, width, height };
50750
50846
  }
50751
50847
  /**
50752
50848
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -50970,13 +51066,8 @@ class HeaderPositionsUIPlugin extends UIPlugin {
50970
51066
  }
50971
51067
  break;
50972
51068
  case "UPDATE_CELL":
50973
- if ("content" in cmd || "format" in cmd) {
50974
- this.headerPositions = {};
50975
- this.isDirty = true;
50976
- }
50977
- else {
50978
- this.headerPositions[cmd.sheetId] = this.computeHeaderPositionsOfSheet(cmd.sheetId);
50979
- }
51069
+ this.headerPositions = {};
51070
+ this.isDirty = true;
50980
51071
  break;
50981
51072
  case "UPDATE_FILTER":
50982
51073
  case "REMOVE_FILTER_TABLE":
@@ -54889,8 +54980,8 @@ class SelectionStreamProcessorImpl {
54889
54980
  let currentPosition = startPosition;
54890
54981
  // If both the current cell and the next cell are not empty, we want to go to the end of the cluster
54891
54982
  const nextCellPosition = this.getNextCellPosition(startPosition, dim, dir);
54892
- let mode = !this.getters.isCellEmpty({ ...currentPosition, sheetId }) &&
54893
- !this.getters.isCellEmpty({ ...nextCellPosition, sheetId })
54983
+ let mode = !this.getters.isEvaluatedCellEmpty({ ...currentPosition, sheetId }) &&
54984
+ !this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId })
54894
54985
  ? "endOfCluster"
54895
54986
  : "nextCluster";
54896
54987
  while (true) {
@@ -54900,7 +54991,7 @@ class SelectionStreamProcessorImpl {
54900
54991
  currentPosition.row === nextCellPosition.row) {
54901
54992
  break;
54902
54993
  }
54903
- const isNextCellEmpty = this.getters.isCellEmpty({ ...nextCellPosition, sheetId });
54994
+ const isNextCellEmpty = this.getters.isEvaluatedCellEmpty({ ...nextCellPosition, sheetId });
54904
54995
  if (mode === "endOfCluster" && isNextCellEmpty) {
54905
54996
  break;
54906
54997
  }
@@ -57185,6 +57276,6 @@ exports.setTranslationMethod = setTranslationMethod;
57185
57276
  exports.tokenize = tokenize;
57186
57277
 
57187
57278
 
57188
- __info__.version = "17.1.4";
57189
- __info__.date = "2024-02-09T13:09:13.430Z";
57190
- __info__.hash = "16d3ece";
57279
+ __info__.version = "17.1.7";
57280
+ __info__.date = "2024-03-12T07:56:10.783Z";
57281
+ __info__.hash = "3a9f334";