@odoo/o-spreadsheet 18.2.0-alpha.5 → 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.5
6
- * @date 2025-01-31T07:59:30.667Z
7
- * @hash efce841
5
+ * @version 18.2.0-alpha.6
6
+ * @date 2025-02-05T06:50:47.008Z
7
+ * @hash dae9ab2
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -18632,19 +18632,20 @@ const HLOOKUP = {
18632
18632
  description: _t("Horizontal lookup"),
18633
18633
  args: [
18634
18634
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18635
- arg("range (range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
18635
+ arg("range (any, range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
18636
18636
  arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
18637
18637
  arg(`is_sorted (boolean, default=${DEFAULT_IS_SORTED})`, _t("Indicates whether the row to be searched (the first row of the specified range) is sorted, in which case the closest match for search_key will be returned.")),
18638
18638
  ],
18639
18639
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18640
18640
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18641
- assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18641
+ const _range = toMatrix(range);
18642
+ assert(() => 1 <= _index && _index <= _range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18642
18643
  const getValueFromRange = (range, index) => range[index][0].value;
18643
18644
  const _isSorted = toBoolean(isSorted.value);
18644
18645
  const colIndex = _isSorted
18645
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range.length, getValueFromRange)
18646
- : linearSearch(range, searchKey, "wildcard", range.length, getValueFromRange);
18647
- const col = range[colIndex];
18646
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18647
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18648
+ const col = _range[colIndex];
18648
18649
  if (col === undefined) {
18649
18650
  return valueNotAvailable(searchKey);
18650
18651
  }
@@ -18736,35 +18737,37 @@ const LOOKUP = {
18736
18737
  description: _t("Look up a value."),
18737
18738
  args: [
18738
18739
  arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
18739
- arg("search_array (range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
18740
- arg("result_range (range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
18740
+ arg("search_array (any, range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
18741
+ arg("result_range (any, range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
18741
18742
  ],
18742
18743
  compute: function (searchKey, searchArray, resultRange) {
18743
- let nbCol = searchArray.length;
18744
- let nbRow = searchArray[0].length;
18744
+ const _searchArray = toMatrix(searchArray);
18745
+ const _resultRange = toMatrix(resultRange);
18746
+ let nbCol = _searchArray.length;
18747
+ let nbRow = _searchArray[0].length;
18745
18748
  const verticalSearch = nbRow >= nbCol;
18746
18749
  const getElement = verticalSearch
18747
18750
  ? (range, index) => range[0][index].value
18748
18751
  : (range, index) => range[index][0].value;
18749
18752
  const rangeLength = verticalSearch ? nbRow : nbCol;
18750
- const index = dichotomicSearch(searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18753
+ const index = dichotomicSearch(_searchArray, searchKey, "nextSmaller", "asc", rangeLength, getElement);
18751
18754
  if (index === -1 ||
18752
- (verticalSearch && searchArray[0][index] === undefined) ||
18753
- (!verticalSearch && searchArray[index][nbRow - 1] === undefined)) {
18755
+ (verticalSearch && _searchArray[0][index] === undefined) ||
18756
+ (!verticalSearch && _searchArray[index][nbRow - 1] === undefined)) {
18754
18757
  return valueNotAvailable(searchKey);
18755
18758
  }
18756
- if (resultRange === undefined) {
18757
- return verticalSearch ? searchArray[nbCol - 1][index] : searchArray[index][nbRow - 1];
18759
+ if (_resultRange[0].length === 0) {
18760
+ return verticalSearch ? _searchArray[nbCol - 1][index] : _searchArray[index][nbRow - 1];
18758
18761
  }
18759
- nbCol = resultRange.length;
18760
- nbRow = resultRange[0].length;
18762
+ nbCol = _resultRange.length;
18763
+ nbRow = _resultRange[0].length;
18761
18764
  assert(() => nbCol === 1 || nbRow === 1, _t("The result_range must be a single row or a single column."));
18762
18765
  if (nbCol > 1) {
18763
18766
  assert(() => index <= nbCol - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range row value %s.", (index + 1).toString()));
18764
- return resultRange[index][0];
18767
+ return _resultRange[index][0];
18765
18768
  }
18766
18769
  assert(() => index <= nbRow - 1, _t("[[FUNCTION_NAME]] evaluates to an out of range column value %s.", (index + 1).toString()));
18767
- return resultRange[0][index];
18770
+ return _resultRange[0][index];
18768
18771
  },
18769
18772
  isExported: true,
18770
18773
  };
@@ -18781,28 +18784,29 @@ const MATCH = {
18781
18784
  ],
18782
18785
  compute: function (searchKey, range, searchType = { value: DEFAULT_SEARCH_TYPE }) {
18783
18786
  let _searchType = toNumber(searchType, this.locale);
18784
- const nbCol = range.length;
18785
- const nbRow = range[0].length;
18787
+ const _range = toMatrix(range);
18788
+ const nbCol = _range.length;
18789
+ const nbRow = _range[0].length;
18786
18790
  assert(() => nbCol === 1 || nbRow === 1, _t("The range must be a single row or a single column."));
18787
18791
  let index = -1;
18788
18792
  const getElement = nbCol === 1
18789
- ? (range, index) => range[0][index].value
18790
- : (range, index) => range[index][0].value;
18791
- const rangeLen = nbCol === 1 ? range[0].length : range.length;
18793
+ ? (_range, index) => _range[0][index].value
18794
+ : (_range, index) => _range[index][0].value;
18795
+ const rangeLen = nbCol === 1 ? _range[0].length : _range.length;
18792
18796
  _searchType = Math.sign(_searchType);
18793
18797
  switch (_searchType) {
18794
18798
  case 1:
18795
- index = dichotomicSearch(range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18799
+ index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18796
18800
  break;
18797
18801
  case 0:
18798
- index = linearSearch(range, searchKey, "wildcard", rangeLen, getElement);
18802
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18799
18803
  break;
18800
18804
  case -1:
18801
- index = dichotomicSearch(range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18805
+ index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
18802
18806
  break;
18803
18807
  }
18804
- if ((nbCol === 1 && range[0][index] === undefined) ||
18805
- (nbCol !== 1 && range[index] === undefined)) {
18808
+ if ((nbCol === 1 && _range[0][index] === undefined) ||
18809
+ (nbCol !== 1 && _range[index] === undefined)) {
18806
18810
  return valueNotAvailable(searchKey);
18807
18811
  }
18808
18812
  return index + 1;
@@ -18857,13 +18861,14 @@ const VLOOKUP = {
18857
18861
  ],
18858
18862
  compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
18859
18863
  const _index = Math.trunc(toNumber(index?.value, this.locale));
18860
- assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18864
+ const _range = toMatrix(range);
18865
+ assert(() => 1 <= _index && _index <= _range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
18861
18866
  const getValueFromRange = (range, index) => range[0][index].value;
18862
18867
  const _isSorted = toBoolean(isSorted.value);
18863
18868
  const rowIndex = _isSorted
18864
- ? dichotomicSearch(range, searchKey, "nextSmaller", "asc", range[0].length, getValueFromRange)
18865
- : linearSearch(range, searchKey, "wildcard", range[0].length, getValueFromRange);
18866
- const value = range[_index - 1][rowIndex];
18869
+ ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18870
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18871
+ const value = _range[_index - 1][rowIndex];
18867
18872
  if (value === undefined) {
18868
18873
  return valueNotAvailable(searchKey);
18869
18874
  }
@@ -18900,27 +18905,29 @@ const XLOOKUP = {
18900
18905
  compute: function (searchKey, lookupRange, returnRange, defaultValue, matchMode = { value: DEFAULT_MATCH_MODE }, searchMode = { value: DEFAULT_SEARCH_MODE }) {
18901
18906
  const _matchMode = Math.trunc(toNumber(matchMode.value, this.locale));
18902
18907
  const _searchMode = Math.trunc(toNumber(searchMode.value, this.locale));
18903
- assert(() => lookupRange.length === 1 || lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18908
+ const _lookupRange = toMatrix(lookupRange);
18909
+ const _returnRange = toMatrix(returnRange);
18910
+ assert(() => _lookupRange.length === 1 || _lookupRange[0].length === 1, _t("lookup_range should be either a single row or single column."));
18904
18911
  assert(() => [-1, 1, -2, 2].includes(_searchMode), _t("search_mode should be a value in [-1, 1, -2, 2]."));
18905
18912
  assert(() => [-1, 0, 1, 2].includes(_matchMode), _t("match_mode should be a value in [-1, 0, 1, 2]."));
18906
- const lookupDirection = lookupRange.length === 1 ? "col" : "row";
18913
+ const lookupDirection = _lookupRange.length === 1 ? "col" : "row";
18907
18914
  assert(() => !(_matchMode === 2 && [-2, 2].includes(_searchMode)), _t("the search and match mode combination is not supported for XLOOKUP evaluation."));
18908
18915
  assert(() => lookupDirection === "col"
18909
- ? returnRange[0].length === lookupRange[0].length
18910
- : returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18916
+ ? _returnRange[0].length === _lookupRange[0].length
18917
+ : _returnRange.length === _lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
18911
18918
  const getElement = lookupDirection === "col"
18912
18919
  ? (range, index) => range[0][index].value
18913
18920
  : (range, index) => range[index][0].value;
18914
- const rangeLen = lookupDirection === "col" ? lookupRange[0].length : lookupRange.length;
18921
+ const rangeLen = lookupDirection === "col" ? _lookupRange[0].length : _lookupRange.length;
18915
18922
  const mode = MATCH_MODE[_matchMode];
18916
18923
  const reverseSearch = _searchMode === -1;
18917
18924
  const index = _searchMode === 2 || _searchMode === -2
18918
- ? dichotomicSearch(lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18919
- : linearSearch(lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18925
+ ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18926
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
18920
18927
  if (index !== -1) {
18921
18928
  return lookupDirection === "col"
18922
- ? returnRange.map((col) => [col[index]])
18923
- : [returnRange[index]];
18929
+ ? _returnRange.map((col) => [col[index]])
18930
+ : [_returnRange[index]];
18924
18931
  }
18925
18932
  if (defaultValue === undefined) {
18926
18933
  return valueNotAvailable(searchKey);
@@ -28456,11 +28463,7 @@ function interpolateData(config, values, labels, newLabels) {
28456
28463
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
28457
28464
  return [];
28458
28465
  }
28459
- const labelMin = Math.min(...labels);
28460
- const labelMax = Math.max(...labels);
28461
- const labelRange = labelMax - labelMin;
28462
- const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
28463
- const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
28466
+ const { normalizedLabels, normalizedNewLabels } = normalizeLabels(labels, newLabels, config);
28464
28467
  try {
28465
28468
  switch (config.type) {
28466
28469
  case "polynomial": {
@@ -28505,6 +28508,30 @@ function interpolateData(config, values, labels, newLabels) {
28505
28508
  return newLabels.map((x) => ({ x, y: NaN }));
28506
28509
  }
28507
28510
  }
28511
+ function normalizeLabels(labels, newLabels, config) {
28512
+ let normalizedLabels = [];
28513
+ let normalizedNewLabels = [];
28514
+ if (config.type === "logarithmic") {
28515
+ // Logarithmic trends in charts are used to visualize proportional growth or
28516
+ // relative changes. Therefore, we change the normalization technique for
28517
+ // logarithmic trend lines for a better fit. The method used here is Max Absolute
28518
+ // Scaling. This Technique is ideal for data spanning several orders of magnitude,
28519
+ // as it balances differences between small and large values by compressing larger
28520
+ // values while preserving proportionality and ensuring all values are scaled relative
28521
+ // to the largest magnitude.
28522
+ const labelMax = Math.max(...labels.map(Math.abs));
28523
+ normalizedLabels = labels.map((l) => l / labelMax);
28524
+ normalizedNewLabels = newLabels.map((l) => l / labelMax);
28525
+ }
28526
+ else {
28527
+ const labelMax = Math.max(...labels);
28528
+ const labelMin = Math.min(...labels);
28529
+ const labelRange = labelMax - labelMin;
28530
+ normalizedLabels = labels.map((l) => (l - labelMax) / labelRange);
28531
+ normalizedNewLabels = newLabels.map((l) => (l - labelMax) / labelRange);
28532
+ }
28533
+ return { normalizedLabels, normalizedNewLabels };
28534
+ }
28508
28535
  function getChartAxisType(chart, labelRange, getters) {
28509
28536
  if (isDateChart(chart, labelRange, getters) && isLuxonTimeAdapterInstalled()) {
28510
28537
  return "time";
@@ -37172,7 +37199,7 @@ function dragAndDropBeyondTheViewport(env, cbMouseMove, cbMouseUp, only = false)
37172
37199
  }
37173
37200
  const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
37174
37201
  let { top, left, bottom, right } = getters.getActiveMainViewport();
37175
- let { scrollX, scrollY } = getters.getActiveSheetDOMScrollInfo();
37202
+ let { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
37176
37203
  const { xSplit, ySplit } = getters.getPaneDivisions(sheetId);
37177
37204
  let canEdgeScroll = false;
37178
37205
  let timeoutDelay = MAX_DELAY;
@@ -41345,6 +41372,16 @@ class StandaloneComposerStore extends AbstractComposerStore {
41345
41372
  }
41346
41373
  return providersDefinitions;
41347
41374
  }
41375
+ /**
41376
+ * Replace the current reference selected by the new one.
41377
+ * */
41378
+ getZoneReference(zone) {
41379
+ const res = super.getZoneReference(zone);
41380
+ if (this.args().defaultStatic) {
41381
+ return setXcToFixedReferenceType(res, "colrow");
41382
+ }
41383
+ return res;
41384
+ }
41348
41385
  getComposerContent() {
41349
41386
  if (this.editionMode === "inactive") {
41350
41387
  // References in the content might not be linked to the current active sheet
@@ -41410,6 +41447,7 @@ class StandaloneComposer extends Component {
41410
41447
  static props = {
41411
41448
  composerContent: { type: String, optional: true },
41412
41449
  defaultRangeSheetId: { type: String, optional: true },
41450
+ defaultStatic: { type: Boolean, optional: true },
41413
41451
  onConfirm: Function,
41414
41452
  contextualAutocomplete: { type: Object, optional: true },
41415
41453
  placeholder: { type: String, optional: true },
@@ -41420,6 +41458,7 @@ class StandaloneComposer extends Component {
41420
41458
  static components = { Composer };
41421
41459
  static defaultProps = {
41422
41460
  composerContent: "",
41461
+ defaultStatic: false,
41423
41462
  };
41424
41463
  composerFocusStore;
41425
41464
  standaloneComposerStore;
@@ -41430,6 +41469,7 @@ class StandaloneComposer extends Component {
41430
41469
  const standaloneComposerStore = useLocalStore(StandaloneComposerStore, () => ({
41431
41470
  onConfirm: this.props.onConfirm,
41432
41471
  content: this.props.composerContent,
41472
+ defaultStatic: this.props.defaultStatic ?? false,
41433
41473
  contextualAutocomplete: this.props.contextualAutocomplete,
41434
41474
  defaultRangeSheetId: this.props.defaultRangeSheetId,
41435
41475
  getContextualColoredSymbolToken: this.props.getContextualColoredSymbolToken,
@@ -42165,6 +42205,7 @@ class ConditionalFormattingEditor extends Component {
42165
42205
  },
42166
42206
  composerContent: this.state.rules.cellIs.values[valueIndex],
42167
42207
  placeholder: _t("Value or formula"),
42208
+ defaultStatic: true,
42168
42209
  invalid: isInvalid,
42169
42210
  class: "o-sidePanel-composer",
42170
42211
  defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
@@ -42183,6 +42224,7 @@ class ConditionalFormattingEditor extends Component {
42183
42224
  },
42184
42225
  composerContent: threshold.value || "",
42185
42226
  placeholder: _t("Formula"),
42227
+ defaultStatic: true,
42186
42228
  invalid: isInvalid,
42187
42229
  class: "o-sidePanel-composer",
42188
42230
  defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
@@ -42198,6 +42240,7 @@ class ConditionalFormattingEditor extends Component {
42198
42240
  },
42199
42241
  composerContent: inflection.value || "",
42200
42242
  placeholder: _t("Formula"),
42243
+ defaultStatic: true,
42201
42244
  invalid: isInvalid,
42202
42245
  class: "o-sidePanel-composer",
42203
42246
  defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
@@ -49806,7 +49849,7 @@ class GridAddRowsFooter extends Component {
49806
49849
  });
49807
49850
  this.props.focusGrid();
49808
49851
  // After adding new rows, scroll down to the new last row
49809
- const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
49852
+ const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
49810
49853
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
49811
49854
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
49812
49855
  offsetX: scrollX,
@@ -50064,7 +50107,7 @@ class GridOverlay extends Component {
50064
50107
  resizeObserver.disconnect();
50065
50108
  });
50066
50109
  useTouchMove(this.gridOverlay, this.props.onGridMoved, () => {
50067
- const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
50110
+ const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
50068
50111
  return scrollY > 0;
50069
50112
  });
50070
50113
  this.cellPopovers = useStore(CellPopoverStore);
@@ -50718,17 +50761,6 @@ class GridRenderer {
50718
50761
  get renderingLayers() {
50719
50762
  return ["Background", "Headers"];
50720
50763
  }
50721
- /**
50722
- * Get the offset of a header (see getColRowOffsetInViewport), adjusted with the header
50723
- * size (HEADER_HEIGHT and HEADER_WIDTH)
50724
- */
50725
- getHeaderOffset(dimension, start, index) {
50726
- let size = this.getters.getColRowOffsetInViewport(dimension, start, index);
50727
- if (!this.getters.isDashboard()) {
50728
- size += dimension === "ROW" ? HEADER_HEIGHT : HEADER_WIDTH;
50729
- }
50730
- return size;
50731
- }
50732
50764
  // ---------------------------------------------------------------------------
50733
50765
  // Grid rendering
50734
50766
  // ---------------------------------------------------------------------------
@@ -50736,11 +50768,10 @@ class GridRenderer {
50736
50768
  switch (layer) {
50737
50769
  case "Background":
50738
50770
  this.drawGlobalBackground(renderingContext);
50739
- for (const zone of this.getters.getAllActiveViewportsZones()) {
50771
+ for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
50740
50772
  const { ctx } = renderingContext;
50741
50773
  ctx.save();
50742
50774
  ctx.beginPath();
50743
- const rect = this.getters.getVisibleRect(zone);
50744
50775
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
50745
50776
  ctx.clip();
50746
50777
  const boxes = this.getGridBoxes(zone);
@@ -51016,10 +51047,8 @@ class GridRenderer {
51016
51047
  const { ctx, thinLineWidth } = renderingContext;
51017
51048
  const visibleCols = this.getters.getSheetViewVisibleCols();
51018
51049
  const left = visibleCols[0];
51019
- const right = visibleCols[visibleCols.length - 1];
51020
51050
  const visibleRows = this.getters.getSheetViewVisibleRows();
51021
51051
  const top = visibleRows[0];
51022
- const bottom = visibleRows[visibleRows.length - 1];
51023
51052
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
51024
51053
  const selection = this.getters.getSelectedZones();
51025
51054
  const selectedCols = getZonesCols(selection);
@@ -51035,7 +51064,7 @@ class GridRenderer {
51035
51064
  ctx.lineWidth = thinLineWidth;
51036
51065
  ctx.strokeStyle = "#333";
51037
51066
  // Columns headers background
51038
- for (let col = left; col <= right; col++) {
51067
+ for (const col of visibleCols) {
51039
51068
  const colZone = { left: col, right: col, top: 0, bottom: numberOfRows - 1 };
51040
51069
  const { x, width } = this.getters.getVisibleRect(colZone);
51041
51070
  const isColActive = activeCols.has(col);
@@ -51052,7 +51081,7 @@ class GridRenderer {
51052
51081
  ctx.fillRect(x, 0, width, HEADER_HEIGHT);
51053
51082
  }
51054
51083
  // Rows headers background
51055
- for (let row = top; row <= bottom; row++) {
51084
+ for (const row of visibleRows) {
51056
51085
  const rowZone = { top: row, bottom: row, left: 0, right: numberOfCols - 1 };
51057
51086
  const { y, height } = this.getters.getVisibleRect(rowZone);
51058
51087
  const isRowActive = activeRows.has(row);
@@ -51076,27 +51105,41 @@ class GridRenderer {
51076
51105
  ctx.lineTo(width, HEADER_HEIGHT);
51077
51106
  ctx.strokeStyle = HEADER_BORDER_COLOR;
51078
51107
  ctx.stroke();
51079
- ctx.beginPath();
51080
51108
  // column text + separator
51081
- for (const i of visibleCols) {
51082
- const colSize = this.getters.getColSize(sheetId, i);
51083
- const colName = numberToLetters(i);
51084
- ctx.fillStyle = activeCols.has(i) ? "#fff" : TEXT_HEADER_COLOR;
51085
- let colStart = this.getHeaderOffset("COL", left, i);
51109
+ for (const col of visibleCols) {
51110
+ const colName = numberToLetters(col);
51111
+ ctx.fillStyle = activeCols.has(col) ? "#fff" : TEXT_HEADER_COLOR;
51112
+ const zone = { left: col, right: col, top: top, bottom: top };
51113
+ const { x: colStart, width: colSize } = this.getters.getRect(zone);
51114
+ const { x, width } = this.getters.getVisibleRect(zone);
51115
+ ctx.save();
51116
+ ctx.beginPath();
51117
+ ctx.rect(x, 0, width, HEADER_HEIGHT);
51118
+ ctx.clip();
51086
51119
  ctx.fillText(colName, colStart + colSize / 2, HEADER_HEIGHT / 2);
51120
+ ctx.restore();
51121
+ ctx.beginPath();
51087
51122
  ctx.moveTo(colStart + colSize, 0);
51088
51123
  ctx.lineTo(colStart + colSize, HEADER_HEIGHT);
51124
+ ctx.stroke();
51089
51125
  }
51090
51126
  // row text + separator
51091
- for (const i of visibleRows) {
51092
- const rowSize = this.getters.getRowSize(sheetId, i);
51093
- ctx.fillStyle = activeRows.has(i) ? "#fff" : TEXT_HEADER_COLOR;
51094
- let rowStart = this.getHeaderOffset("ROW", top, i);
51095
- ctx.fillText(String(i + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
51127
+ for (const row of visibleRows) {
51128
+ ctx.fillStyle = activeRows.has(row) ? "#fff" : TEXT_HEADER_COLOR;
51129
+ const zone = { top: row, bottom: row, left: left, right: left };
51130
+ const { y: rowStart, height: rowSize } = this.getters.getRect(zone);
51131
+ const { y, height } = this.getters.getVisibleRect(zone);
51132
+ ctx.save();
51133
+ ctx.beginPath();
51134
+ ctx.rect(0, y, HEADER_WIDTH, height);
51135
+ ctx.clip();
51136
+ ctx.fillText(String(row + 1), HEADER_WIDTH / 2, rowStart + rowSize / 2);
51137
+ ctx.restore();
51138
+ ctx.beginPath();
51096
51139
  ctx.moveTo(0, rowStart + rowSize);
51097
51140
  ctx.lineTo(HEADER_WIDTH, rowStart + rowSize);
51141
+ ctx.stroke();
51098
51142
  }
51099
- ctx.stroke();
51100
51143
  }
51101
51144
  drawFrozenPanesHeaders(renderingContext) {
51102
51145
  const { ctx, thinLineWidth } = renderingContext;
@@ -51399,6 +51442,9 @@ function useGridDrawing(refName, model, canvasSize) {
51399
51442
  canvas.width = width * dpr;
51400
51443
  canvas.height = height * dpr;
51401
51444
  canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
51445
+ if (width === 0 || height === 0) {
51446
+ return;
51447
+ }
51402
51448
  // Imagine each pixel as a large square. The whole-number coordinates (0, 1, 2…)
51403
51449
  // are the edges of the squares. If you draw a one-unit-wide line between whole-number
51404
51450
  // coordinates, it will overlap opposite sides of the pixel square, and the resulting
@@ -51742,7 +51788,7 @@ class HorizontalScrollBar extends Component {
51742
51788
  leftOffset: 0,
51743
51789
  };
51744
51790
  get offset() {
51745
- return this.env.model.getters.getActiveSheetDOMScrollInfo().scrollX;
51791
+ return this.env.model.getters.getActiveSheetScrollInfo().scrollX;
51746
51792
  }
51747
51793
  get width() {
51748
51794
  return this.env.model.getters.getMainViewportRect().width;
@@ -51761,7 +51807,7 @@ class HorizontalScrollBar extends Component {
51761
51807
  };
51762
51808
  }
51763
51809
  onScroll(offset) {
51764
- const { scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
51810
+ const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
51765
51811
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
51766
51812
  offsetX: offset,
51767
51813
  offsetY: scrollY, // offsetY is the same
@@ -51787,7 +51833,7 @@ class VerticalScrollBar extends Component {
51787
51833
  topOffset: 0,
51788
51834
  };
51789
51835
  get offset() {
51790
- return this.env.model.getters.getActiveSheetDOMScrollInfo().scrollY;
51836
+ return this.env.model.getters.getActiveSheetScrollInfo().scrollY;
51791
51837
  }
51792
51838
  get height() {
51793
51839
  return this.env.model.getters.getMainViewportRect().height;
@@ -51806,7 +51852,7 @@ class VerticalScrollBar extends Component {
51806
51852
  };
51807
51853
  }
51808
51854
  onScroll(offset) {
51809
- const { scrollX } = this.env.model.getters.getActiveSheetDOMScrollInfo();
51855
+ const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
51810
51856
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
51811
51857
  offsetX: scrollX, // offsetX is the same
51812
51858
  offsetY: offset,
@@ -52277,7 +52323,7 @@ class Grid extends Component {
52277
52323
  });
52278
52324
  }
52279
52325
  moveCanvas(deltaX, deltaY) {
52280
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
52326
+ const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
52281
52327
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
52282
52328
  offsetX: scrollX + deltaX,
52283
52329
  offsetY: scrollY + deltaY,
@@ -67158,8 +67204,6 @@ class InternalViewport {
67158
67204
  right;
67159
67205
  offsetX;
67160
67206
  offsetY;
67161
- offsetScrollbarX;
67162
- offsetScrollbarY;
67163
67207
  canScrollVertically;
67164
67208
  canScrollHorizontally;
67165
67209
  viewportWidth;
@@ -67170,10 +67214,17 @@ class InternalViewport {
67170
67214
  this.getters = getters;
67171
67215
  this.sheetId = sheetId;
67172
67216
  this.boundaries = boundaries;
67173
- this.viewportWidth = sizeInGrid.width;
67174
- this.viewportHeight = sizeInGrid.height;
67175
- this.offsetScrollbarX = offsets.x;
67176
- this.offsetScrollbarY = offsets.y;
67217
+ if (sizeInGrid.width < 0 || sizeInGrid.height < 0) {
67218
+ throw new Error("Viewport size cannot be negative");
67219
+ }
67220
+ this.viewportWidth = sizeInGrid.height && sizeInGrid.width;
67221
+ this.viewportHeight = sizeInGrid.width && sizeInGrid.height;
67222
+ this.top = boundaries.top;
67223
+ this.bottom = boundaries.bottom;
67224
+ this.left = boundaries.left;
67225
+ this.right = boundaries.right;
67226
+ this.offsetX = offsets.x;
67227
+ this.offsetY = offsets.y;
67177
67228
  this.canScrollVertically = options.canScrollVertically;
67178
67229
  this.canScrollHorizontally = options.canScrollHorizontally;
67179
67230
  this.offsetCorrectionX = this.getters.getColDimensions(this.sheetId, this.boundaries.left).start;
@@ -67214,9 +67265,9 @@ class InternalViewport {
67214
67265
  Math.min(topRowSize, this.viewportHeight - lastRowSize) // Add pixels that allows the snapping at maximum vertical scroll
67215
67266
  );
67216
67267
  height = Math.max(height, this.viewportHeight); // if the viewport grid size is smaller than its client height, return client height
67217
- }
67218
- if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
67219
- height += FOOTER_HEIGHT;
67268
+ if (lastRowEnd + FOOTER_HEIGHT > height && !this.getters.isReadonly()) {
67269
+ height += FOOTER_HEIGHT;
67270
+ }
67220
67271
  }
67221
67272
  return { width, height };
67222
67273
  }
@@ -67229,7 +67280,7 @@ class InternalViewport {
67229
67280
  if (x < this.offsetCorrectionX || x > this.offsetCorrectionX + this.viewportWidth) {
67230
67281
  return -1;
67231
67282
  }
67232
- return this.searchHeaderIndex("COL", x - this.offsetCorrectionX, this.left);
67283
+ return this.searchHeaderIndex("COL", x - this.offsetCorrectionX + this.snapCorrection.x, this.left);
67233
67284
  }
67234
67285
  /**
67235
67286
  * Return the index of a row given an offset y, based on the pane top
@@ -67240,7 +67291,7 @@ class InternalViewport {
67240
67291
  if (y < this.offsetCorrectionY || y > this.offsetCorrectionY + this.viewportHeight) {
67241
67292
  return -1;
67242
67293
  }
67243
- return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY, this.top);
67294
+ return this.searchHeaderIndex("ROW", y - this.offsetCorrectionY + this.snapCorrection.y, this.top);
67244
67295
  }
67245
67296
  /**
67246
67297
  * This function will make sure that the provided cell position (or current selected position) is part of
@@ -67260,55 +67311,29 @@ class InternalViewport {
67260
67311
  }
67261
67312
  adjustPositionX(targetCol) {
67262
67313
  const sheetId = this.sheetId;
67263
- const { end } = this.getters.getColDimensions(sheetId, targetCol);
67264
- if (this.offsetX + this.offsetCorrectionX + this.viewportWidth < end) {
67265
- const maxCol = this.getters.getNumberCols(sheetId);
67266
- let finalTarget = targetCol;
67267
- while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget < maxCol) {
67268
- finalTarget++;
67269
- }
67270
- const finalTargetEnd = this.getters.getColDimensions(sheetId, finalTarget).end;
67271
- const startIndex = this.searchHeaderIndex("COL", finalTargetEnd - this.viewportWidth - this.offsetCorrectionX, this.boundaries.left);
67272
- this.offsetScrollbarX =
67273
- this.getters.getColDimensions(sheetId, startIndex).end - this.offsetCorrectionX;
67274
- }
67275
- else if (this.left > targetCol) {
67276
- let finalTarget = targetCol;
67277
- while (this.getters.isColHidden(sheetId, finalTarget) && finalTarget > 0) {
67278
- finalTarget--;
67279
- }
67280
- this.offsetScrollbarX =
67281
- this.getters.getColDimensions(sheetId, finalTarget).start - this.offsetCorrectionX;
67314
+ const { start, end } = this.getters.getColDimensions(sheetId, targetCol);
67315
+ if (this.offsetX + this.viewportWidth + this.offsetCorrectionX < end) {
67316
+ this.offsetX = end - this.viewportWidth;
67317
+ }
67318
+ else if (this.offsetX + this.offsetCorrectionX > start) {
67319
+ this.offsetX = start - this.offsetCorrectionX;
67282
67320
  }
67283
67321
  this.adjustViewportZoneX();
67284
67322
  }
67285
67323
  adjustPositionY(targetRow) {
67286
67324
  const sheetId = this.sheetId;
67287
- const { end } = this.getters.getRowDimensions(sheetId, targetRow);
67325
+ const { start, end } = this.getters.getRowDimensions(sheetId, targetRow);
67288
67326
  if (this.offsetY + this.viewportHeight + this.offsetCorrectionY < end) {
67289
- const maxRow = this.getters.getNumberRows(sheetId);
67290
- let finalTarget = targetRow;
67291
- while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget < maxRow) {
67292
- finalTarget++;
67293
- }
67294
- const finalTargetEnd = this.getters.getRowDimensions(sheetId, finalTarget).end;
67295
- const startIndex = this.searchHeaderIndex("ROW", finalTargetEnd - this.viewportHeight - this.offsetCorrectionY, this.boundaries.top);
67296
- this.offsetScrollbarY =
67297
- this.getters.getRowDimensions(sheetId, startIndex).end - this.offsetCorrectionY;
67327
+ this.offsetY = end - this.viewportHeight;
67298
67328
  }
67299
- else if (this.top > targetRow) {
67300
- let finalTarget = targetRow;
67301
- while (this.getters.isRowHidden(sheetId, finalTarget) && finalTarget > 0) {
67302
- finalTarget--;
67303
- }
67304
- this.offsetScrollbarY =
67305
- this.getters.getRowDimensions(sheetId, finalTarget).start - this.offsetCorrectionY;
67329
+ else if (this.offsetY + this.offsetCorrectionY > start) {
67330
+ this.offsetY = start - this.offsetCorrectionY;
67306
67331
  }
67307
67332
  this.adjustViewportZoneY();
67308
67333
  }
67309
67334
  willNewOffsetScrollViewport(offsetX, offsetY) {
67310
- return ((this.canScrollHorizontally && this.offsetScrollbarX !== offsetX) ||
67311
- (this.canScrollVertically && this.offsetScrollbarY !== offsetY));
67335
+ return ((this.canScrollHorizontally && this.offsetX !== offsetX) ||
67336
+ (this.canScrollVertically && this.offsetY !== offsetY));
67312
67337
  }
67313
67338
  setViewportOffset(offsetX, offsetY) {
67314
67339
  this.setViewportOffsetX(offsetX);
@@ -67325,11 +67350,19 @@ class InternalViewport {
67325
67350
  */
67326
67351
  getVisibleRect(zone) {
67327
67352
  const targetZone = intersection(zone, this);
67353
+ const scrollDeltaX = this.snapCorrection.x;
67354
+ const scrollDeltaY = this.snapCorrection.y;
67328
67355
  if (targetZone) {
67329
- const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
67330
- const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
67331
- const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
67332
- const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
67356
+ const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) +
67357
+ this.offsetCorrectionX -
67358
+ (this.left !== targetZone.left ? scrollDeltaX : 0);
67359
+ const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) +
67360
+ this.offsetCorrectionY -
67361
+ (this.top !== targetZone.top ? scrollDeltaY : 0);
67362
+ const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1) -
67363
+ (this.left === targetZone.left ? scrollDeltaX : 0), this.viewportWidth);
67364
+ const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1) -
67365
+ (this.top === targetZone.top ? scrollDeltaY : 0), this.viewportHeight);
67333
67366
  return { x, y, width, height };
67334
67367
  }
67335
67368
  return undefined;
@@ -67341,12 +67374,14 @@ class InternalViewport {
67341
67374
  */
67342
67375
  getFullRect(zone) {
67343
67376
  const targetZone = intersection(zone, this);
67377
+ const scrollDeltaX = this.snapCorrection.x;
67378
+ const scrollDeltaY = this.snapCorrection.y;
67344
67379
  if (targetZone) {
67345
67380
  const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
67346
67381
  const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
67347
67382
  const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
67348
67383
  const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
67349
- return { x, y, width, height };
67384
+ return { x: x - scrollDeltaX, y: y - scrollDeltaY, width, height };
67350
67385
  }
67351
67386
  return undefined;
67352
67387
  }
@@ -67357,6 +67392,9 @@ class InternalViewport {
67357
67392
  !this.getters.isRowHidden(this.sheetId, row));
67358
67393
  }
67359
67394
  searchHeaderIndex(dimension, position, startIndex = 0) {
67395
+ if (this.viewportWidth <= 0 || this.viewportHeight <= 0) {
67396
+ return -1;
67397
+ }
67360
67398
  const sheetId = this.sheetId;
67361
67399
  const headers = this.getters.getNumberHeaders(sheetId, dimension);
67362
67400
  // using a binary search:
@@ -67382,36 +67420,36 @@ class InternalViewport {
67382
67420
  if (!this.canScrollHorizontally) {
67383
67421
  return;
67384
67422
  }
67385
- this.offsetScrollbarX = offsetX;
67423
+ this.offsetX = offsetX;
67386
67424
  this.adjustViewportZoneX();
67387
67425
  }
67388
67426
  setViewportOffsetY(offsetY) {
67389
67427
  if (!this.canScrollVertically) {
67390
67428
  return;
67391
67429
  }
67392
- this.offsetScrollbarY = offsetY;
67430
+ this.offsetY = offsetY;
67393
67431
  this.adjustViewportZoneY();
67394
67432
  }
67395
67433
  /** Corrects the viewport's horizontal offset based on the current structure
67396
- * To make sure that at least on column is visible inside the viewport.
67434
+ * To make sure that at least one column is visible inside the viewport.
67397
67435
  */
67398
67436
  adjustViewportOffsetX() {
67399
67437
  if (this.canScrollHorizontally) {
67400
67438
  const { width: viewportWidth } = this.getMaxSize();
67401
- if (this.viewportWidth + this.offsetScrollbarX > viewportWidth) {
67402
- this.offsetScrollbarX = Math.max(0, viewportWidth - this.viewportWidth);
67439
+ if (this.viewportWidth + this.offsetX > viewportWidth) {
67440
+ this.offsetX = Math.max(0, viewportWidth - this.viewportWidth);
67403
67441
  }
67404
67442
  }
67405
67443
  this.adjustViewportZoneX();
67406
67444
  }
67407
67445
  /** Corrects the viewport's vertical offset based on the current structure
67408
- * To make sure that at least on row is visible inside the viewport.
67446
+ * To make sure that at least one row is visible inside the viewport.
67409
67447
  */
67410
67448
  adjustViewportOffsetY() {
67411
67449
  if (this.canScrollVertically) {
67412
67450
  const { height: paneHeight } = this.getMaxSize();
67413
- if (this.viewportHeight + this.offsetScrollbarY > paneHeight) {
67414
- this.offsetScrollbarY = Math.max(0, paneHeight - this.viewportHeight);
67451
+ if (this.viewportHeight + this.offsetY > paneHeight) {
67452
+ this.offsetY = Math.max(0, paneHeight - this.viewportHeight);
67415
67453
  }
67416
67454
  }
67417
67455
  this.adjustViewportZoneY();
@@ -67419,34 +67457,47 @@ class InternalViewport {
67419
67457
  /** Updates the pane zone and snapped offset based on its horizontal
67420
67458
  * offset (will find Left) and its width (will find Right) */
67421
67459
  adjustViewportZoneX() {
67422
- const sheetId = this.sheetId;
67423
- this.left = this.searchHeaderIndex("COL", this.offsetScrollbarX, this.boundaries.left);
67424
- this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL", this.viewportWidth, this.left));
67460
+ this.left = this.searchHeaderIndex("COL", this.offsetX, this.boundaries.left);
67461
+ this.right = Math.min(this.boundaries.right, this.searchHeaderIndex("COL",
67462
+ // if we hit the border of two cells, we want to match the previous
67463
+ Math.max(this.viewportWidth + this.snapCorrection.x - 0.1), this.left));
67464
+ if (!this.viewportWidth) {
67465
+ return;
67466
+ }
67425
67467
  if (this.left === -1) {
67426
67468
  this.left = this.boundaries.left;
67427
67469
  }
67428
67470
  if (this.right === -1) {
67429
- this.right = this.getters.getNumberCols(sheetId) - 1;
67471
+ this.right = this.boundaries.right;
67430
67472
  }
67431
- this.offsetX =
67432
- this.getters.getColDimensions(sheetId, this.left).start -
67433
- this.getters.getColDimensions(sheetId, this.boundaries.left).start;
67434
67473
  }
67435
67474
  /** Updates the pane zone and snapped offset based on its vertical
67436
67475
  * offset (will find Top) and its width (will find Bottom) */
67437
67476
  adjustViewportZoneY() {
67438
- const sheetId = this.sheetId;
67439
- this.top = this.searchHeaderIndex("ROW", this.offsetScrollbarY, this.boundaries.top);
67440
- this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW", this.viewportHeight, this.top));
67477
+ this.top = this.searchHeaderIndex("ROW", this.offsetY, this.boundaries.top);
67478
+ this.bottom = Math.min(this.boundaries.bottom, this.searchHeaderIndex("ROW",
67479
+ // if we hit the border of two cells, we want to match the previous
67480
+ Math.max(this.viewportHeight + this.snapCorrection.y - 0.1, 0), this.top));
67481
+ if (!this.viewportHeight) {
67482
+ return;
67483
+ }
67441
67484
  if (this.top === -1) {
67442
67485
  this.top = this.boundaries.top;
67443
67486
  }
67444
67487
  if (this.bottom === -1) {
67445
- this.bottom = this.getters.getNumberRows(sheetId) - 1;
67488
+ this.bottom = this.boundaries.bottom;
67446
67489
  }
67447
- this.offsetY =
67448
- this.getters.getRowDimensions(sheetId, this.top).start -
67449
- this.getters.getRowDimensions(sheetId, this.boundaries.top).start;
67490
+ }
67491
+ /** represents the part of the header on the topLeft that could be partially
67492
+ * hidden due to the scroll
67493
+ * */
67494
+ get snapCorrection() {
67495
+ return {
67496
+ x: Math.abs(this.offsetX -
67497
+ this.getters.getColRowOffset("COL", this.boundaries.left, Math.max(0, this.left))),
67498
+ y: Math.abs(this.offsetY -
67499
+ this.getters.getColRowOffset("ROW", this.boundaries.top, Math.max(0, this.top))),
67500
+ };
67450
67501
  }
67451
67502
  }
67452
67503
 
@@ -67509,14 +67560,13 @@ class SheetViewPlugin extends UIPlugin {
67509
67560
  "getColRowOffsetInViewport",
67510
67561
  "getMainViewportCoordinates",
67511
67562
  "getActiveSheetScrollInfo",
67512
- "getActiveSheetDOMScrollInfo",
67513
67563
  "getSheetViewVisibleCols",
67514
67564
  "getSheetViewVisibleRows",
67515
67565
  "getFrozenSheetViewRatio",
67516
67566
  "isPixelPositionVisible",
67517
67567
  "getColDimensionsInViewport",
67518
67568
  "getRowDimensionsInViewport",
67519
- "getAllActiveViewportsZones",
67569
+ "getAllActiveViewportsZonesAndRect",
67520
67570
  "getRect",
67521
67571
  ];
67522
67572
  viewports = {};
@@ -67719,8 +67769,8 @@ class SheetViewPlugin extends UIPlugin {
67719
67769
  return this.getMainViewport(sheetId);
67720
67770
  }
67721
67771
  /**
67722
- * Return the scroll info of the active sheet, ie. the offset between the viewport left/top side and
67723
- * the grid left/top side, snapped to the columns/rows.
67772
+ * Return the DOM scroll info of the active sheet, ie. the offset between the viewport left/top side and
67773
+ * the grid left/top side, corresponding to the scroll of the scrollbars and not snapped to the grid.
67724
67774
  */
67725
67775
  getActiveSheetScrollInfo() {
67726
67776
  const sheetId = this.getters.getActiveSheetId();
@@ -67730,28 +67780,16 @@ class SheetViewPlugin extends UIPlugin {
67730
67780
  scrollY: viewport.offsetY,
67731
67781
  };
67732
67782
  }
67733
- /**
67734
- * Return the DOM scroll info of the active sheet, ie. the offset between the viewport left/top side and
67735
- * the grid left/top side, corresponding to the scroll of the scrollbars and not snapped to the grid.
67736
- */
67737
- getActiveSheetDOMScrollInfo() {
67738
- const sheetId = this.getters.getActiveSheetId();
67739
- const viewport = this.getMainInternalViewport(sheetId);
67740
- return {
67741
- scrollX: viewport.offsetScrollbarX,
67742
- scrollY: viewport.offsetScrollbarY,
67743
- };
67744
- }
67745
67783
  getSheetViewVisibleCols() {
67746
67784
  const sheetId = this.getters.getActiveSheetId();
67747
67785
  const viewports = this.getSubViewports(sheetId);
67748
67786
  //TODO ake another commit to eimprove this
67749
- return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => !this.getters.isHeaderHidden(sheetId, "COL", col));
67787
+ return [...new Set(viewports.map((v) => range(v.left, v.right + 1)).flat())].filter((col) => col >= 0 && !this.getters.isHeaderHidden(sheetId, "COL", col));
67750
67788
  }
67751
67789
  getSheetViewVisibleRows() {
67752
67790
  const sheetId = this.getters.getActiveSheetId();
67753
67791
  const viewports = this.getSubViewports(sheetId);
67754
- return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => !this.getters.isHeaderHidden(sheetId, "ROW", row));
67792
+ return [...new Set(viewports.map((v) => range(v.top, v.bottom + 1)).flat())].filter((row) => row >= 0 && !this.getters.isHeaderHidden(sheetId, "ROW", row));
67755
67793
  }
67756
67794
  /**
67757
67795
  * Get the positions of all the cells that are visible in the viewport, taking merges into account.
@@ -67794,19 +67832,19 @@ class SheetViewPlugin extends UIPlugin {
67794
67832
  maxOffsetY: Math.max(0, height - viewport.viewportHeight + 1),
67795
67833
  };
67796
67834
  }
67797
- getColRowOffsetInViewport(dimension, referenceIndex, index) {
67798
- const sheetId = this.getters.getActiveSheetId();
67799
- const visibleCols = this.getters.getSheetViewVisibleCols();
67800
- const visibleRows = this.getters.getSheetViewVisibleRows();
67801
- if (index < referenceIndex) {
67802
- return -this.getColRowOffsetInViewport(dimension, index, referenceIndex);
67835
+ getColRowOffsetInViewport(dimension, referenceHeaderIndex, targetHeaderIndex) {
67836
+ if (targetHeaderIndex < referenceHeaderIndex) {
67837
+ return -this.getColRowOffsetInViewport(dimension, targetHeaderIndex, referenceHeaderIndex);
67803
67838
  }
67839
+ const sheetId = this.getters.getActiveSheetId();
67840
+ const visibleHeaders = dimension === "COL"
67841
+ ? this.getters.getSheetViewVisibleCols()
67842
+ : this.getters.getSheetViewVisibleRows();
67843
+ const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
67844
+ const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
67845
+ const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
67804
67846
  let offset = 0;
67805
- const visibleIndexes = dimension === "COL" ? visibleCols : visibleRows;
67806
- for (let i = referenceIndex; i < index; i++) {
67807
- if (!visibleIndexes.includes(i)) {
67808
- continue;
67809
- }
67847
+ for (const i of relevantIndexes) {
67810
67848
  offset += this.getters.getHeaderSize(sheetId, dimension, i);
67811
67849
  }
67812
67850
  return offset;
@@ -67853,7 +67891,7 @@ class SheetViewPlugin extends UIPlugin {
67853
67891
  }
67854
67892
  return { canEdgeScroll, direction, delay };
67855
67893
  }
67856
- getEdgeScrollRow(y, previousY, tartingY) {
67894
+ getEdgeScrollRow(y, previousY, startingY) {
67857
67895
  let canEdgeScroll = false;
67858
67896
  let direction = 0;
67859
67897
  let delay = 0;
@@ -67874,7 +67912,7 @@ class SheetViewPlugin extends UIPlugin {
67874
67912
  delay = scrollDelay(y - height);
67875
67913
  direction = 1;
67876
67914
  }
67877
- else if (y < offsetCorrectionY && tartingY >= offsetCorrectionY && currentOffsetY > 0) {
67915
+ else if (y < offsetCorrectionY && startingY >= offsetCorrectionY && currentOffsetY > 0) {
67878
67916
  // 2
67879
67917
  canEdgeScroll = true;
67880
67918
  delay = scrollDelay(offsetCorrectionY - y);
@@ -67900,13 +67938,7 @@ class SheetViewPlugin extends UIPlugin {
67900
67938
  */
67901
67939
  getVisibleRectWithoutHeaders(zone) {
67902
67940
  const sheetId = this.getters.getActiveSheetId();
67903
- const viewportRects = this.getSubViewports(sheetId)
67904
- .map((viewport) => viewport.getVisibleRect(zone))
67905
- .filter(isDefined);
67906
- if (viewportRects.length === 0) {
67907
- return { x: 0, y: 0, width: 0, height: 0 };
67908
- }
67909
- return this.recomposeRect(viewportRects);
67941
+ return this.mapViewportsToRect(sheetId, (viewport) => viewport.getVisibleRect(zone));
67910
67942
  }
67911
67943
  /**
67912
67944
  * Computes the actual size and position (:Rect) of the zone on the canvas
@@ -67914,13 +67946,7 @@ class SheetViewPlugin extends UIPlugin {
67914
67946
  */
67915
67947
  getRect(zone) {
67916
67948
  const sheetId = this.getters.getActiveSheetId();
67917
- const viewportRects = this.getSubViewports(sheetId)
67918
- .map((viewport) => viewport.getFullRect(zone))
67919
- .filter(isDefined);
67920
- if (viewportRects.length === 0) {
67921
- return { x: 0, y: 0, width: 0, height: 0 };
67922
- }
67923
- const rect = this.recomposeRect(viewportRects);
67949
+ const rect = this.mapViewportsToRect(sheetId, (viewport) => viewport.getFullRect(zone));
67924
67950
  return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
67925
67951
  }
67926
67952
  /**
@@ -67940,34 +67966,43 @@ class SheetViewPlugin extends UIPlugin {
67940
67966
  * column of the current viewport
67941
67967
  */
67942
67968
  getColDimensionsInViewport(sheetId, col) {
67943
- const left = largeMin(this.getters.getSheetViewVisibleCols());
67944
- const start = this.getters.getColRowOffsetInViewport("COL", left, col);
67945
- const size = this.getters.getColSize(sheetId, col);
67946
- const isColHidden = this.getters.isColHidden(sheetId, col);
67947
- return {
67948
- start,
67949
- size: size,
67950
- end: start + (isColHidden ? 0 : size),
67969
+ const zone = {
67970
+ left: col,
67971
+ right: col,
67972
+ top: 0,
67973
+ bottom: this.getters.getNumberRows(sheetId) - 1,
67951
67974
  };
67975
+ const { x, width } = this.getVisibleRect(zone);
67976
+ const start = x - this.gridOffsetX;
67977
+ return { start, size: width, end: start + width };
67952
67978
  }
67953
67979
  /**
67954
67980
  * Returns the size, start and end coordinates of a row relative to the top row
67955
67981
  * of the current viewport
67956
67982
  */
67957
67983
  getRowDimensionsInViewport(sheetId, row) {
67958
- const top = largeMin(this.getters.getSheetViewVisibleRows());
67959
- const start = this.getters.getColRowOffsetInViewport("ROW", top, row);
67960
- const size = this.getters.getRowSize(sheetId, row);
67961
- const isRowHidden = this.getters.isRowHidden(sheetId, row);
67962
- return {
67963
- start,
67964
- size: size,
67965
- end: start + (isRowHidden ? 0 : size),
67984
+ const zone = {
67985
+ left: 0,
67986
+ right: this.getters.getNumberCols(sheetId) - 1,
67987
+ top: row,
67988
+ bottom: row,
67966
67989
  };
67990
+ const { y, height } = this.getVisibleRect(zone);
67991
+ const start = y - this.gridOffsetY;
67992
+ return { start, size: height, end: start + height };
67967
67993
  }
67968
- getAllActiveViewportsZones() {
67994
+ getAllActiveViewportsZonesAndRect() {
67969
67995
  const sheetId = this.getters.getActiveSheetId();
67970
- return this.getSubViewports(sheetId);
67996
+ return this.getSubViewports(sheetId).map((viewport) => {
67997
+ return {
67998
+ zone: viewport,
67999
+ rect: {
68000
+ x: viewport.offsetCorrectionX + this.gridOffsetX,
68001
+ y: viewport.offsetCorrectionY + this.gridOffsetY,
68002
+ ...viewport.getMaxSize(),
68003
+ },
68004
+ };
68005
+ });
67971
68006
  }
67972
68007
  // ---------------------------------------------------------------------------
67973
68008
  // Private
@@ -68026,12 +68061,11 @@ class SheetViewPlugin extends UIPlugin {
68026
68061
  }
68027
68062
  /** gets rid of deprecated sheetIds */
68028
68063
  cleanViewports() {
68029
- const sheetIds = this.getters.getSheetIds();
68030
- for (let sheetId of Object.keys(this.viewports)) {
68031
- if (!sheetIds.includes(sheetId)) {
68032
- delete this.viewports[sheetId];
68033
- }
68064
+ const newViewport = {};
68065
+ for (const sheetId of this.getters.getSheetIds()) {
68066
+ newViewport[sheetId] = this.viewports[sheetId];
68034
68067
  }
68068
+ this.viewports = newViewport;
68035
68069
  }
68036
68070
  resizeSheetView(height, width, gridOffsetX = 0, gridOffsetY = 0) {
68037
68071
  this.sheetViewHeight = height;
@@ -68041,7 +68075,7 @@ class SheetViewPlugin extends UIPlugin {
68041
68075
  this.recomputeViewports();
68042
68076
  }
68043
68077
  recomputeViewports() {
68044
- for (let sheetId of Object.keys(this.viewports)) {
68078
+ for (const sheetId of this.getters.getSheetIds()) {
68045
68079
  this.resetViewports(sheetId);
68046
68080
  }
68047
68081
  }
@@ -68052,8 +68086,8 @@ class SheetViewPlugin extends UIPlugin {
68052
68086
  }
68053
68087
  getViewportOffset(sheetId) {
68054
68088
  return {
68055
- x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0,
68056
- y: this.viewports[sheetId]?.bottomRight.offsetScrollbarY || 0,
68089
+ x: this.viewports[sheetId]?.bottomRight.offsetX || 0,
68090
+ y: this.viewports[sheetId]?.bottomRight.offsetY || 0,
68057
68091
  };
68058
68092
  }
68059
68093
  resetViewports(sheetId) {
@@ -68063,8 +68097,10 @@ class SheetViewPlugin extends UIPlugin {
68063
68097
  const { xSplit, ySplit } = this.getters.getPaneDivisions(sheetId);
68064
68098
  const nCols = this.getters.getNumberCols(sheetId);
68065
68099
  const nRows = this.getters.getNumberRows(sheetId);
68066
- const colOffset = this.getters.getColRowOffset("COL", 0, xSplit, sheetId);
68067
- const rowOffset = this.getters.getColRowOffset("ROW", 0, ySplit, sheetId);
68100
+ const colOffset = Math.min(this.getters.getColRowOffset("COL", 0, xSplit, sheetId), this.sheetViewWidth);
68101
+ const rowOffset = Math.min(this.getters.getColRowOffset("ROW", 0, ySplit, sheetId), this.sheetViewHeight);
68102
+ const unfrozenWidth = Math.max(this.sheetViewWidth - colOffset, 0);
68103
+ const unfrozenHeight = Math.max(this.sheetViewHeight - rowOffset, 0);
68068
68104
  const { xRatio, yRatio } = this.getFrozenSheetViewRatio(sheetId);
68069
68105
  const canScrollHorizontally = xRatio < 1.0;
68070
68106
  const canScrollVertically = yRatio < 1.0;
@@ -68075,14 +68111,14 @@ class SheetViewPlugin extends UIPlugin {
68075
68111
  new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: 0, bottom: ySplit - 1 }, { width: colOffset, height: rowOffset }, { canScrollHorizontally: false, canScrollVertically: false }, { x: 0, y: 0 })) ||
68076
68112
  undefined,
68077
68113
  topRight: (ySplit &&
68078
- new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: this.sheetViewWidth - colOffset, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) ||
68114
+ new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: 0, bottom: ySplit - 1 }, { width: unfrozenWidth, height: rowOffset }, { canScrollHorizontally, canScrollVertically: false }, { x: canScrollHorizontally ? previousOffset.x : 0, y: 0 })) ||
68079
68115
  undefined,
68080
68116
  bottomLeft: (xSplit &&
68081
- new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: this.sheetViewHeight - rowOffset }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) ||
68117
+ new InternalViewport(this.getters, sheetId, { left: 0, right: xSplit - 1, top: ySplit, bottom: nRows - 1 }, { width: colOffset, height: unfrozenHeight }, { canScrollHorizontally: false, canScrollVertically }, { x: 0, y: canScrollVertically ? previousOffset.y : 0 })) ||
68082
68118
  undefined,
68083
68119
  bottomRight: new InternalViewport(this.getters, sheetId, { left: xSplit, right: nCols - 1, top: ySplit, bottom: nRows - 1 }, {
68084
- width: this.sheetViewWidth - colOffset,
68085
- height: this.sheetViewHeight - rowOffset,
68120
+ width: unfrozenWidth,
68121
+ height: unfrozenHeight,
68086
68122
  }, { canScrollHorizontally, canScrollVertically }, {
68087
68123
  x: canScrollHorizontally ? previousOffset.x : 0,
68088
68124
  y: canScrollVertically ? previousOffset.y : 0,
@@ -68159,12 +68195,26 @@ class SheetViewPlugin extends UIPlugin {
68159
68195
  const height = this.sheetViewHeight + this.gridOffsetY;
68160
68196
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
68161
68197
  }
68162
- recomposeRect(viewportRects) {
68163
- const x = Math.min(...viewportRects.map((rect) => rect.x));
68164
- const y = Math.min(...viewportRects.map((rect) => rect.y));
68165
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
68166
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
68167
- return { x, y, width, height };
68198
+ mapViewportsToRect(sheetId, rectCallBack) {
68199
+ let x = Infinity;
68200
+ let y = Infinity;
68201
+ let width = 0;
68202
+ let height = 0;
68203
+ let hasViewports = false;
68204
+ for (const viewport of this.getSubViewports(sheetId)) {
68205
+ const rect = rectCallBack(viewport);
68206
+ if (rect) {
68207
+ hasViewports = true;
68208
+ x = Math.min(x, rect.x);
68209
+ y = Math.min(y, rect.y);
68210
+ width = Math.max(width, rect.x + rect.width);
68211
+ height = Math.max(height, rect.y + rect.height);
68212
+ }
68213
+ }
68214
+ if (!hasViewports) {
68215
+ return { x: 0, y: 0, width: 0, height: 0 };
68216
+ }
68217
+ return { x, y, width: width - x, height: height - y };
68168
68218
  }
68169
68219
  }
68170
68220
 
@@ -69339,7 +69389,7 @@ class SpreadsheetDashboard extends Component {
69339
69389
  });
69340
69390
  }
69341
69391
  moveCanvas(deltaX, deltaY) {
69342
- const { scrollX, scrollY } = this.env.model.getters.getActiveSheetDOMScrollInfo();
69392
+ const { scrollX, scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
69343
69393
  this.env.model.dispatch("SET_VIEWPORT_OFFSET", {
69344
69394
  offsetX: scrollX + deltaX,
69345
69395
  offsetY: scrollY + deltaY,
@@ -75282,6 +75332,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
75282
75332
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
75283
75333
 
75284
75334
 
75285
- __info__.version = "18.2.0-alpha.5";
75286
- __info__.date = "2025-01-31T07:59:30.667Z";
75287
- __info__.hash = "efce841";
75335
+ __info__.version = "18.2.0-alpha.6";
75336
+ __info__.date = "2025-02-05T06:50:47.008Z";
75337
+ __info__.hash = "dae9ab2";