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