Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve behavior of the viewport scroll after selection change #10709

Merged
merged 20 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fb151ad
Add calculators for partially visible indexes
budnix Jan 9, 2024
0fc6921
Add changelog entry file
budnix Jan 9, 2024
ea7f322
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 9, 2024
885ae4c
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 10, 2024
ab2ea49
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 10, 2024
8e27bdc
Modularize ViewportScroll module, improve Scroll logic
budnix Jan 10, 2024
c24062b
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 11, 2024
996daee
Add tests, complete documentation, update TS defs
budnix Jan 11, 2024
108a15b
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 11, 2024
6f80b19
Cover with tests some cases related to viewport scrolling
budnix Jan 12, 2024
b219b10
Cover the ViewportScroll module with more tests
budnix Jan 15, 2024
8984d9c
Extract the changes of dev-handsontable#1605 to separate PR
budnix Jan 15, 2024
71932d3
Extract the changes for dev-handsontable#1711 to separate PR
budnix Jan 15, 2024
1ce975c
Remove unrelated code to this PR
budnix Jan 15, 2024
1339d2e
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 15, 2024
31fccb6
Cover with tests the `setExpectedLayers` method
budnix Jan 15, 2024
96371d1
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 15, 2024
e51bcf7
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 16, 2024
6d89add
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 17, 2024
223cbd6
Merge branch 'develop' into feature/dev-issue-1689
budnix Jan 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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