Skip to content

Commit

Permalink
Improve behavior of the viewport scroll after selection change (#10709)
Browse files Browse the repository at this point in the history
  • Loading branch information
budnix committed Jan 18, 2024
1 parent d82e84a commit 40cdda4
Show file tree
Hide file tree
Showing 45 changed files with 3,944 additions and 130 deletions.
8 changes: 8 additions & 0 deletions .changelogs/10709.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"issuesOrigin": "private",
"title": "Improved behavior of the viewport scroll after a cell click.",
"type": "changed",
"issueOrPR": 10709,
"breaking": false,
"framework": "none"
}
12 changes: 12 additions & 0 deletions handsontable/src/3rdparty/walkontable/src/core/_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,18 @@ export default class CoreAbstract {
get startColumnVisible() {
return wot.wtViewport.columnsVisibleCalculator.startColumn;
},
get startColumnPartiallyVisible() {
return wot.wtViewport.columnsPartiallyVisibleCalculator.startColumn;
},
get endColumnRendered() {
return wot.wtViewport.columnsRenderCalculator.endColumn;
},
get endColumnVisible() {
return wot.wtViewport.columnsVisibleCalculator.endColumn;
},
get endColumnPartiallyVisible() {
return wot.wtViewport.columnsPartiallyVisibleCalculator.endColumn;
},
get countColumnsRendered() {
return wot.wtViewport.columnsRenderCalculator.count;
},
Expand All @@ -354,12 +360,18 @@ export default class CoreAbstract {
get startRowVisible() {
return wot.wtViewport.rowsVisibleCalculator.startRow;
},
get startRowPartiallyVisible() {
return wot.wtViewport.rowsPartiallyVisibleCalculator.startRow;
},
get endRowRendered() {
return wot.wtViewport.rowsRenderCalculator.endRow;
},
get endRowVisible() {
return wot.wtViewport.rowsVisibleCalculator.endRow;
},
get endRowPartiallyVisible() {
return wot.wtViewport.rowsPartiallyVisibleCalculator.endRow;
},
get countRowsRendered() {
return wot.wtViewport.rowsRenderCalculator.count;
},
Expand Down
176 changes: 97 additions & 79 deletions handsontable/src/3rdparty/walkontable/src/scroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,6 @@ class Scroll {
* @type {ScrollDao}
*/
dataAccessObject;
/**
* Holds the last column reached by the scroll, which determines the scroll snapping direction
* (left or right) for a next horizontal scroll.
*
* @protected
* @type {number}
*/
lastScrolledColumnPos = -1;
/**
* Holds the last row reached by the scroll, which determines the scroll snapping direction
* (top or bottom) for a next vertical scroll.
*
* @protected
* @type {number}
*/
lastScrolledRowPos = -1;

/**
* @param {ScrollDao} dataAccessObject Tha data access object.
Expand Down Expand Up @@ -77,8 +61,6 @@ class Scroll {
return false;
}

const firstVisibleColumn = this.getFirstVisibleColumn();
const lastVisibleColumn = this.getLastVisibleColumn();
const autoSnapping = snapToRight === undefined && snapToLeft === undefined;
const {
fixedColumnsStart,
Expand All @@ -91,25 +73,17 @@ class Scroll {
return false;
}

let result = false;

column = this.dataAccessObject.wtSettings.getSetting('onBeforeViewportScrollHorizontally', column);

// if there is no fully visible columns use the supporting variable (lastScrolledColumnPos) to
// determine the snapping direction (left or right)
if (firstVisibleColumn === -1) {
result = inlineStartOverlay
.scrollTo(column, autoSnapping ? column > this.lastScrolledColumnPos : snapToRight);
const firstColumn = this.getFirstVisibleColumn();
const lastColumn = this.getLastVisibleColumn();
let result = false;

} else if (autoSnapping && (column < firstVisibleColumn || column > lastVisibleColumn) || !autoSnapping) {
if (autoSnapping && (column < firstColumn || column > lastColumn) || !autoSnapping) {
// if there is at least one fully visible column determine the snapping direction based on
// that columns or by snapToRight/snapToLeft flags, if provided.
result = inlineStartOverlay
.scrollTo(column, autoSnapping ? column > lastVisibleColumn : snapToRight);
}

if (result) {
this.lastScrolledColumnPos = column;
.scrollTo(column, autoSnapping ? column >= this.getLastPartiallyVisibleColumn() : snapToRight);
}

return result;
Expand All @@ -134,8 +108,6 @@ class Scroll {
return false;
}

const firstVisibleRow = this.getFirstVisibleRow();
const lastVisibleRow = this.getLastVisibleRow();
const autoSnapping = snapToTop === undefined && snapToBottom === undefined;
const {
fixedRowsBottom,
Expand All @@ -149,23 +121,16 @@ class Scroll {
return false;
}

let result = false;

row = this.dataAccessObject.wtSettings.getSetting('onBeforeViewportScrollVertically', row);

// if there is no fully visible rows use the supporting variable (lastScrolledRowPos) to
// determine the snapping direction (top or bottom)
if (firstVisibleRow === -1) {
result = topOverlay.scrollTo(row, autoSnapping ? row > this.lastScrolledRowPos : snapToBottom);
const firstRow = this.getFirstVisibleRow();
const lastRow = this.getLastVisibleRow();
let result = false;

} else if (autoSnapping && (row < firstVisibleRow || row > lastVisibleRow) || !autoSnapping) {
if (autoSnapping && (row < firstRow || row > lastRow) || !autoSnapping) {
// if there is at least one fully visible row determine the snapping direction based on
// that rows or by snapToTop/snapToBottom flags, if provided.
result = topOverlay.scrollTo(row, autoSnapping ? row > lastVisibleRow : snapToBottom);
}

if (result) {
this.lastScrolledRowPos = row;
result = topOverlay.scrollTo(row, autoSnapping ? row >= this.getLastPartiallyVisibleRow() : snapToBottom);
}

return result;
Expand All @@ -186,37 +151,25 @@ class Scroll {
* @returns {number}
*/
getLastVisibleRow() {
const {
topOverlay,
wtTable,
wtViewport,
totalRows,
rootWindow,
} = this.dataAccessObject;
let lastVisibleRow = wtTable.getLastVisibleRow();

if (topOverlay.mainTableScrollableElement === rootWindow) {
const rootElementOffset = offset(wtTable.wtRootElement);
const windowScrollTop = getScrollTop(rootWindow, rootWindow);

// Only calculate lastVisibleRow when table didn't filled (from bottom) whole viewport space
if (rootElementOffset.top > windowScrollTop) {
const windowHeight = innerHeight(rootWindow);
let rowsHeight = wtViewport.getColumnHeaderHeight();

for (let row = 1; row <= totalRows; row++) {
rowsHeight += topOverlay.sumCellSizes(row - 1, row);
return this.#getLastRowIndex(this.dataAccessObject.wtTable.getLastVisibleRow());
}

if (rootElementOffset.top + rowsHeight - windowScrollTop >= windowHeight) {
// Return physical row - 1 (-2 because rangeEach gives row index + 1 - sumCellSizes requirements)
lastVisibleRow = row - 2;
break;
}
}
}
}
/**
* Get first partially visible row based on virtual dom and how table is visible in browser window viewport.
*
* @returns {number}
*/
getFirstPartiallyVisibleRow() {
return this.dataAccessObject.wtTable.getFirstPartiallyVisibleRow();
}

return lastVisibleRow;
/**
* Get last visible row based on virtual dom and how table is visible in browser window viewport.
*
* @returns {number}
*/
getLastPartiallyVisibleRow() {
return this.#getLastRowIndex(this.dataAccessObject.wtTable.getLastPartiallyVisibleRow());
}

/**
Expand All @@ -234,6 +187,34 @@ class Scroll {
* @returns {number}
*/
getLastVisibleColumn() {
return this.#getLastColumnIndex(this.dataAccessObject.wtTable.getLastVisibleColumn());
}

/**
* Get first partially visible column based on virtual dom and how table is visible in browser window viewport.
*
* @returns {number}
*/
getFirstPartiallyVisibleColumn() {
return this.dataAccessObject.wtTable.getFirstPartiallyVisibleColumn();
}

/**
* Get last partially visible column based on virtual dom and how table is visible in browser window viewport.
*
* @returns {number}
*/
getLastPartiallyVisibleColumn() {
return this.#getLastColumnIndex(this.dataAccessObject.wtTable.getLastPartiallyVisibleColumn());
}

/**
* Get last visible column based on virtual dom and how table is visible in browser window viewport.
*
* @param {number} lastColumnIndex The last visible column index.
* @returns {number}
*/
#getLastColumnIndex(lastColumnIndex) {
const {
wtSettings,
inlineStartOverlay,
Expand All @@ -243,8 +224,6 @@ class Scroll {
rootWindow,
} = this.dataAccessObject;

let lastVisibleColumn = wtTable.getLastVisibleColumn();

if (inlineStartOverlay.mainTableScrollableElement === rootWindow) {
const isRtl = wtSettings.getSetting('rtlMode');
let inlineStartRootElementOffset = null;
Expand All @@ -264,7 +243,7 @@ class Scroll {

const windowScrollLeft = Math.abs(getScrollLeft(rootWindow, rootWindow));

// Only calculate lastVisibleColumn when table didn't filled (from right) whole viewport space
// Only calculate lastColumnIndex when table didn't filled (from right) whole viewport space
if (inlineStartRootElementOffset > windowScrollLeft) {
const windowWidth = innerWidth(rootWindow);
let columnsWidth = wtViewport.getRowHeaderWidth();
Expand All @@ -274,14 +253,53 @@ class Scroll {

if (inlineStartRootElementOffset + columnsWidth - windowScrollLeft >= windowWidth) {
// Return physical column - 1 (-2 because rangeEach gives column index + 1 - sumCellSizes requirements)
lastVisibleColumn = column - 2;
lastColumnIndex = column - 2;
break;
}
}
}
}

return lastColumnIndex;
}

/**
* Get last visible row based on virtual dom and how table is visible in browser window viewport.
*
* @param {number} lastRowIndex The last visible row index.
* @returns {number}
*/
#getLastRowIndex(lastRowIndex) {
const {
topOverlay,
wtTable,
wtViewport,
totalRows,
rootWindow,
} = this.dataAccessObject;

if (topOverlay.mainTableScrollableElement === rootWindow) {
const rootElementOffset = offset(wtTable.wtRootElement);
const windowScrollTop = getScrollTop(rootWindow, rootWindow);

// Only calculate lastRowIndex when table didn't filled (from bottom) whole viewport space
if (rootElementOffset.top > windowScrollTop) {
const windowHeight = innerHeight(rootWindow);
let rowsHeight = wtViewport.getColumnHeaderHeight();

for (let row = 1; row <= totalRows; row++) {
rowsHeight += topOverlay.sumCellSizes(row - 1, row);

if (rootElementOffset.top + rowsHeight - windowScrollTop >= windowHeight) {
// Return physical row - 1 (-2 because rangeEach gives row index + 1 - sumCellSizes requirements)
lastRowIndex = row - 2;
break;
}
}
}
}

return lastVisibleColumn;
return lastRowIndex;
}
}

Expand Down
2 changes: 2 additions & 0 deletions handsontable/src/3rdparty/walkontable/src/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ class Table {
if (this.isMaster) {
// in case we only scrolled without redraw, update visible rows information in oldRowsCalculator
wtViewport.createVisibleCalculators();
wtViewport.createPartiallyVisibleCalculators();
}
if (wtOverlays) {
wtOverlays.refresh(true);
Expand Down Expand Up @@ -371,6 +372,7 @@ class Table {

if (this.isMaster) {
this.dataAccessObject.wtViewport.createVisibleCalculators();
this.dataAccessObject.wtViewport.createPartiallyVisibleCalculators();
this.dataAccessObject.wtOverlays.refresh(false);
this.dataAccessObject.wtOverlays.applyToDOM();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ const calculatedColumns = {
return startColumn;
},

/**
* Get the source index of the first column partially visible in the viewport. If no columns are partially visible, returns an error code: -1.
*
* @returns {number}
* @this Table
*/
getFirstPartiallyVisibleColumn() {
const startColumn = this.dataAccessObject.startColumnPartiallyVisible;

if (startColumn === null) {
return -1;
}

return startColumn;
},

/**
* Get the source index of the last rendered column. If no columns are rendered, returns an error code: -1.
*
Expand Down Expand Up @@ -75,6 +91,22 @@ const calculatedColumns = {
return endColumn;
},

/**
* Get the source index of the last column partially visible in the viewport. If no columns are partially visible, returns an error code: -1.
*
* @returns {number}
* @this Table
*/
getLastPartiallyVisibleColumn() {
const endColumn = this.dataAccessObject.endColumnPartiallyVisible;

if (endColumn === null) {
return -1;
}

return endColumn;
},

/**
* Get the number of rendered columns.
*
Expand Down

0 comments on commit 40cdda4

Please sign in to comment.