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