(core) Scrolling to the active record when editor is activated

Summary: When an editor is activated by typing, the active view should be scrolled to the active record.

Test Plan: new tests

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3196
This commit is contained in:
Jarosław Sadziński
2022-01-05 21:14:44 +01:00
parent 5cdc7b2ea4
commit 08881d9663
7 changed files with 78 additions and 24 deletions

View File

@@ -278,25 +278,53 @@ exports.cssClass = cssClass;
function scrollChildIntoView(valueOrFunc) {
return makeBinding(valueOrFunc, doScrollChildIntoView);
}
function doScrollChildIntoView(elem, index) {
// Key at which we will store the index to scroll for async scrolling.
const indexKey = Symbol();
function doScrollChildIntoView(elem, index, sync) {
if (index === null) {
return Promise.resolve();
}
const scrolly = ko.utils.domData.get(elem, "scrolly");
if (scrolly) {
// Delay this in case it's triggered while other changes are processed (e.g. splices).
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
if (!scrolly.isDisposed()) {
scrolly.scrollRowIntoView(index);
if (sync) {
scrolly.scrollRowIntoView(index);
// Clear async index for scrolling.
elem[indexKey] = null;
return Promise.resolve();
} else {
// Delay this in case it's triggered while other changes are processed (e.g. splices).
// Scrolling is asynchronous, so in case there is already
// active scroll queued, we will change the target index.
// For example:
// doScrollChildIntoView(el, 10, false) # sets the index to 10 and queues a Promise1
// doScrollChildIntoView(el, 20, false) # updates index to 20 and queues a Promise2
// ....
// Promise1 moves to 20, and clears the index.
// Promise2 checks the index is null and just returns.
elem[indexKey] = index;
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
// If scroll was cancelled (there was another call after, that finished
// and cleared the index) return.
if (elem[indexKey] === null) {
resolve();
return;
}
if (!scrolly.isDisposed()) {
scrolly.scrollRowIntoView(elem[indexKey]);
}
resolve();
} catch(err) {
reject(err);
} finally {
// Clear the index, any subsequent async scrolls will be cancelled (on the if test above).
elem[indexKey] = null;
}
resolve();
} catch(err) {
reject(err);
}
}, 0);
});
}, 0);
});
}
} else {
const child = elem.children[index];
if (child) {
@@ -312,8 +340,8 @@ function doScrollChildIntoView(elem, index) {
child.scrollIntoView(false); // ..bottom if scrolling down.
}
}
return Promise.resolve();
}
return Promise.resolve();
}
exports.scrollChildIntoView = scrollChildIntoView;
exports.doScrollChildIntoView = doScrollChildIntoView;