client/general: improve scrolling
This commit is contained in:
parent
5d8dd9cb05
commit
394e51ed21
16 changed files with 57 additions and 38 deletions
|
@ -24,12 +24,6 @@ router.enter(
|
||||||
(ctx, next) => {
|
(ctx, next) => {
|
||||||
mousetrap.reset();
|
mousetrap.reset();
|
||||||
next();
|
next();
|
||||||
window.requestAnimationFrame(
|
|
||||||
() => {
|
|
||||||
window.scrollTo(
|
|
||||||
ctx.state.scrollX || 0,
|
|
||||||
ctx.state.scrollY || 0);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// register controller routes
|
// register controller routes
|
||||||
|
|
|
@ -390,17 +390,15 @@ function replaceContent(target, source) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToHash() {
|
function syncScrollPosition() {
|
||||||
window.setTimeout(() => {
|
window.requestAnimationFrame(
|
||||||
if (!window.location.hash) {
|
() => {
|
||||||
return;
|
if (history.state.hasOwnProperty('scrollX')) {
|
||||||
}
|
window.scrollTo(history.state.scrollX, history.state.scrollY);
|
||||||
const el = document.getElementById(
|
} else {
|
||||||
window.location.hash.replace(/#/, ''));
|
window.scrollTo(0, 0);
|
||||||
if (el) {
|
}
|
||||||
el.scrollIntoView();
|
});
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideDown(element) {
|
function slideDown(element) {
|
||||||
|
@ -470,7 +468,7 @@ module.exports = {
|
||||||
decorateValidator: decorateValidator,
|
decorateValidator: decorateValidator,
|
||||||
makeVoidElement: makeVoidElement,
|
makeVoidElement: makeVoidElement,
|
||||||
makeNonVoidElement: makeNonVoidElement,
|
makeNonVoidElement: makeNonVoidElement,
|
||||||
scrollToHash: scrollToHash,
|
syncScrollPosition: syncScrollPosition,
|
||||||
slideDown: slideDown,
|
slideDown: slideDown,
|
||||||
slideUp: slideUp,
|
slideUp: slideUp,
|
||||||
monitorNodeRemoval: monitorNodeRemoval,
|
monitorNodeRemoval: monitorNodeRemoval,
|
||||||
|
|
|
@ -11,6 +11,7 @@ class EmptyView {
|
||||||
constructor() {
|
constructor() {
|
||||||
this._hostNode = document.getElementById('content-holder');
|
this._hostNode = document.getElementById('content-holder');
|
||||||
views.replaceContent(this._hostNode, template());
|
views.replaceContent(this._hostNode, template());
|
||||||
|
views.syncScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(message) {
|
showError(message) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ class EndlessPageView {
|
||||||
this._hostNode = document.getElementById('content-holder');
|
this._hostNode = document.getElementById('content-holder');
|
||||||
this._active = true;
|
this._active = true;
|
||||||
this._working = 0;
|
this._working = 0;
|
||||||
this._init = true;
|
this._init = false;
|
||||||
|
|
||||||
this.threshold = window.innerHeight / 3;
|
this.threshold = window.innerHeight / 3;
|
||||||
this.minPageShown = null;
|
this.minPageShown = null;
|
||||||
|
@ -30,7 +30,11 @@ class EndlessPageView {
|
||||||
ctx.headerRenderer(ctx.headerContext);
|
ctx.headerRenderer(ctx.headerContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadPage(ctx, ctx.parameters.page, true);
|
this._loadPage(ctx, ctx.parameters.page, true).then(pageNode => {
|
||||||
|
if (ctx.parameters.page !== 1) {
|
||||||
|
pageNode.scrollIntoView();
|
||||||
|
}
|
||||||
|
});
|
||||||
this._probePageLoad(ctx);
|
this._probePageLoad(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,26 +91,32 @@ class EndlessPageView {
|
||||||
|
|
||||||
_loadPage(ctx, pageNumber, append) {
|
_loadPage(ctx, pageNumber, append) {
|
||||||
this._working++;
|
this._working++;
|
||||||
ctx.requestPage(pageNumber).then(response => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!this._active) {
|
ctx.requestPage(pageNumber).then(response => {
|
||||||
this._working--;
|
if (!this._active) {
|
||||||
return Promise.reject();
|
this._working--;
|
||||||
}
|
return Promise.reject();
|
||||||
this.totalPages = Math.ceil(response.total / response.pageSize);
|
}
|
||||||
window.requestAnimationFrame(() => {
|
this.totalPages = Math.ceil(response.total / response.pageSize);
|
||||||
this._renderPage(
|
window.requestAnimationFrame(() => {
|
||||||
ctx, pageNumber, append, response);
|
let pageNode = this._renderPage(
|
||||||
|
ctx, pageNumber, append, response);
|
||||||
|
this._working--;
|
||||||
|
resolve(pageNode);
|
||||||
|
});
|
||||||
|
}, response => {
|
||||||
|
this.showError(response.description);
|
||||||
this._working--;
|
this._working--;
|
||||||
|
reject();
|
||||||
});
|
});
|
||||||
}, response => {
|
|
||||||
this.showError(response.description);
|
|
||||||
this._working--;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderPage(ctx, pageNumber, append, response) {
|
_renderPage(ctx, pageNumber, append, response) {
|
||||||
|
let pageNode = null;
|
||||||
|
|
||||||
if (response.total) {
|
if (response.total) {
|
||||||
const pageNode = pageTemplate({
|
pageNode = pageTemplate({
|
||||||
page: pageNumber,
|
page: pageNumber,
|
||||||
totalPages: this.totalPages,
|
totalPages: this.totalPages,
|
||||||
});
|
});
|
||||||
|
@ -128,7 +138,7 @@ class EndlessPageView {
|
||||||
|
|
||||||
if (append) {
|
if (append) {
|
||||||
this._pagesHolderNode.appendChild(pageNode);
|
this._pagesHolderNode.appendChild(pageNode);
|
||||||
if (this._init && pageNumber !== 1) {
|
if (!this._init && pageNumber !== 1) {
|
||||||
window.scroll(0, pageNode.getBoundingClientRect().top);
|
window.scroll(0, pageNode.getBoundingClientRect().top);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,7 +151,9 @@ class EndlessPageView {
|
||||||
} else if (response.total <= (pageNumber - 1) * response.pageSize) {
|
} else if (response.total <= (pageNumber - 1) * response.pageSize) {
|
||||||
this.showInfo('No data to show');
|
this.showInfo('No data to show');
|
||||||
}
|
}
|
||||||
this._init = false;
|
|
||||||
|
this._init = true;
|
||||||
|
return pageNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
showSuccess(message) {
|
showSuccess(message) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ class HelpView {
|
||||||
}
|
}
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
views.replaceContent(this._hostNode, sourceNode);
|
||||||
views.scrollToHash();
|
views.syncScrollPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ class HomeView {
|
||||||
|
|
||||||
const sourceNode = template(ctx);
|
const sourceNode = template(ctx);
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
views.replaceContent(this._hostNode, sourceNode);
|
||||||
|
views.syncScrollPosition();
|
||||||
|
|
||||||
if (this._formNode) {
|
if (this._formNode) {
|
||||||
this._tagAutoCompleteControl = new TagAutoCompleteControl(
|
this._tagAutoCompleteControl = new TagAutoCompleteControl(
|
||||||
|
|
|
@ -16,6 +16,7 @@ class LoginView extends events.EventTarget {
|
||||||
passwordPattern: config.passwordRegex,
|
passwordPattern: config.passwordRegex,
|
||||||
canSendMails: config.canSendMails,
|
canSendMails: config.canSendMails,
|
||||||
}));
|
}));
|
||||||
|
views.syncScrollPosition();
|
||||||
|
|
||||||
views.decorateValidator(this._formNode);
|
views.decorateValidator(this._formNode);
|
||||||
this._userNameFieldNode.setAttribute('pattern', config.userNameRegex);
|
this._userNameFieldNode.setAttribute('pattern', config.userNameRegex);
|
||||||
|
|
|
@ -107,6 +107,8 @@ class ManualPageView {
|
||||||
if (response.total <= (currentPage - 1) * response.pageSize) {
|
if (response.total <= (currentPage - 1) * response.pageSize) {
|
||||||
this.showInfo('No data to show');
|
this.showInfo('No data to show');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
views.syncScrollPosition();
|
||||||
}, response => {
|
}, response => {
|
||||||
this.showError(response.description);
|
this.showError(response.description);
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@ class NotFoundView {
|
||||||
|
|
||||||
const sourceNode = template({path: path});
|
const sourceNode = template({path: path});
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
views.replaceContent(this._hostNode, sourceNode);
|
||||||
|
views.syncScrollPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,9 @@ class PasswordResetView extends events.EventTarget {
|
||||||
this._hostNode = document.getElementById('content-holder');
|
this._hostNode = document.getElementById('content-holder');
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, template());
|
views.replaceContent(this._hostNode, template());
|
||||||
views.decorateValidator(this._formNode);
|
views.syncScrollPosition();
|
||||||
|
|
||||||
|
views.decorateValidator(this._formNode);
|
||||||
this._hostNode.addEventListener('submit', e => {
|
this._hostNode.addEventListener('submit', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.dispatchEvent(new CustomEvent('submit', {
|
this.dispatchEvent(new CustomEvent('submit', {
|
||||||
|
|
|
@ -23,6 +23,7 @@ class PostView {
|
||||||
const postContainerNode = sourceNode.querySelector('.post-container');
|
const postContainerNode = sourceNode.querySelector('.post-container');
|
||||||
const sidebarNode = sourceNode.querySelector('.sidebar');
|
const sidebarNode = sourceNode.querySelector('.sidebar');
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
views.replaceContent(this._hostNode, sourceNode);
|
||||||
|
views.syncScrollPosition();
|
||||||
|
|
||||||
const postViewNode = document.body.querySelector('.content-wrapper');
|
const postViewNode = document.body.querySelector('.content-wrapper');
|
||||||
const topNavigationNode =
|
const topNavigationNode =
|
||||||
|
|
|
@ -14,6 +14,7 @@ class RegistrationView extends events.EventTarget {
|
||||||
userNamePattern: config.userNameRegex,
|
userNamePattern: config.userNameRegex,
|
||||||
passwordPattern: config.passwordRegex,
|
passwordPattern: config.passwordRegex,
|
||||||
}));
|
}));
|
||||||
|
views.syncScrollPosition();
|
||||||
views.decorateValidator(this._formNode);
|
views.decorateValidator(this._formNode);
|
||||||
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,9 @@ class SettingsView extends events.EventTarget {
|
||||||
this._hostNode = document.getElementById('content-holder');
|
this._hostNode = document.getElementById('content-holder');
|
||||||
views.replaceContent(
|
views.replaceContent(
|
||||||
this._hostNode, template({browsingSettings: ctx.settings}));
|
this._hostNode, template({browsingSettings: ctx.settings}));
|
||||||
views.decorateValidator(this._formNode);
|
views.syncScrollPosition();
|
||||||
|
|
||||||
|
views.decorateValidator(this._formNode);
|
||||||
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class TagCategoriesView extends events.EventTarget {
|
||||||
this._hostNode = document.getElementById('content-holder');
|
this._hostNode = document.getElementById('content-holder');
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, template(ctx));
|
views.replaceContent(this._hostNode, template(ctx));
|
||||||
|
views.syncScrollPosition();
|
||||||
views.decorateValidator(this._formNode);
|
views.decorateValidator(this._formNode);
|
||||||
|
|
||||||
const categoriesToAdd = Array.from(ctx.tagCategories);
|
const categoriesToAdd = Array.from(ctx.tagCategories);
|
||||||
|
|
|
@ -52,6 +52,8 @@ class TagView extends events.EventTarget {
|
||||||
} else {
|
} else {
|
||||||
this._view = new TagSummaryView(ctx);
|
this._view = new TagSummaryView(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
views.syncScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearMessages() {
|
clearMessages() {
|
||||||
|
|
|
@ -47,6 +47,8 @@ class UserView extends events.EventTarget {
|
||||||
} else {
|
} else {
|
||||||
this._view = new UserSummaryView(ctx);
|
this._view = new UserSummaryView(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
views.syncScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearMessages() {
|
clearMessages() {
|
||||||
|
|
Loading…
Reference in a new issue