@odoo/o-spreadsheet 18.2.0-alpha.4 → 18.2.0-alpha.6

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 18.2.0-alpha.4
6
- * @date 2025-01-29T06:30:12.773Z
7
- * @hash 6838c26
5
+ * @version 18.2.0-alpha.6
6
+ * @date 2025-02-05T06:50:47.008Z
7
+ * @hash dae9ab2
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -3359,11 +3359,11 @@ const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(l
3359
3359
  * number from the point of view of the isNumber function.
3360
3360
  */
3361
3361
  function parseNumber(str, locale) {
3362
+ // remove invaluable characters
3363
+ str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3362
3364
  if (locale.decimalSeparator !== ".") {
3363
3365
  str = str.replace(locale.decimalSeparator, ".");
3364
3366
  }
3365
- // remove invaluable characters
3366
- str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3367
3367
  let n = Number(str);
3368
3368
  if (isNaN(n) && str.includes("%")) {
3369
3369
  n = Number(str.split("%")[0]);
@@ -18632,19 +18632,20 @@ const HLOOKUP = {
18632
18632
  description: _t("Horizontal lookup"),
18633
18633
  args: [
18634
18634
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18635
- arg("range (range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
18635
+ arg("range (any, range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
18636
18636
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18637
18637
  arg(`is_sorted (boolean, default=${DEFAULT_IS_SORTED})`, _t("Indicates whether the row to be searched (the first row of the specified range) is sorted, in which case the closest match for search_key will be returned.")),
18638
18638
  ],
18639
18639
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18640
18640
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18641
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18641
+ const _range = toMatrix(range);
18642
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18642
18643
  const getValueFromRange = (range, index) => range[index][0].value;
18643
18644
  const _isSorted = toBoolean(isSorted.value);
18644
18645
  const colIndex = _isSorted
18645
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18646
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18647
- const col = range[colIndex];
18646
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18647
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18648
+ const col = _range[colIndex];
18648
18649
  if (col === undefined) {
18649
18650
  return valueNotAvailable(searchKey);
18650
18651
  }
@@ -18736,35 +18737,37 @@ const LOOKUP = {
18736
18737
  description: _t("Look up a value."),
18737
18738
  args: [
18738
18739
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18739
- arg("search_array (range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
18740
- arg("result_range (range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
18740
+ arg("search_array (any, range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
18741
+ arg("result_range (any, range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
18741
18742
  ],
18742
18743
  compute: function (searchKey, searchArray, resultRange) {
18743
- let nbCol = searchArray.length;
18744
- let nbRow = searchArray[0].length;
18744
+ const _searchArray = toMatrix(searchArray);
18745
+ const _resultRange = toMatrix(resultRange);
18746
+ let nbCol = _searchArray.length;
18747
+ let nbRow = _searchArray[0].length;
18745
18748
  const verticalSearch = nbRow >= nbCol;
18746
18749
  const getElement = verticalSearch
18747
18750
  ? (range, index) => range[0][index].value
18748
18751
  : (range, index) => range[index][0].value;
18749
18752
  const rangeLength = verticalSearch ? nbRow : nbCol;
18750
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18753
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18751
18754
  if (index === -1 ||
18752
- (verticalSearch && searchArray[0][index] === undefined) ||
18753
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18755
+ (verticalSearch && _searchArray[0][index] === undefined) ||
18756
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
18754
18757
  return valueNotAvailable(searchKey);
18755
18758
  }
18756
- if (resultRange === undefined) {
18757
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18759
+ if (_resultRange[0].length === 0) {
18760
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
18758
18761
  }
18759
- nbCol = resultRange.length;
18760
- nbRow = resultRange[0].length;
18762
+ nbCol = _resultRange.length;
18763
+ nbRow = _resultRange[0].length;
18761
18764
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
18762
18765
  if (nbCol > 1) {
18763
18766
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
18764
- return resultRange[index][0];
18767
+ return _resultRange[index][0];
18765
18768
  }
18766
18769
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
18767
- return resultRange[0][index];
18770
+ return _resultRange[0][index];
18768
18771
  },
18769
18772
  isExported: true,
18770
18773
  };
@@ -18781,28 +18784,29 @@ const MATCH = {
18781
18784
  ],
18782
18785
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
18783
18786
  let _searchType = toNumber(searchType, this.locale);
18784
- const nbCol = range.length;
18785
- const nbRow = range[0].length;
18787
+ const _range = toMatrix(range);
18788
+ const nbCol = _range.length;
18789
+ const nbRow = _range[0].length;
18786
18790
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
18787
18791
  let index = -1;
18788
18792
  const getElement = nbCol === 1
18789
- ? (range, index) => range[0][index].value
18790
- : (range, index) => range[index][0].value;
18791
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
18793
+ ? (_range, index) => _range[0][index].value
18794
+ : (_range, index) => _range[index][0].value;
18795
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
18792
18796
  _searchType = Math.sign(_searchType);
18793
18797
  switch (_searchType) {
18794
18798
  case 1:
18795
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18799
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18796
18800
  break;
18797
18801
  case 0:
18798
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
18802
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18799
18803
  break;
18800
18804
  case -1:
18801
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18805
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18802
18806
  break;
18803
18807
  }
18804
- if ((nbCol === 1 && range[0][index] === undefined) ||
18805
- (nbCol !== 1 && range[index] === undefined)) {
18808
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
18809
+ (nbCol !== 1 && _range[index] === undefined)) {
18806
18810
  return valueNotAvailable(searchKey);
18807
18811
  }
18808
18812
  return index + 1;
@@ -18857,13 +18861,14 @@ const VLOOKUP = {
18857
18861
  ],
18858
18862
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18859
18863
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18860
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18864
+ const _range = toMatrix(range);
18865
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18861
18866
  const getValueFromRange = (range, index) => range[0][index].value;
18862
18867
  const _isSorted = toBoolean(isSorted.value);
18863
18868
  const rowIndex = _isSorted
18864
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18865
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
18866
- const value = range[_index - 1][rowIndex];
18869
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18870
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18871
+ const value = _range[_index - 1][rowIndex];
18867
18872
  if (value === undefined) {
18868
18873
  return valueNotAvailable(searchKey);
18869
18874
  }
@@ -18900,27 +18905,29 @@ const XLOOKUP = {
18900
18905
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18901
18906
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18902
18907
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18903
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18908
+ const _lookupRange = toMatrix(lookupRange);
18909
+ const _returnRange = toMatrix(returnRange);
18910
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18904
18911
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18905
18912
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18906
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18913
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18907
18914
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18908
18915
  assert(() => lookupDirection === "col"
18909
- ? returnRange[0].length === lookupRange[0].length
18910
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18916
+ ? _returnRange[0].length === _lookupRange[0].length
18917
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18911
18918
  const getElement = lookupDirection === "col"
18912
18919
  ? (range, index) => range[0][index].value
18913
18920
  : (range, index) => range[index][0].value;
18914
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18921
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18915
18922
  const mode = MATCH_MODE[_matchMode];
18916
18923
  const reverseSearch = _searchMode === -1;
18917
18924
  const index = _searchMode === 2 || _searchMode === -2
18918
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18919
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18925
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18926
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18920
18927
  if (index !== -1) {
18921
18928
  return lookupDirection === "col"
18922
- ? returnRange.map((col) => [col[index]])
18923
- : [returnRange[index]];
18929
+ ? _returnRange.map((col) => [col[index]])
18930
+ : [_returnRange[index]];
18924
18931
  }
18925
18932
  if (defaultValue === undefined) {
18926
18933
  return valueNotAvailable(searchKey);
@@ -28456,11 +28463,7 @@ function interpolateData(config, values, labels, newLabels) {
28456
28463
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
28457
28464
  return [];
28458
28465
  }
28459
- const labelMin = Math.min(...labels);
28460
- const labelMax = Math.max(...labels);
28461
- const labelRange = labelMax - labelMin;
28462
- const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
28463
- const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
28466
+ const { normalizedLabels, normalizedNewLabels } = normalizeLabels(labels, newLabels, config);
28464
28467
  try {
28465
28468
  switch (config.type) {
28466
28469
  case "polynomial": {
@@ -28505,6 +28508,30 @@ function interpolateData(config, values, labels, newLabels) {
28505
28508
  return newLabels.map((x) => ({ x, y: NaN }));
28506
28509
  }
28507
28510
  }
28511
+ function normalizeLabels(labels, newLabels, config) {
28512
+ let normalizedLabels = [];
28513
+ let normalizedNewLabels = [];
28514
+ if (config.type === "logarithmic") {
28515
+ // Logarithmic trends in charts are used to visualize proportional growth or
28516
+ // relative changes. Therefore, we change the normalization technique for
28517
+ // logarithmic trend lines for a better fit. The method used here is Max Absolute
28518
+ // Scaling. This Technique is ideal for data spanning several orders of magnitude,
28519
+ // as it balances differences between small and large values by compressing larger
28520
+ // values while preserving proportionality and ensuring all values are scaled relative
28521
+ // to the largest magnitude.
28522
+ const labelMax = Math.max(...labels.map(Math.abs));
28523
+ normalizedLabels = labels.map((l) => l / labelMax);
28524
+ normalizedNewLabels = newLabels.map((l) => l / labelMax);
28525
+ }
28526
+ else {
28527
+ const labelMax = Math.max(...labels);
28528
+ const labelMin = Math.min(...labels);
28529
+ const labelRange = labelMax - labelMin;
28530
+ normalizedLabels = labels.map((l) => (l - labelMax) / labelRange);
28531
+ normalizedNewLabels = newLabels.map((l) => (l - labelMax) / labelRange);
28532
+ }
28533
+ return { normalizedLabels, normalizedNewLabels };
28534
+ }
28508
28535
  function getChartAxisType(chart, labelRange, getters) {
28509
28536
  if (isDateChart(chart, labelRange, getters) && isLuxonTimeAdapterInstalled()) {
28510
28537
  return "time";
@@ -32536,36 +32563,58 @@ class ErrorToolTip extends Component {
32536
32563
  static maxSize = { maxHeight: ERROR_TOOLTIP_MAX_HEIGHT };
32537
32564
  static template = "o-spreadsheet-ErrorToolTip";
32538
32565
  static props = {
32539
- errors: Array,
32566
+ cellPosition: Object,
32540
32567
  onClosed: { type: Function, optional: true },
32541
32568
  };
32569
+ get dataValidationErrorMessage() {
32570
+ return this.env.model.getters.getInvalidDataValidationMessage(this.props.cellPosition);
32571
+ }
32572
+ get evaluationError() {
32573
+ const cell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
32574
+ if (cell.message) {
32575
+ return cell;
32576
+ }
32577
+ return undefined;
32578
+ }
32579
+ get errorOriginPositionString() {
32580
+ const evaluationError = this.evaluationError;
32581
+ const position = evaluationError?.errorOriginPosition;
32582
+ if (!position || deepEquals(position, this.props.cellPosition)) {
32583
+ return "";
32584
+ }
32585
+ const sheetId = position.sheetId;
32586
+ return this.env.model.getters.getRangeString(this.env.model.getters.getRangeFromZone(sheetId, positionToZone(position)), this.env.model.getters.getActiveSheetId());
32587
+ }
32588
+ selectCell() {
32589
+ const position = this.evaluationError?.errorOriginPosition;
32590
+ if (!position) {
32591
+ return;
32592
+ }
32593
+ const activeSheetId = this.env.model.getters.getActiveSheetId();
32594
+ if (position.sheetId !== activeSheetId) {
32595
+ this.env.model.dispatch("ACTIVATE_SHEET", {
32596
+ sheetIdFrom: activeSheetId,
32597
+ sheetIdTo: position.sheetId,
32598
+ });
32599
+ }
32600
+ this.env.model.selection.selectCell(position.col, position.row);
32601
+ }
32542
32602
  }
32543
32603
  const ErrorToolTipPopoverBuilder = {
32544
32604
  onHover: (position, getters) => {
32545
32605
  const cell = getters.getEvaluatedCell(position);
32546
- const errors = [];
32547
- if (cell.type === CellValueType.error && !!cell.message) {
32548
- errors.push({
32549
- title: _t("Error"),
32550
- message: cell.message,
32551
- });
32552
- }
32553
- const validationErrorMessage = getters.getInvalidDataValidationMessage(position);
32554
- if (validationErrorMessage) {
32555
- errors.push({
32556
- title: _t("Invalid"),
32557
- message: validationErrorMessage,
32558
- });
32559
- }
32560
- if (!errors.length) {
32561
- return { isOpen: false };
32606
+ if ((cell.type === CellValueType.error && !!cell.message) ||
32607
+ getters.getInvalidDataValidationMessage(position)) {
32608
+ return {
32609
+ isOpen: true,
32610
+ props: {
32611
+ cellPosition: position,
32612
+ },
32613
+ Component: ErrorToolTip,
32614
+ cellCorner: "TopRight",
32615
+ };
32562
32616
  }
32563
- return {
32564
- isOpen: true,
32565
- props: { errors: errors },
32566
- Component: ErrorToolTip,
32567
- cellCorner: "TopRight",
32568
- };
32617
+ return { isOpen: false };
32569
32618
  },
32570
32619
  };
32571
32620
 
@@ -33129,6 +33178,100 @@ function* iterateChildren(el) {
33129
33178
  function getOpenedMenus() {
33130
33179
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33131
33180
  }
33181
+ function getCurrentSelection(el) {
33182
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33183
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33184
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33185
+ return {
33186
+ start: startSizeBefore,
33187
+ end: endSizeBefore,
33188
+ };
33189
+ }
33190
+ function getStartAndEndSelection(el) {
33191
+ const selection = document.getSelection();
33192
+ return {
33193
+ startElement: selection.anchorNode || el,
33194
+ startSelectionOffset: selection.anchorOffset,
33195
+ endElement: selection.focusNode || el,
33196
+ endSelectionOffset: selection.focusOffset,
33197
+ };
33198
+ }
33199
+ /**
33200
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33201
+ * The selected node is either a Text node or an Element node.
33202
+ *
33203
+ * case 1 -Text node:
33204
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33205
+ * content length of all previous nodes.
33206
+ *
33207
+ * case 2 - Element node:
33208
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33209
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33210
+ *
33211
+ * See the MDN documentation for more details.
33212
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33213
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33214
+ *
33215
+ */
33216
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33217
+ let usedCharacters = 0;
33218
+ let it = iterateChildren(el);
33219
+ let current = it.next();
33220
+ let isFirstParagraph = true;
33221
+ while (!current.done && current.value !== nodeToFind) {
33222
+ if (!current.value.hasChildNodes()) {
33223
+ if (current.value.textContent) {
33224
+ usedCharacters += current.value.textContent.length;
33225
+ }
33226
+ }
33227
+ // One new paragraph = one new line character, except for the first paragraph
33228
+ if (current.value.nodeName === "P" ||
33229
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33230
+ ) {
33231
+ if (isFirstParagraph) {
33232
+ isFirstParagraph = false;
33233
+ }
33234
+ else {
33235
+ usedCharacters++;
33236
+ }
33237
+ }
33238
+ current = it.next();
33239
+ }
33240
+ if (current.value !== nodeToFind) {
33241
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33242
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33243
+ *
33244
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33245
+ */
33246
+ return 0;
33247
+ }
33248
+ else {
33249
+ if (!current.value.hasChildNodes()) {
33250
+ usedCharacters += nodeOffset;
33251
+ }
33252
+ else {
33253
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33254
+ usedCharacters += children.reduce((acc, child, index) => {
33255
+ if (child.textContent !== null) {
33256
+ // need to account for paragraph nodes that implicitly add a new line
33257
+ // except for the last paragraph
33258
+ let chars = child.textContent.length;
33259
+ if (child.nodeName === "P" && index !== children.length - 1) {
33260
+ chars++;
33261
+ }
33262
+ return acc + chars;
33263
+ }
33264
+ else {
33265
+ return acc;
33266
+ }
33267
+ }, 0);
33268
+ }
33269
+ }
33270
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33271
+ usedCharacters++;
33272
+ }
33273
+ return usedCharacters;
33274
+ }
33132
33275
  const letterRegex = /^[a-zA-Z]$/;
33133
33276
  /**
33134
33277
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -37056,7 +37199,7 @@ function dragAndDropBeyondTheViewport(env, cbMouseMove, cbMouseUp, only = false)
37056
37199
  }
37057
37200
  const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
37058
37201
  let { top, left, bottom, right } = getters.getActiveMainViewport();
37059
- let { scrollX, scrollY } = getters.getActiveSheetDOMScrollInfo();
37202
+ let { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
37060
37203
  const { xSplit, ySplit } = getters.getPaneDivisions(sheetId);
37061
37204
  let canEdgeScroll = false;
37062
37205
  let timeoutDelay = MAX_DELAY;
@@ -40336,6 +40479,10 @@ class ContentEditableHelper {
40336
40479
  if (currentStart === start && currentEnd === end) {
40337
40480
  return;
40338
40481
  }
40482
+ if (selection.rangeCount === 0) {
40483
+ const range = document.createRange();
40484
+ selection.addRange(range);
40485
+ }
40339
40486
  const currentRange = selection.getRangeAt(0);
40340
40487
  let range;
40341
40488
  if (this.el.contains(currentRange.startContainer)) {
@@ -40498,7 +40645,7 @@ class ContentEditableHelper {
40498
40645
  if (!focusedNode || !this.el.contains(focusedNode))
40499
40646
  return;
40500
40647
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40501
- element?.scrollIntoView({ block: "nearest" });
40648
+ element?.scrollIntoView?.({ block: "nearest" });
40502
40649
  }
40503
40650
  /**
40504
40651
  * remove the current selection of the user
@@ -40518,100 +40665,7 @@ class ContentEditableHelper {
40518
40665
  * finds the indexes of the current selection.
40519
40666
  * */
40520
40667
  getCurrentSelection() {
40521
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40522
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40523
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40524
- return {
40525
- start: startSizeBefore,
40526
- end: endSizeBefore,
40527
- };
40528
- }
40529
- /**
40530
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40531
- * The selected node is either a Text node or an Element node.
40532
- *
40533
- * case 1 -Text node:
40534
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40535
- * content length of all previous nodes.
40536
- *
40537
- * case 2 - Element node:
40538
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40539
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40540
- *
40541
- * See the MDN documentation for more details.
40542
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40543
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40544
- *
40545
- */
40546
- findSelectionIndex(nodeToFind, nodeOffset) {
40547
- let usedCharacters = 0;
40548
- let it = iterateChildren(this.el);
40549
- let current = it.next();
40550
- let isFirstParagraph = true;
40551
- while (!current.done && current.value !== nodeToFind) {
40552
- if (!current.value.hasChildNodes()) {
40553
- if (current.value.textContent) {
40554
- usedCharacters += current.value.textContent.length;
40555
- }
40556
- }
40557
- // One new paragraph = one new line character, except for the first paragraph
40558
- if (current.value.nodeName === "P" ||
40559
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40560
- ) {
40561
- if (isFirstParagraph) {
40562
- isFirstParagraph = false;
40563
- }
40564
- else {
40565
- usedCharacters++;
40566
- }
40567
- }
40568
- current = it.next();
40569
- }
40570
- if (current.value !== nodeToFind) {
40571
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40572
- * In this case, we return 0 because we don't know the size of the text before the selection.
40573
- *
40574
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40575
- *
40576
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40577
- */
40578
- return 0;
40579
- }
40580
- else {
40581
- if (!current.value.hasChildNodes()) {
40582
- usedCharacters += nodeOffset;
40583
- }
40584
- else {
40585
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40586
- usedCharacters += children.reduce((acc, child, index) => {
40587
- if (child.textContent !== null) {
40588
- // need to account for paragraph nodes that implicitely add a new line
40589
- // except for the last paragraph
40590
- let chars = child.textContent.length;
40591
- if (child.nodeName === "P" && index !== children.length - 1) {
40592
- chars++;
40593
- }
40594
- return acc + chars;
40595
- }
40596
- else {
40597
- return acc;
40598
- }
40599
- }, 0);
40600
- }
40601
- }
40602
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40603
- usedCharacters++;
40604
- }
40605
- return usedCharacters;
40606
- }
40607
- getStartAndEndSelection() {
40608
- const selection = document.getSelection();
40609
- return {
40610
- startElement: selection.anchorNode || this.el,
40611
- startSelectionOffset: selection.anchorOffset,
40612
- endElement: selection.focusNode || this.el,
40613
- endSelectionOffset: selection.focusOffset,
40614
- };
40668
+ return getCurrentSelection(this.el);
40615
40669
  }
40616
40670
  getText() {
40617
40671
  let text = "";
@@ -40875,6 +40929,12 @@ class Composer extends Component {
40875
40929
  }
40876
40930
  this.contentHelper.updateEl(el);
40877
40931
  });
40932
+ this.env.model.selection.observe(this, {
40933
+ handleEvent: () => this.autoCompleteState.hide(),
40934
+ });
40935
+ onWillUnmount(() => {
40936
+ this.env.model.selection.detachObserver(this);
40937
+ });
40878
40938
  useEffect(() => {
40879
40939
  this.processContent();
40880
40940
  if (document.activeElement === this.contentHelper.el &&
@@ -41312,6 +41372,16 @@ class StandaloneComposerStore extends AbstractComposerStore {
41312
41372
  }
41313
41373
  return providersDefinitions;
41314
41374
  }
41375
+ /**
41376
+ * Replace the current reference selected by the new one.
41377
+ * */
41378
+ getZoneReference(zone) {
41379
+ const res = super.getZoneReference(zone);
41380
+ if (this.args().defaultStatic) {
41381
+ return setXcToFixedReferenceType(res, "colrow");
41382
+ }
41383
+ return res;
41384
+ }
41315
41385
  getComposerContent() {
41316
41386
  if (this.editionMode === "inactive") {
41317
41387
  // References in the content might not be linked to the current active sheet
@@ -41377,6 +41447,7 @@ class StandaloneComposer extends Component {
41377
41447
  static props = {
41378
41448
  composerContent: { type: String, optional: true },
41379
41449
  defaultRangeSheetId: { type: String, optional: true },
41450
+ defaultStatic: { type: Boolean, optional: true },
41380
41451
  onConfirm: Function,
41381
41452
  contextualAutocomplete: { type: Object, optional: true },
41382
41453
  placeholder: { type: String, optional: true },
@@ -41387,6 +41458,7 @@ class StandaloneComposer extends Component {
41387
41458
  static components = { Composer };
41388
41459
  static defaultProps = {
41389
41460
  composerContent: "",
41461
+ defaultStatic: false,
41390
41462
  };
41391
41463
  composerFocusStore;
41392
41464
  standaloneComposerStore;
@@ -41397,6 +41469,7 @@ class StandaloneComposer extends Component {
41397
41469
  const standaloneComposerStore = useLocalStore(StandaloneComposerStore, () => ({
41398
41470
  onConfirm: this.props.onConfirm,
41399
41471
  content: this.props.composerContent,
41472
+ defaultStatic: this.props.defaultStatic ?? false,
41400
41473
  contextualAutocomplete: this.props.contextualAutocomplete,
41401
41474
  defaultRangeSheetId: this.props.defaultRangeSheetId,
41402
41475
  getContextualColoredSymbolToken: this.props.getContextualColoredSymbolToken,
@@ -42132,6 +42205,7 @@ class ConditionalFormattingEditor extends Component {
42132
42205
  },
42133
42206
  composerContent: this.state.rules.cellIs.values[valueIndex],
42134
42207
  placeholder: _t("Value or formula"),
42208
+ defaultStatic: true,
42135
42209
  invalid: isInvalid,
42136
42210
  class: "o-sidePanel-composer",
42137
42211
  defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
@@ -42150,6 +42224,7 @@ class ConditionalFormattingEditor extends Component {
42150
42224
  },
42151
42225
  composerContent: threshold.value || "",
42152
42226
  placeholder: _t("Formula"),
42227
+ defaultStatic: true,
42153
42228
  invalid: isInvalid,
42154
42229
  class: "o-sidePanel-composer",
42155
42230
  defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
@@ -42165,6 +42240,7 @@ class ConditionalFormattingEditor extends Component {
42165
42240
  },
42166
42241
  composerContent: inflection.value || "",
42167
42242
  placeholder: _t("Formula"),
42243
+ defaultStatic: true,
42168
42244
  invalid: isInvalid,
42169
42245
  class: "o-sidePanel-composer",
42170
42246
  defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
@@ -49214,7 +49290,7 @@ function isAxisVisible(getters, figure, axis) {
49214
49290
  axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
49215
49291
  break;
49216
49292
  }
49217
- return axisStartEndPositions.some(getters.isPositionVisible);
49293
+ return axisStartEndPositions.some(getters.isPixelPositionVisible);
49218
49294
  }
49219
49295
  /**
49220
49296
  * Get a snap line for the given figure, if the figure can snap to any other figure
@@ -49773,7 +49849,7 @@ class GridAddRowsFooter extends Component {
49773
49849
  });
49774
49850
  this.props.focusGrid();
49775
49851
  // After adding new rows, scroll down to the new last row
49776
- const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
49852
+ const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
49777
49853
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
49778
49854
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
49779
49855
  offsetX: scrollX,
@@ -50031,7 +50107,7 @@ class GridOverlay extends Component {
50031
50107
  resizeObserver.disconnect();
50032
50108
  });
50033
50109
  useTouchMove(this.gridOverlay, this.props.onGridMoved, () => {
50034
- const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
50110
+ const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
50035
50111
  return scrollY > 0;
50036
50112
  });
50037
50113
  this.cellPopovers = useStore(CellPopoverStore);
@@ -50685,17 +50761,6 @@ class GridRenderer {
50685
50761
  get renderingLayers() {
50686
50762
  return ["Background", "Headers"];
50687
50763
  }
50688
- /**
50689
- * Get the offset of a header (see getColRowOffsetInViewport), adjusted with the header
50690
- * size (HEADER_HEIGHT and HEADER_WIDTH)
50691
- */
50692
- getHeaderOffset(dimension, start, index) {
50693
- let size = this.getters.getColRowOffsetInViewport(dimension, start, index);
50694
- if (!this.getters.isDashboard()) {
50695
- size += dimension === "ROW" ? HEADER_HEIGHT : HEADER_WIDTH;
50696
- }
50697
- return size;
50698
- }
50699
50764
  // ---------------------------------------------------------------------------
50700
50765
  // Grid rendering
50701
50766
  // ---------------------------------------------------------------------------
@@ -50703,11 +50768,10 @@ class GridRenderer {
50703
50768
  switch (layer) {
50704
50769
  case "Background":
50705
50770
  this.drawGlobalBackground(renderingContext);
50706
- for (const zone of this.getters.getAllActiveViewportsZones()) {
50771
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
50707
50772
  const { ctx } = renderingContext;
50708
50773
  ctx.save();
50709
50774
  ctx.beginPath();
50710
- const rect = this.getters.getVisibleRect(zone);
50711
50775
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
50712
50776
  ctx.clip();
50713
50777
  const boxes = this.getGridBoxes(zone);
@@ -50983,10 +51047,8 @@ class GridRenderer {
50983
51047
  const { ctx, thinLineWidth } = renderingContext;
50984
51048
  const visibleCols = this.getters.getSheetViewVisibleCols();
50985
51049
  const left = visibleCols[0];
50986
- const right = visibleCols[visibleCols.length - 1];
50987
51050
  const visibleRows = this.getters.getSheetViewVisibleRows();
50988
51051
  const top = visibleRows[0];
50989
- const bottom = visibleRows[visibleRows.length - 1];
50990
51052
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
50991
51053
  const selection = this.getters.getSelectedZones();
50992
51054
  const selectedCols = getZonesCols(selection);
@@ -51002,7 +51064,7 @@ class GridRenderer {
51002
51064
  ctx.lineWidth = thinLineWidth;
51003
51065
  ctx.strokeStyle = "#333";
51004
51066
  // Columns headers background
51005
- for (let col = left; col <= right; col++) {
51067
+ for (const col of visibleCols) {
51006
51068
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
51007
51069
  const { x, width } = this.getters.getVisibleRect(colZone);
51008
51070
  const isColActive = activeCols.has(col);
@@ -51019,7 +51081,7 @@ class GridRenderer {
51019
51081
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
51020
51082
  }
51021
51083
  // Rows headers background
51022
- for (let row = top; row <= bottom; row++) {
51084
+ for (const row of visibleRows) {
51023
51085
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
51024
51086
  const { y, height } = this.getters.getVisibleRect(rowZone);
51025
51087
  const isRowActive = activeRows.has(row);
@@ -51043,27 +51105,41 @@ class GridRenderer {
51043
51105
  ctx.lineTo(width, HEADER_HEIGHT);
51044
51106
  ctx.strokeStyle = HEADER_BORDER_COLOR;
51045
51107
  ctx.stroke();
51046
- ctx.beginPath();
51047
51108
  // column text + separator
51048
- for (const i of visibleCols) {
51049
- const colSize = this.getters.getColSize(sheetId, i);
51050
- const colName = numberToLetters(i);
51051
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
51052
- let colStart = this.getHeaderOffset("COL", left, i);
51109
+ for (const col of visibleCols) {
51110
+ const colName = numberToLetters(col);
51111
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
51112
+ const zone = { left: col, right: col, top: top, bottom: top };
51113
+ const { x: colStart, width: colSize } = this.getters.getRect(zone);
51114
+ const { x, width } = this.getters.getVisibleRect(zone);
51115
+ ctx.save();
51116
+ ctx.beginPath();
51117
+ ctx.rect(x, 0, width, HEADER_HEIGHT);
51118
+ ctx.clip();
51053
51119
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
51120
+ ctx.restore();
51121
+ ctx.beginPath();
51054
51122
  ctx.moveTo(colStart + colSize, 0);
51055
51123
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
51124
+ ctx.stroke();
51056
51125
  }
51057
51126
  // row text + separator
51058
- for (const i of visibleRows) {
51059
- const rowSize = this.getters.getRowSize(sheetId, i);
51060
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
51061
- let rowStart = this.getHeaderOffset("ROW", top, i);
51062
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
51127
+ for (const row of visibleRows) {
51128
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
51129
+ const zone = { top: row, bottom: row, left: left, right: left };
51130
+ const { y: rowStart, height: rowSize } = this.getters.getRect(zone);
51131
+ const { y, height } = this.getters.getVisibleRect(zone);
51132
+ ctx.save();
51133
+ ctx.beginPath();
51134
+ ctx.rect(0, y, HEADER_WIDTH, height);
51135
+ ctx.clip();
51136
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
51137
+ ctx.restore();
51138
+ ctx.beginPath();
51063
51139
  ctx.moveTo(0, rowStart + rowSize);
51064
51140
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
51141
+ ctx.stroke();
51065
51142
  }
51066
- ctx.stroke();
51067
51143
  }
51068
51144
  drawFrozenPanesHeaders(renderingContext) {
51069
51145
  const { ctx, thinLineWidth } = renderingContext;
@@ -51241,8 +51317,8 @@ class GridRenderer {
51241
51317
  previousColIndex = col;
51242
51318
  }
51243
51319
  else {
51244
- nextColIndex = this.findNextEmptyCol(col, right, row);
51245
- previousColIndex = this.findPreviousEmptyCol(col, left, row);
51320
+ nextColIndex = box.border?.right ? zone.right : this.findNextEmptyCol(col, right, row);
51321
+ previousColIndex = box.border?.left ? zone.left : this.findPreviousEmptyCol(col, left, row);
51246
51322
  box.isOverflow = true;
51247
51323
  }
51248
51324
  switch (align) {
@@ -51366,6 +51442,9 @@ function useGridDrawing(refName, model, canvasSize) {
51366
51442
  canvas.width = width * dpr;
51367
51443
  canvas.height = height * dpr;
51368
51444
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
51445
+ if (width === 0 || height === 0) {
51446
+ return;
51447
+ }
51369
51448
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
51370
51449
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
51371
51450
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -51709,7 +51788,7 @@ class HorizontalScrollBar extends Component {
51709
51788
  leftOffset: 0,
51710
51789
  };
51711
51790
  get offset() {
51712
- return this.env.model.getters.getActiveSheetDOMScrollInfo().scrollX;
51791
+ return this.env.model.getters.getActiveSheetScrollInfo().scrollX;
51713
51792
  }
51714
51793
  get width() {
51715
51794
  return this.env.model.getters.getMainViewportRect().width;
@@ -51728,7 +51807,7 @@ class HorizontalScrollBar extends Component {
51728
51807
  };
51729
51808
  }
51730
51809
  onScroll(offset) {
51731
- const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
51810
+ const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
51732
51811
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
51733
51812
  offsetX: offset,
51734
51813
  offsetY: scrollY, // offsetY is the same
@@ -51754,7 +51833,7 @@ class VerticalScrollBar extends Component {
51754
51833
  topOffset: 0,
51755
51834
  };
51756
51835
  get offset() {
51757
- return this.env.model.getters.getActiveSheetDOMScrollInfo().scrollY;
51836
+ return this.env.model.getters.getActiveSheetScrollInfo().scrollY;
51758
51837
  }
51759
51838
  get height() {
51760
51839
  return this.env.model.getters.getMainViewportRect().height;
@@ -51773,7 +51852,7 @@ class VerticalScrollBar extends Component {
51773
51852
  };
51774
51853
  }
51775
51854
  onScroll(offset) {
51776
- const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
51855
+ const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
51777
51856
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
51778
51857
  offsetX: scrollX, // offsetX is the same
51779
51858
  offsetY: offset,
@@ -52244,7 +52323,7 @@ class Grid extends Component {
52244
52323
  });
52245
52324
  }
52246
52325
  moveCanvas(deltaX, deltaY) {
52247
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
52326
+ const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
52248
52327
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
52249
52328
  offsetX: scrollX + deltaX,
52250
52329
  offsetY: scrollY + deltaY,
@@ -52715,7 +52794,7 @@ class BordersPlugin extends CorePlugin {
52715
52794
  // map and slice preserve empty values and do not set `undefined` instead
52716
52795
  const bordersCopy = borders
52717
52796
  .slice()
52718
- .map((col) => col?.slice().map((border) => ({ ...border })));
52797
+ .map((col) => col?.slice().map((border) => deepCopy(border)));
52719
52798
  this.history.update("borders", cmd.sheetIdTo, bordersCopy);
52720
52799
  }
52721
52800
  break;
@@ -52745,32 +52824,12 @@ class BordersPlugin extends CorePlugin {
52745
52824
  const elements = [...cmd.elements].sort((a, b) => b - a);
52746
52825
  for (const group of groupConsecutive(elements)) {
52747
52826
  if (cmd.dimension === "COL") {
52748
- if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52749
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52750
- this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52751
- }
52752
- }
52753
- if (group[group.length - 1] === 0) {
52754
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52755
- this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52756
- }
52757
- }
52758
- const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52827
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1], group[0]);
52759
52828
  this.clearInsideBorders(cmd.sheetId, [zone]);
52760
52829
  this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52761
52830
  }
52762
52831
  else {
52763
- if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52764
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52765
- this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52766
- }
52767
- }
52768
- if (group[group.length - 1] === 0) {
52769
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52770
- this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52771
- }
52772
- }
52773
- const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52832
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1], group[0]);
52774
52833
  this.clearInsideBorders(cmd.sheetId, [zone]);
52775
52834
  this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52776
52835
  }
@@ -52795,16 +52854,12 @@ class BordersPlugin extends CorePlugin {
52795
52854
  let colLeftOfInsertion;
52796
52855
  let colRightOfInsertion;
52797
52856
  if (cmd.position === "before") {
52798
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity, {
52799
- moveFirstLeftBorder: true,
52800
- });
52857
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity);
52801
52858
  colLeftOfInsertion = cmd.base - 1;
52802
52859
  colRightOfInsertion = cmd.base + cmd.quantity;
52803
52860
  }
52804
52861
  else {
52805
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52806
- moveFirstLeftBorder: false,
52807
- });
52862
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity);
52808
52863
  colLeftOfInsertion = cmd.base;
52809
52864
  colRightOfInsertion = cmd.base + cmd.quantity + 1;
52810
52865
  }
@@ -52819,16 +52874,12 @@ class BordersPlugin extends CorePlugin {
52819
52874
  let rowAboveInsertion;
52820
52875
  let rowBelowInsertion;
52821
52876
  if (cmd.position === "before") {
52822
- this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity, {
52823
- moveFirstTopBorder: true,
52824
- });
52877
+ this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity);
52825
52878
  rowAboveInsertion = cmd.base - 1;
52826
52879
  rowBelowInsertion = cmd.base + cmd.quantity;
52827
52880
  }
52828
52881
  else {
52829
- this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52830
- moveFirstTopBorder: false,
52831
- });
52882
+ this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity);
52832
52883
  rowAboveInsertion = cmd.base;
52833
52884
  rowBelowInsertion = cmd.base + cmd.quantity + 1;
52834
52885
  }
@@ -52838,16 +52889,8 @@ class BordersPlugin extends CorePlugin {
52838
52889
  // Getters
52839
52890
  // ---------------------------------------------------------------------------
52840
52891
  getCellBorder({ sheetId, col, row }) {
52841
- const border = {
52842
- top: this.borders[sheetId]?.[col]?.[row]?.horizontal,
52843
- bottom: this.borders[sheetId]?.[col]?.[row + 1]?.horizontal,
52844
- left: this.borders[sheetId]?.[col]?.[row]?.vertical,
52845
- right: this.borders[sheetId]?.[col + 1]?.[row]?.vertical,
52846
- };
52847
- if (!border.bottom && !border.left && !border.right && !border.top) {
52848
- return null;
52849
- }
52850
- return border;
52892
+ const border = this.borders[sheetId]?.[col]?.[row];
52893
+ return border?.top || border?.bottom || border?.left || border?.right ? deepCopy(border) : null;
52851
52894
  }
52852
52895
  getBordersColors(sheetId) {
52853
52896
  const colors = [];
@@ -52855,11 +52898,13 @@ class BordersPlugin extends CorePlugin {
52855
52898
  if (sheetBorders) {
52856
52899
  for (const borders of sheetBorders.filter(isDefined)) {
52857
52900
  for (const cellBorder of borders) {
52858
- if (cellBorder?.horizontal) {
52859
- colors.push(cellBorder.horizontal.color);
52860
- }
52861
- if (cellBorder?.vertical) {
52862
- colors.push(cellBorder.vertical.color);
52901
+ if (cellBorder) {
52902
+ for (const direction of ["top", "bottom", "left", "right"]) {
52903
+ const color = cellBorder[direction]?.color;
52904
+ if (color) {
52905
+ colors.push(color);
52906
+ }
52907
+ }
52863
52908
  }
52864
52909
  }
52865
52910
  }
@@ -52912,7 +52957,7 @@ class BordersPlugin extends CorePlugin {
52912
52957
  getCommonSides(border1, border2) {
52913
52958
  const commonBorder = {};
52914
52959
  for (let side of ["top", "bottom", "left", "right"]) {
52915
- if (border1[side] && border1[side] === border2[side]) {
52960
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52916
52961
  commonBorder[side] = border1[side];
52917
52962
  }
52918
52963
  }
@@ -52957,23 +53002,15 @@ class BordersPlugin extends CorePlugin {
52957
53002
  * @param start starting column (included)
52958
53003
  * @param delta how much borders will be moved (negative if moved to the left)
52959
53004
  */
52960
- shiftBordersHorizontally(sheetId, start, delta, { moveFirstLeftBorder } = {}) {
53005
+ shiftBordersHorizontally(sheetId, start, delta) {
52961
53006
  const borders = this.borders[sheetId];
52962
53007
  if (!borders)
52963
53008
  return;
52964
- if (delta < 0) {
52965
- this.moveBordersOfColumn(sheetId, start, delta, "vertical", {
52966
- destructive: false,
52967
- });
52968
- }
52969
53009
  this.getColumnsWithBorders(sheetId)
52970
53010
  .filter((col) => col >= start)
52971
53011
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52972
53012
  .forEach((col) => {
52973
- if ((col === start && moveFirstLeftBorder) || col !== start) {
52974
- this.moveBordersOfColumn(sheetId, col, delta, "vertical");
52975
- }
52976
- this.moveBordersOfColumn(sheetId, col, delta, "horizontal");
53013
+ this.moveBordersOfColumn(sheetId, col, delta);
52977
53014
  });
52978
53015
  }
52979
53016
  /**
@@ -52982,12 +53019,12 @@ class BordersPlugin extends CorePlugin {
52982
53019
  * @param start starting row (included)
52983
53020
  * @param delta how much borders will be moved (negative if moved to the above)
52984
53021
  */
52985
- shiftBordersVertically(sheetId, start, delta, { moveFirstTopBorder } = {}) {
53022
+ shiftBordersVertically(sheetId, start, delta) {
52986
53023
  const borders = this.borders[sheetId];
52987
53024
  if (!borders)
52988
53025
  return;
52989
53026
  if (delta < 0) {
52990
- this.moveBordersOfRow(sheetId, start, delta, "horizontal", {
53027
+ this.moveBordersOfRow(sheetId, start, delta, {
52991
53028
  destructive: false,
52992
53029
  });
52993
53030
  }
@@ -52995,10 +53032,7 @@ class BordersPlugin extends CorePlugin {
52995
53032
  .filter((row) => row >= start)
52996
53033
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52997
53034
  .forEach((row) => {
52998
- if ((row === start && moveFirstTopBorder) || row !== start) {
52999
- this.moveBordersOfRow(sheetId, row, delta, "horizontal");
53000
- }
53001
- this.moveBordersOfRow(sheetId, row, delta, "vertical");
53035
+ this.moveBordersOfRow(sheetId, row, delta);
53002
53036
  });
53003
53037
  }
53004
53038
  /**
@@ -53012,15 +53046,15 @@ class BordersPlugin extends CorePlugin {
53012
53046
  * argument `destructive` is given false, the target border is preserved if
53013
53047
  * the moved border is empty
53014
53048
  */
53015
- moveBordersOfRow(sheetId, row, delta, borderDirection, { destructive } = { destructive: true }) {
53049
+ moveBordersOfRow(sheetId, row, delta, { destructive } = { destructive: true }) {
53016
53050
  const borders = this.borders[sheetId];
53017
53051
  if (!borders)
53018
53052
  return;
53019
53053
  this.getColumnsWithBorders(sheetId).forEach((col) => {
53020
- const targetBorder = borders[col]?.[row + delta]?.[borderDirection];
53021
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53022
- this.history.update("borders", sheetId, col, row + delta, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53023
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53054
+ const targetBorder = borders[col]?.[row + delta];
53055
+ const movedBorder = borders[col]?.[row];
53056
+ this.history.update("borders", sheetId, col, row + delta, destructive ? movedBorder : movedBorder || targetBorder);
53057
+ this.history.update("borders", sheetId, col, row, undefined);
53024
53058
  });
53025
53059
  }
53026
53060
  /**
@@ -53034,15 +53068,17 @@ class BordersPlugin extends CorePlugin {
53034
53068
  * argument `destructive` is given false, the target border is preserved if
53035
53069
  * the moved border is empty
53036
53070
  */
53037
- moveBordersOfColumn(sheetId, col, delta, borderDirection, { destructive } = { destructive: true }) {
53071
+ moveBordersOfColumn(sheetId, col, delta, { destructive } = { destructive: true }) {
53038
53072
  const borders = this.borders[sheetId];
53039
53073
  if (!borders)
53040
53074
  return;
53041
53075
  this.getRowsRange(sheetId).forEach((row) => {
53042
- const targetBorder = borders[col + delta]?.[row]?.[borderDirection];
53043
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53044
- this.history.update("borders", sheetId, col + delta, row, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53045
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53076
+ const targetBorder = borders[col + delta]?.[row];
53077
+ const movedBorder = borders[col]?.[row];
53078
+ this.history.update("borders", sheetId, col + delta, row, destructive ? movedBorder : movedBorder || targetBorder);
53079
+ if (destructive) {
53080
+ this.history.update("borders", sheetId, col, row, undefined);
53081
+ }
53046
53082
  });
53047
53083
  }
53048
53084
  /**
@@ -53050,33 +53086,69 @@ class BordersPlugin extends CorePlugin {
53050
53086
  * It overrides the current border if override === true.
53051
53087
  */
53052
53088
  setBorder(sheetId, col, row, border, override = true) {
53053
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
53054
- this.history.update("borders", sheetId, col, row, "vertical", border?.left);
53089
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53090
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53091
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.left) {
53092
+ this.history.update("borders", sheetId, col, row, "left", border?.left);
53093
+ if (border?.left &&
53094
+ col > 0 &&
53095
+ !deepEquals(this.getCellBorder({ sheetId, col: col - 1, row })?.right, border?.left)) {
53096
+ this.history.update("borders", sheetId, col - 1, row, "right", undefined);
53097
+ }
53055
53098
  }
53056
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.horizontal) {
53057
- this.history.update("borders", sheetId, col, row, "horizontal", border?.top);
53099
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.top) {
53100
+ this.history.update("borders", sheetId, col, row, "top", border?.top);
53101
+ if (border?.top &&
53102
+ row > 0 &&
53103
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row - 1 })?.bottom, border?.top)) {
53104
+ this.history.update("borders", sheetId, col, row - 1, "bottom", undefined);
53105
+ }
53058
53106
  }
53059
- if (override || !this.borders?.[sheetId]?.[col + 1]?.[row]?.vertical) {
53060
- this.history.update("borders", sheetId, col + 1, row, "vertical", border?.right);
53107
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.right) {
53108
+ this.history.update("borders", sheetId, col, row, "right", border?.right);
53109
+ if (border?.right &&
53110
+ col < maxCol &&
53111
+ !deepEquals(this.getCellBorder({ sheetId, col: col + 1, row })?.left, border?.right)) {
53112
+ this.history.update("borders", sheetId, col + 1, row, "left", undefined);
53113
+ }
53061
53114
  }
53062
- if (override || !this.borders?.[sheetId]?.[col]?.[row + 1]?.horizontal) {
53063
- this.history.update("borders", sheetId, col, row + 1, "horizontal", border?.bottom);
53115
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.bottom) {
53116
+ this.history.update("borders", sheetId, col, row, "bottom", border?.bottom);
53117
+ if (border?.bottom &&
53118
+ row < maxRow &&
53119
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row + 1 })?.top, border?.bottom)) {
53120
+ this.history.update("borders", sheetId, col, row + 1, "top", undefined);
53121
+ }
53064
53122
  }
53065
53123
  }
53066
53124
  /**
53067
53125
  * Remove the borders of a zone
53068
53126
  */
53069
- clearBorders(sheetId, zones) {
53127
+ clearBorders(sheetId, zones, eraseBoundaries = false) {
53128
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53129
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53070
53130
  for (let zone of recomputeZones(zones)) {
53071
53131
  for (let row = zone.top; row <= zone.bottom; row++) {
53072
- this.history.update("borders", sheetId, zone.right + 1, row, "vertical", undefined);
53132
+ if (eraseBoundaries) {
53133
+ if (zone.left > 0) {
53134
+ this.history.update("borders", sheetId, zone.left - 1, row, "right", undefined);
53135
+ }
53136
+ if (zone.right < maxCol) {
53137
+ this.history.update("borders", sheetId, zone.right + 1, row, "left", undefined);
53138
+ }
53139
+ }
53073
53140
  for (let col = zone.left; col <= zone.right; col++) {
53074
53141
  this.history.update("borders", sheetId, col, row, undefined);
53142
+ if (eraseBoundaries) {
53143
+ if (zone.top > 0) {
53144
+ this.history.update("borders", sheetId, col, zone.top - 1, "bottom", undefined);
53145
+ }
53146
+ if (zone.bottom < maxRow) {
53147
+ this.history.update("borders", sheetId, col, zone.bottom + 1, "top", undefined);
53148
+ }
53149
+ }
53075
53150
  }
53076
53151
  }
53077
- for (let col = zone.left; col <= zone.right; col++) {
53078
- this.history.update("borders", sheetId, col, zone.bottom + 1, "horizontal", undefined);
53079
- }
53080
53152
  }
53081
53153
  }
53082
53154
  /**
@@ -53106,41 +53178,63 @@ class BordersPlugin extends CorePlugin {
53106
53178
  */
53107
53179
  setBorders(sheetId, zones, position, border) {
53108
53180
  if (position === "clear") {
53109
- return this.clearBorders(sheetId, zones);
53181
+ return this.clearBorders(sheetId, zones, true);
53110
53182
  }
53111
53183
  for (let zone of recomputeZones(zones)) {
53112
- if (position === "h" || position === "hv" || position === "all") {
53113
- for (let row = zone.top + 1; row <= zone.bottom; row++) {
53184
+ if (position === "all") {
53185
+ for (let row = zone.top; row <= zone.bottom; row++) {
53114
53186
  for (let col = zone.left; col <= zone.right; col++) {
53115
- this.addBorder(sheetId, col, row, { top: border });
53187
+ this.addBorder(sheetId, col, row, {
53188
+ top: border,
53189
+ right: border,
53190
+ bottom: border,
53191
+ left: border,
53192
+ });
53116
53193
  }
53117
53194
  }
53118
53195
  }
53119
- if (position === "v" || position === "hv" || position === "all") {
53196
+ if (position === "h" || position === "hv") {
53197
+ if (zone.top === zone.bottom) {
53198
+ continue;
53199
+ }
53200
+ for (let col = zone.left; col <= zone.right; col++) {
53201
+ this.addBorder(sheetId, col, zone.top, { bottom: border });
53202
+ for (let row = zone.top + 1; row < zone.bottom; row++) {
53203
+ this.addBorder(sheetId, col, row, { top: border, bottom: border });
53204
+ }
53205
+ this.addBorder(sheetId, col, zone.bottom, { top: border });
53206
+ }
53207
+ }
53208
+ if (position === "v" || position === "hv") {
53209
+ if (zone.left === zone.right) {
53210
+ continue;
53211
+ }
53120
53212
  for (let row = zone.top; row <= zone.bottom; row++) {
53121
- for (let col = zone.left + 1; col <= zone.right; col++) {
53122
- this.addBorder(sheetId, col, row, { left: border });
53213
+ this.addBorder(sheetId, zone.left, row, { right: border });
53214
+ for (let col = zone.left + 1; col < zone.right; col++) {
53215
+ this.addBorder(sheetId, col, row, { left: border, right: border });
53123
53216
  }
53217
+ this.addBorder(sheetId, zone.right, row, { left: border });
53124
53218
  }
53125
53219
  }
53126
- if (position === "left" || position === "all" || position === "external") {
53220
+ if (position === "left" || position === "external") {
53127
53221
  for (let row = zone.top; row <= zone.bottom; row++) {
53128
53222
  this.addBorder(sheetId, zone.left, row, { left: border });
53129
53223
  }
53130
53224
  }
53131
- if (position === "right" || position === "all" || position === "external") {
53225
+ if (position === "right" || position === "external") {
53132
53226
  for (let row = zone.top; row <= zone.bottom; row++) {
53133
- this.addBorder(sheetId, zone.right + 1, row, { left: border });
53227
+ this.addBorder(sheetId, zone.right, row, { right: border });
53134
53228
  }
53135
53229
  }
53136
- if (position === "top" || position === "all" || position === "external") {
53230
+ if (position === "top" || position === "external") {
53137
53231
  for (let col = zone.left; col <= zone.right; col++) {
53138
53232
  this.addBorder(sheetId, col, zone.top, { top: border });
53139
53233
  }
53140
53234
  }
53141
- if (position === "bottom" || position === "all" || position === "external") {
53235
+ if (position === "bottom" || position === "external") {
53142
53236
  for (let col = zone.left; col <= zone.right; col++) {
53143
- this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
53237
+ this.addBorder(sheetId, col, zone.bottom, { bottom: border });
53144
53238
  }
53145
53239
  }
53146
53240
  }
@@ -59885,7 +59979,11 @@ class Evaluator {
59885
59979
  computeFormulaCell(formulaPosition, cellData) {
59886
59980
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
59887
59981
  if (!isMatrix(formulaReturn)) {
59888
- return createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59982
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59983
+ if (evaluatedCell.type === CellValueType.error) {
59984
+ evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
59985
+ }
59986
+ return evaluatedCell;
59889
59987
  }
59890
59988
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
59891
59989
  const nbColumns = formulaReturn.length;
@@ -59958,6 +60056,9 @@ class Evaluator {
59958
60056
  const position = { sheetId, col: i + col, row: j + row };
59959
60057
  const cell = this.getters.getCell(position);
59960
60058
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
60059
+ if (evaluatedCell.type === CellValueType.error) {
60060
+ evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
60061
+ }
59961
60062
  this.evaluatedCells.set(position, evaluatedCell);
59962
60063
  };
59963
60064
  return spreadValues;
@@ -61516,7 +61617,7 @@ function withPivotPresentationLayer (PivotClass) {
61516
61617
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
61517
61618
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
61518
61619
  }
61519
- return this._getPivotCellValueAndFormat(symbolName, domain);
61620
+ return this.getPivotCellValueAndFormat(symbolName, domain);
61520
61621
  };
61521
61622
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
61522
61623
  if (isMatrix(result)) {
@@ -64702,14 +64803,12 @@ class SheetUIPlugin extends UIPlugin {
64702
64803
  }
64703
64804
  break;
64704
64805
  case "AUTORESIZE_ROWS":
64705
- for (let row of cmd.rows) {
64706
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64707
- elements: [row],
64708
- dimension: "ROW",
64709
- size: null,
64710
- sheetId: cmd.sheetId,
64711
- });
64712
- }
64806
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
64807
+ elements: cmd.rows,
64808
+ dimension: "ROW",
64809
+ size: null,
64810
+ sheetId: cmd.sheetId,
64811
+ });
64713
64812
  break;
64714
64813
  }
64715
64814
  }
@@ -67105,8 +67204,6 @@ class InternalViewport {
67105
67204
  right;
67106
67205
  offsetX;
67107
67206
  offsetY;
67108
- offsetScrollbarX;
67109
- offsetScrollbarY;
67110
67207
  canScrollVertically;
67111
67208
  canScrollHorizontally;
67112
67209
  viewportWidth;
@@ -67117,10 +67214,17 @@ class InternalViewport {
67117
67214
  this.getters = getters;
67118
67215
  this.sheetId = sheetId;
67119
67216
  this.boundaries = boundaries;
67120
- this.viewportWidth = sizeInGrid.width;
67121
- this.viewportHeight = sizeInGrid.height;
67122
- this.offsetScrollbarX = offsets.x;
67123
- this.offsetScrollbarY = offsets.y;
67217
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
67218
+ throw new Error("Viewport size cannot be negative");
67219
+ }
67220
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
67221
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
67222
+ this.top = boundaries.top;
67223
+ this.bottom = boundaries.bottom;
67224
+ this.left = boundaries.left;
67225
+ this.right = boundaries.right;
67226
+ this.offsetX = offsets.x;
67227
+ this.offsetY = offsets.y;
67124
67228
  this.canScrollVertically = options.canScrollVertically;
67125
67229
  this.canScrollHorizontally = options.canScrollHorizontally;
67126
67230
  this.offsetCorrectionX = this.getters.getColDimensions(this.sheetId, this.boundaries.left).start;
@@ -67161,9 +67265,9 @@ class InternalViewport {
67161
67265
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
67162
67266
  );
67163
67267
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
67164
- }
67165
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
67166
- height += FOOTER_HEIGHT;
67268
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
67269
+ height += FOOTER_HEIGHT;
67270
+ }
67167
67271
  }
67168
67272
  return { width, height };
67169
67273
  }
@@ -67176,7 +67280,7 @@ class InternalViewport {
67176
67280
  if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
67177
67281
  return -1;
67178
67282
  }
67179
- return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left);
67283
+ return this.searchHeaderIndex("COL", x - this.offsetCorrectionX + this.snapCorrection.x, this.left);
67180
67284
  }
67181
67285
  /**
67182
67286
  * Return the index of a row given an offset y, based on the pane top
@@ -67187,7 +67291,7 @@ class InternalViewport {
67187
67291
  if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
67188
67292
  return -1;
67189
67293
  }
67190
- return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top);
67294
+ return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY + this.snapCorrection.y, this.top);
67191
67295
  }
67192
67296
  /**
67193
67297
  * This function will make sure that the provided cell position (or current selected position) is part of
@@ -67207,55 +67311,29 @@ class InternalViewport {
67207
67311
  }
67208
67312
  adjustPositionX(targetCol) {
67209
67313
  const sheetId = this.sheetId;
67210
- const { end } = this.getters.getColDimensions(sheetId, targetCol);
67211
- if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
67212
- const maxCol = this.getters.getNumberCols(sheetId);
67213
- let finalTarget = targetCol;
67214
- while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget < maxCol) {
67215
- finalTarget++;
67216
- }
67217
- const finalTargetEnd = this.getters.getColDimensions(sheetId, finalTarget).end;
67218
- const startIndex = this.searchHeaderIndex("COL", finalTargetEnd - this.viewportWidth - this.offsetCorrectionX, this.boundaries.left);
67219
- this.offsetScrollbarX =
67220
- this.getters.getColDimensions(sheetId, startIndex).end - this.offsetCorrectionX;
67221
- }
67222
- else if (this.left > targetCol) {
67223
- let finalTarget = targetCol;
67224
- while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget > 0) {
67225
- finalTarget--;
67226
- }
67227
- this.offsetScrollbarX =
67228
- this.getters.getColDimensions(sheetId, finalTarget).start - this.offsetCorrectionX;
67314
+ const { start, end } = this.getters.getColDimensions(sheetId, targetCol);
67315
+ if (this.offsetX + this.viewportWidth + this.offsetCorrectionX < end) {
67316
+ this.offsetX = end - this.viewportWidth;
67317
+ }
67318
+ else if (this.offsetX + this.offsetCorrectionX > start) {
67319
+ this.offsetX = start - this.offsetCorrectionX;
67229
67320
  }
67230
67321
  this.adjustViewportZoneX();
67231
67322
  }
67232
67323
  adjustPositionY(targetRow) {
67233
67324
  const sheetId = this.sheetId;
67234
- const { end } = this.getters.getRowDimensions(sheetId, targetRow);
67325
+ const { start, end } = this.getters.getRowDimensions(sheetId, targetRow);
67235
67326
  if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
67236
- const maxRow = this.getters.getNumberRows(sheetId);
67237
- let finalTarget = targetRow;
67238
- while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget < maxRow) {
67239
- finalTarget++;
67240
- }
67241
- const finalTargetEnd = this.getters.getRowDimensions(sheetId, finalTarget).end;
67242
- const startIndex = this.searchHeaderIndex("ROW", finalTargetEnd - this.viewportHeight - this.offsetCorrectionY, this.boundaries.top);
67243
- this.offsetScrollbarY =
67244
- this.getters.getRowDimensions(sheetId, startIndex).end - this.offsetCorrectionY;
67327
+ this.offsetY = end - this.viewportHeight;
67245
67328
  }
67246
- else if (this.top > targetRow) {
67247
- let finalTarget = targetRow;
67248
- while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget > 0) {
67249
- finalTarget--;
67250
- }
67251
- this.offsetScrollbarY =
67252
- this.getters.getRowDimensions(sheetId, finalTarget).start - this.offsetCorrectionY;
67329
+ else if (this.offsetY + this.offsetCorrectionY > start) {
67330
+ this.offsetY = start - this.offsetCorrectionY;
67253
67331
  }
67254
67332
  this.adjustViewportZoneY();
67255
67333
  }
67256
67334
  willNewOffsetScrollViewport(offsetX, offsetY) {
67257
- return ((this.canScrollHorizontally && this.offsetScrollbarX !== offsetX) ||
67258
- (this.canScrollVertically && this.offsetScrollbarY !== offsetY));
67335
+ return ((this.canScrollHorizontally && this.offsetX !== offsetX) ||
67336
+ (this.canScrollVertically && this.offsetY !== offsetY));
67259
67337
  }
67260
67338
  setViewportOffset(offsetX, offsetY) {
67261
67339
  this.setViewportOffsetX(offsetX);
@@ -67272,11 +67350,19 @@ class InternalViewport {
67272
67350
  */
67273
67351
  getVisibleRect(zone) {
67274
67352
  const targetZone = intersection(zone, this);
67353
+ const scrollDeltaX = this.snapCorrection.x;
67354
+ const scrollDeltaY = this.snapCorrection.y;
67275
67355
  if (targetZone) {
67276
- const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
67277
- const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
67278
- const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
67279
- const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
67356
+ const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) +
67357
+ this.offsetCorrectionX -
67358
+ (this.left !== targetZone.left ? scrollDeltaX : 0);
67359
+ const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) +
67360
+ this.offsetCorrectionY -
67361
+ (this.top !== targetZone.top ? scrollDeltaY : 0);
67362
+ const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1) -
67363
+ (this.left === targetZone.left ? scrollDeltaX : 0), this.viewportWidth);
67364
+ const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1) -
67365
+ (this.top === targetZone.top ? scrollDeltaY : 0), this.viewportHeight);
67280
67366
  return { x, y, width, height };
67281
67367
  }
67282
67368
  return undefined;
@@ -67288,12 +67374,14 @@ class InternalViewport {
67288
67374
  */
67289
67375
  getFullRect(zone) {
67290
67376
  const targetZone = intersection(zone, this);
67377
+ const scrollDeltaX = this.snapCorrection.x;
67378
+ const scrollDeltaY = this.snapCorrection.y;
67291
67379
  if (targetZone) {
67292
67380
  const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
67293
67381
  const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
67294
67382
  const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
67295
67383
  const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
67296
- return { x, y, width, height };
67384
+ return { x: x - scrollDeltaX, y: y - scrollDeltaY, width, height };
67297
67385
  }
67298
67386
  return undefined;
67299
67387
  }
@@ -67304,6 +67392,9 @@ class InternalViewport {
67304
67392
  !this.getters.isRowHidden(this.sheetId, row));
67305
67393
  }
67306
67394
  searchHeaderIndex(dimension, position, startIndex = 0) {
67395
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
67396
+ return -1;
67397
+ }
67307
67398
  const sheetId = this.sheetId;
67308
67399
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
67309
67400
  // using a binary search:
@@ -67329,36 +67420,36 @@ class InternalViewport {
67329
67420
  if (!this.canScrollHorizontally) {
67330
67421
  return;
67331
67422
  }
67332
- this.offsetScrollbarX = offsetX;
67423
+ this.offsetX = offsetX;
67333
67424
  this.adjustViewportZoneX();
67334
67425
  }
67335
67426
  setViewportOffsetY(offsetY) {
67336
67427
  if (!this.canScrollVertically) {
67337
67428
  return;
67338
67429
  }
67339
- this.offsetScrollbarY = offsetY;
67430
+ this.offsetY = offsetY;
67340
67431
  this.adjustViewportZoneY();
67341
67432
  }
67342
67433
  /** Corrects the viewport's horizontal offset based on the current structure
67343
- * To make sure that at least on column is visible inside the viewport.
67434
+ * To make sure that at least one column is visible inside the viewport.
67344
67435
  */
67345
67436
  adjustViewportOffsetX() {
67346
67437
  if (this.canScrollHorizontally) {
67347
67438
  const { width: viewportWidth } = this.getMaxSize();
67348
- if (this.viewportWidth + this.offsetScrollbarX > viewportWidth) {
67349
- this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
67439
+ if (this.viewportWidth + this.offsetX > viewportWidth) {
67440
+ this.offsetX = Math.max(0, viewportWidth - this.viewportWidth);
67350
67441
  }
67351
67442
  }
67352
67443
  this.adjustViewportZoneX();
67353
67444
  }
67354
67445
  /** Corrects the viewport's vertical offset based on the current structure
67355
- * To make sure that at least on row is visible inside the viewport.
67446
+ * To make sure that at least one row is visible inside the viewport.
67356
67447
  */
67357
67448
  adjustViewportOffsetY() {
67358
67449
  if (this.canScrollVertically) {
67359
67450
  const { height: paneHeight } = this.getMaxSize();
67360
- if (this.viewportHeight + this.offsetScrollbarY > paneHeight) {
67361
- this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
67451
+ if (this.viewportHeight + this.offsetY > paneHeight) {
67452
+ this.offsetY = Math.max(0, paneHeight - this.viewportHeight);
67362
67453
  }
67363
67454
  }
67364
67455
  this.adjustViewportZoneY();
@@ -67366,34 +67457,47 @@ class InternalViewport {
67366
67457
  /** Updates the pane zone and snapped offset based on its horizontal
67367
67458
  * offset (will find Left) and its width (will find Right) */
67368
67459
  adjustViewportZoneX() {
67369
- const sheetId = this.sheetId;
67370
- this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
67371
- this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
67460
+ this.left = this.searchHeaderIndex("COL", this.offsetX, this.boundaries.left);
67461
+ this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL",
67462
+ // if we hit the border of two cells, we want to match the previous
67463
+ Math.max(this.viewportWidth + this.snapCorrection.x - 0.1), this.left));
67464
+ if (!this.viewportWidth) {
67465
+ return;
67466
+ }
67372
67467
  if (this.left === -1) {
67373
67468
  this.left = this.boundaries.left;
67374
67469
  }
67375
67470
  if (this.right === -1) {
67376
- this.right = this.getters.getNumberCols(sheetId) - 1;
67471
+ this.right = this.boundaries.right;
67377
67472
  }
67378
- this.offsetX =
67379
- this.getters.getColDimensions(sheetId, this.left).start -
67380
- this.getters.getColDimensions(sheetId, this.boundaries.left).start;
67381
67473
  }
67382
67474
  /** Updates the pane zone and snapped offset based on its vertical
67383
67475
  * offset (will find Top) and its width (will find Bottom) */
67384
67476
  adjustViewportZoneY() {
67385
- const sheetId = this.sheetId;
67386
- this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
67387
- this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
67477
+ this.top = this.searchHeaderIndex("ROW", this.offsetY, this.boundaries.top);
67478
+ this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW",
67479
+ // if we hit the border of two cells, we want to match the previous
67480
+ Math.max(this.viewportHeight + this.snapCorrection.y - 0.1, 0), this.top));
67481
+ if (!this.viewportHeight) {
67482
+ return;
67483
+ }
67388
67484
  if (this.top === -1) {
67389
67485
  this.top = this.boundaries.top;
67390
67486
  }
67391
67487
  if (this.bottom === -1) {
67392
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
67488
+ this.bottom = this.boundaries.bottom;
67393
67489
  }
67394
- this.offsetY =
67395
- this.getters.getRowDimensions(sheetId, this.top).start -
67396
- this.getters.getRowDimensions(sheetId, this.boundaries.top).start;
67490
+ }
67491
+ /** represents the part of the header on the topLeft that could be partially
67492
+ * hidden due to the scroll
67493
+ * */
67494
+ get snapCorrection() {
67495
+ return {
67496
+ x: Math.abs(this.offsetX -
67497
+ this.getters.getColRowOffset("COL", this.boundaries.left, Math.max(0, this.left))),
67498
+ y: Math.abs(this.offsetY -
67499
+ this.getters.getColRowOffset("ROW", this.boundaries.top, Math.max(0, this.top))),
67500
+ };
67397
67501
  }
67398
67502
  }
67399
67503
 
@@ -67456,14 +67560,13 @@ class SheetViewPlugin extends UIPlugin {
67456
67560
  "getColRowOffsetInViewport",
67457
67561
  "getMainViewportCoordinates",
67458
67562
  "getActiveSheetScrollInfo",
67459
- "getActiveSheetDOMScrollInfo",
67460
67563
  "getSheetViewVisibleCols",
67461
67564
  "getSheetViewVisibleRows",
67462
67565
  "getFrozenSheetViewRatio",
67463
- "isPositionVisible",
67566
+ "isPixelPositionVisible",
67464
67567
  "getColDimensionsInViewport",
67465
67568
  "getRowDimensionsInViewport",
67466
- "getAllActiveViewportsZones",
67569
+ "getAllActiveViewportsZonesAndRect",
67467
67570
  "getRect",
67468
67571
  ];
67469
67572
  viewports = {};
@@ -67666,8 +67769,8 @@ class SheetViewPlugin extends UIPlugin {
67666
67769
  return this.getMainViewport(sheetId);
67667
67770
  }
67668
67771
  /**
67669
- * Return the scroll info of the active sheet, ie. the offset between the viewport left/top side and
67670
- * the grid left/top side, snapped to the columns/rows.
67772
+ * Return the DOM scroll info of the active sheet, ie. the offset between the viewport left/top side and
67773
+ * the grid left/top side, corresponding to the scroll of the scrollbars and not snapped to the grid.
67671
67774
  */
67672
67775
  getActiveSheetScrollInfo() {
67673
67776
  const sheetId = this.getters.getActiveSheetId();
@@ -67677,28 +67780,16 @@ class SheetViewPlugin extends UIPlugin {
67677
67780
  scrollY: viewport.offsetY,
67678
67781
  };
67679
67782
  }
67680
- /**
67681
- * Return the DOM scroll info of the active sheet, ie. the offset between the viewport left/top side and
67682
- * the grid left/top side, corresponding to the scroll of the scrollbars and not snapped to the grid.
67683
- */
67684
- getActiveSheetDOMScrollInfo() {
67685
- const sheetId = this.getters.getActiveSheetId();
67686
- const viewport = this.getMainInternalViewport(sheetId);
67687
- return {
67688
- scrollX: viewport.offsetScrollbarX,
67689
- scrollY: viewport.offsetScrollbarY,
67690
- };
67691
- }
67692
67783
  getSheetViewVisibleCols() {
67693
67784
  const sheetId = this.getters.getActiveSheetId();
67694
67785
  const viewports = this.getSubViewports(sheetId);
67695
67786
  //TODO ake another commit to eimprove this
67696
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
67787
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
67697
67788
  }
67698
67789
  getSheetViewVisibleRows() {
67699
67790
  const sheetId = this.getters.getActiveSheetId();
67700
67791
  const viewports = this.getSubViewports(sheetId);
67701
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
67792
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
67702
67793
  }
67703
67794
  /**
67704
67795
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -67741,19 +67832,19 @@ class SheetViewPlugin extends UIPlugin {
67741
67832
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
67742
67833
  };
67743
67834
  }
67744
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
67745
- const sheetId = this.getters.getActiveSheetId();
67746
- const visibleCols = this.getters.getSheetViewVisibleCols();
67747
- const visibleRows = this.getters.getSheetViewVisibleRows();
67748
- if (index < referenceIndex) {
67749
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
67835
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
67836
+ if (targetHeaderIndex < referenceHeaderIndex) {
67837
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
67750
67838
  }
67839
+ const sheetId = this.getters.getActiveSheetId();
67840
+ const visibleHeaders = dimension === "COL"
67841
+ ? this.getters.getSheetViewVisibleCols()
67842
+ : this.getters.getSheetViewVisibleRows();
67843
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
67844
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
67845
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
67751
67846
  let offset = 0;
67752
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
67753
- for (let i = referenceIndex; i < index; i++) {
67754
- if (!visibleIndexes.includes(i)) {
67755
- continue;
67756
- }
67847
+ for (const i of relevantIndexes) {
67757
67848
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
67758
67849
  }
67759
67850
  return offset;
@@ -67800,7 +67891,7 @@ class SheetViewPlugin extends UIPlugin {
67800
67891
  }
67801
67892
  return { canEdgeScroll, direction, delay };
67802
67893
  }
67803
- getEdgeScrollRow(y, previousY, tartingY) {
67894
+ getEdgeScrollRow(y, previousY, startingY) {
67804
67895
  let canEdgeScroll = false;
67805
67896
  let direction = 0;
67806
67897
  let delay = 0;
@@ -67821,7 +67912,7 @@ class SheetViewPlugin extends UIPlugin {
67821
67912
  delay = scrollDelay(y - height);
67822
67913
  direction = 1;
67823
67914
  }
67824
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
67915
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
67825
67916
  // 2
67826
67917
  canEdgeScroll = true;
67827
67918
  delay = scrollDelay(offsetCorrectionY - y);
@@ -67847,13 +67938,7 @@ class SheetViewPlugin extends UIPlugin {
67847
67938
  */
67848
67939
  getVisibleRectWithoutHeaders(zone) {
67849
67940
  const sheetId = this.getters.getActiveSheetId();
67850
- const viewportRects = this.getSubViewports(sheetId)
67851
- .map((viewport) => viewport.getVisibleRect(zone))
67852
- .filter(isDefined);
67853
- if (viewportRects.length === 0) {
67854
- return { x: 0, y: 0, width: 0, height: 0 };
67855
- }
67856
- return this.recomposeRect(viewportRects);
67941
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
67857
67942
  }
67858
67943
  /**
67859
67944
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -67861,13 +67946,7 @@ class SheetViewPlugin extends UIPlugin {
67861
67946
  */
67862
67947
  getRect(zone) {
67863
67948
  const sheetId = this.getters.getActiveSheetId();
67864
- const viewportRects = this.getSubViewports(sheetId)
67865
- .map((viewport) => viewport.getFullRect(zone))
67866
- .filter(isDefined);
67867
- if (viewportRects.length === 0) {
67868
- return { x: 0, y: 0, width: 0, height: 0 };
67869
- }
67870
- const rect = this.recomposeRect(viewportRects);
67949
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
67871
67950
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
67872
67951
  }
67873
67952
  /**
@@ -67887,34 +67966,43 @@ class SheetViewPlugin extends UIPlugin {
67887
67966
  * column of the current viewport
67888
67967
  */
67889
67968
  getColDimensionsInViewport(sheetId, col) {
67890
- const left = largeMin(this.getters.getSheetViewVisibleCols());
67891
- const start = this.getters.getColRowOffsetInViewport("COL", left, col);
67892
- const size = this.getters.getColSize(sheetId, col);
67893
- const isColHidden = this.getters.isColHidden(sheetId, col);
67894
- return {
67895
- start,
67896
- size: size,
67897
- end: start + (isColHidden ? 0 : size),
67969
+ const zone = {
67970
+ left: col,
67971
+ right: col,
67972
+ top: 0,
67973
+ bottom: this.getters.getNumberRows(sheetId) - 1,
67898
67974
  };
67975
+ const { x, width } = this.getVisibleRect(zone);
67976
+ const start = x - this.gridOffsetX;
67977
+ return { start, size: width, end: start + width };
67899
67978
  }
67900
67979
  /**
67901
67980
  * Returns the size, start and end coordinates of a row relative to the top row
67902
67981
  * of the current viewport
67903
67982
  */
67904
67983
  getRowDimensionsInViewport(sheetId, row) {
67905
- const top = largeMin(this.getters.getSheetViewVisibleRows());
67906
- const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
67907
- const size = this.getters.getRowSize(sheetId, row);
67908
- const isRowHidden = this.getters.isRowHidden(sheetId, row);
67909
- return {
67910
- start,
67911
- size: size,
67912
- end: start + (isRowHidden ? 0 : size),
67984
+ const zone = {
67985
+ left: 0,
67986
+ right: this.getters.getNumberCols(sheetId) - 1,
67987
+ top: row,
67988
+ bottom: row,
67913
67989
  };
67990
+ const { y, height } = this.getVisibleRect(zone);
67991
+ const start = y - this.gridOffsetY;
67992
+ return { start, size: height, end: start + height };
67914
67993
  }
67915
- getAllActiveViewportsZones() {
67994
+ getAllActiveViewportsZonesAndRect() {
67916
67995
  const sheetId = this.getters.getActiveSheetId();
67917
- return this.getSubViewports(sheetId);
67996
+ return this.getSubViewports(sheetId).map((viewport) => {
67997
+ return {
67998
+ zone: viewport,
67999
+ rect: {
68000
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
68001
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
68002
+ ...viewport.getMaxSize(),
68003
+ },
68004
+ };
68005
+ });
67918
68006
  }
67919
68007
  // ---------------------------------------------------------------------------
67920
68008
  // Private
@@ -67973,12 +68061,11 @@ class SheetViewPlugin extends UIPlugin {
67973
68061
  }
67974
68062
  /** gets rid of deprecated sheetIds */
67975
68063
  cleanViewports() {
67976
- const sheetIds = this.getters.getSheetIds();
67977
- for (let sheetId of Object.keys(this.viewports)) {
67978
- if (!sheetIds.includes(sheetId)) {
67979
- delete this.viewports[sheetId];
67980
- }
68064
+ const newViewport = {};
68065
+ for (const sheetId of this.getters.getSheetIds()) {
68066
+ newViewport[sheetId] = this.viewports[sheetId];
67981
68067
  }
68068
+ this.viewports = newViewport;
67982
68069
  }
67983
68070
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
67984
68071
  this.sheetViewHeight = height;
@@ -67988,7 +68075,7 @@ class SheetViewPlugin extends UIPlugin {
67988
68075
  this.recomputeViewports();
67989
68076
  }
67990
68077
  recomputeViewports() {
67991
- for (let sheetId of Object.keys(this.viewports)) {
68078
+ for (const sheetId of this.getters.getSheetIds()) {
67992
68079
  this.resetViewports(sheetId);
67993
68080
  }
67994
68081
  }
@@ -67999,8 +68086,8 @@ class SheetViewPlugin extends UIPlugin {
67999
68086
  }
68000
68087
  getViewportOffset(sheetId) {
68001
68088
  return {
68002
- x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0,
68003
- y: this.viewports[sheetId]?.bottomRight.offsetScrollbarY || 0,
68089
+ x: this.viewports[sheetId]?.bottomRight.offsetX || 0,
68090
+ y: this.viewports[sheetId]?.bottomRight.offsetY || 0,
68004
68091
  };
68005
68092
  }
68006
68093
  resetViewports(sheetId) {
@@ -68010,8 +68097,10 @@ class SheetViewPlugin extends UIPlugin {
68010
68097
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
68011
68098
  const nCols = this.getters.getNumberCols(sheetId);
68012
68099
  const nRows = this.getters.getNumberRows(sheetId);
68013
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
68014
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
68100
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
68101
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
68102
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
68103
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
68015
68104
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
68016
68105
  const canScrollHorizontally = xRatio < 1.0;
68017
68106
  const canScrollVertically = yRatio < 1.0;
@@ -68022,14 +68111,14 @@ class SheetViewPlugin extends UIPlugin {
68022
68111
  new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: 0, bottom: ySplit - 1 }, { width: colOffset, height: rowOffset }, { canScrollHorizontally: false, canScrollVertically: false }, { x: 0, y: 0 })) ||
68023
68112
  undefined,
68024
68113
  topRight: (ySplit &&
68025
- new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: this.sheetViewWidth - colOffset, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) ||
68114
+ new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: unfrozenWidth, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) ||
68026
68115
  undefined,
68027
68116
  bottomLeft: (xSplit &&
68028
- new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: this.sheetViewHeight - rowOffset }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) ||
68117
+ new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: unfrozenHeight }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) ||
68029
68118
  undefined,
68030
68119
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
68031
- width: this.sheetViewWidth - colOffset,
68032
- height: this.sheetViewHeight - rowOffset,
68120
+ width: unfrozenWidth,
68121
+ height: unfrozenHeight,
68033
68122
  }, { canScrollHorizontally, canScrollVertically }, {
68034
68123
  x: canScrollHorizontally ? previousOffset.x : 0,
68035
68124
  y: canScrollVertically ? previousOffset.y : 0,
@@ -68084,7 +68173,7 @@ class SheetViewPlugin extends UIPlugin {
68084
68173
  }
68085
68174
  return result;
68086
68175
  }
68087
- isPositionVisible(position) {
68176
+ isPixelPositionVisible(position) {
68088
68177
  const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
68089
68178
  const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
68090
68179
  const { width, height } = this.getters.getSheetViewDimension();
@@ -68106,12 +68195,26 @@ class SheetViewPlugin extends UIPlugin {
68106
68195
  const height = this.sheetViewHeight + this.gridOffsetY;
68107
68196
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
68108
68197
  }
68109
- recomposeRect(viewportRects) {
68110
- const x = Math.min(...viewportRects.map((rect) => rect.x));
68111
- const y = Math.min(...viewportRects.map((rect) => rect.y));
68112
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
68113
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
68114
- return { x, y, width, height };
68198
+ mapViewportsToRect(sheetId, rectCallBack) {
68199
+ let x = Infinity;
68200
+ let y = Infinity;
68201
+ let width = 0;
68202
+ let height = 0;
68203
+ let hasViewports = false;
68204
+ for (const viewport of this.getSubViewports(sheetId)) {
68205
+ const rect = rectCallBack(viewport);
68206
+ if (rect) {
68207
+ hasViewports = true;
68208
+ x = Math.min(x, rect.x);
68209
+ y = Math.min(y, rect.y);
68210
+ width = Math.max(width, rect.x + rect.width);
68211
+ height = Math.max(height, rect.y + rect.height);
68212
+ }
68213
+ }
68214
+ if (!hasViewports) {
68215
+ return { x: 0, y: 0, width: 0, height: 0 };
68216
+ }
68217
+ return { x, y, width: width - x, height: height - y };
68115
68218
  }
68116
68219
  }
68117
68220
 
@@ -69286,7 +69389,7 @@ class SpreadsheetDashboard extends Component {
69286
69389
  });
69287
69390
  }
69288
69391
  moveCanvas(deltaX, deltaY) {
69289
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
69392
+ const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
69290
69393
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
69291
69394
  offsetX: scrollX + deltaX,
69292
69395
  offsetY: scrollY + deltaY,
@@ -70479,12 +70582,8 @@ css /* scss */ `
70479
70582
  .o-spreadsheet {
70480
70583
  position: relative;
70481
70584
  display: grid;
70482
- color: ${TEXT_BODY};
70483
70585
  font-size: 14px;
70484
70586
 
70485
- input {
70486
- background-color: white;
70487
- }
70488
70587
  .text-muted {
70489
70588
  color: ${TEXT_BODY_MUTED} !important;
70490
70589
  }
@@ -71731,6 +71830,9 @@ class EventStream {
71731
71830
  observe(owner, callbacks) {
71732
71831
  this.observers.set(owner, { owner, callbacks });
71733
71832
  }
71833
+ detachObserver(owner) {
71834
+ this.observers.delete(owner);
71835
+ }
71734
71836
  /**
71735
71837
  * Capture the stream for yourself
71736
71838
  */
@@ -71823,6 +71925,9 @@ class SelectionStreamProcessorImpl {
71823
71925
  observe(owner, callbacks) {
71824
71926
  this.stream.observe(owner, callbacks);
71825
71927
  }
71928
+ detachObserver(owner) {
71929
+ this.stream.detachObserver(owner);
71930
+ }
71826
71931
  release(owner) {
71827
71932
  if (this.stream.isListening(owner)) {
71828
71933
  this.stream.release(owner);
@@ -75227,6 +75332,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75227
75332
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75228
75333
 
75229
75334
 
75230
- __info__.version = "18.2.0-alpha.4";
75231
- __info__.date = "2025-01-29T06:30:12.773Z";
75232
- __info__.hash = "6838c26";
75335
+ __info__.version = "18.2.0-alpha.6";
75336
+ __info__.date = "2025-02-05T06:50:47.008Z";
75337
+ __info__.hash = "dae9ab2";