@hydecorp/push-state
Version:
Turn static web sites into dynamic web apps
71 lines (61 loc) • 2.07 kB
text/typescript
import { getScrollTop, Cause } from "./common";
interface ScrollState {
[k: string]: any;
scrollTop?: number;
scrollHeight?: number;
}
export class ScrollManager {
private parent: { histId: string } & HTMLElement;
constructor(parent: { histId: string } & HTMLElement) {
this.parent = parent;
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
}
manageScrollPosition({ cause, url: { hash } }: { cause: Cause, url: URL }) {
switch (cause) {
case Cause.Push: {
// FIXME: make configurable
this.scrollHashIntoView(hash, { behavior: "smooth", block: "start", inline: "nearest" });
break;
}
case Cause.Pop: {
this.restoreScrollPosition();
break;
}
case Cause.Init: {
this.restoreScrollPositionOnReload();
break;
}
}
}
private elementFromHash(hash: string) {
return document.getElementById(decodeURIComponent(hash.substr(1)))
}
private scrollHashIntoView(hash: string, options: boolean | ScrollIntoViewOptions) {
if (hash) {
const el = this.elementFromHash(hash);
if (el) el.scrollIntoView(options);
} else {
window.scroll(window.pageXOffset, 0);
}
}
private restoreScrollPosition() {
const { histId } = this.parent;
const { scrollTop } = (history.state && history.state[histId]) || {} as ScrollState;
if (scrollTop != null) {
window.scroll(window.pageXOffset, scrollTop);
}
}
private restoreScrollPositionOnReload() {
const { histId } = this.parent;
const scrollState = history.state && history.state[histId];
// FIXME: As far as I can tell there is no better way of figuring out if the user has scrolled
// and it doesn't work on hash links b/c the scroll position is going to be non-null by definition
if (scrollState && getScrollTop() === 0) {
this.restoreScrollPosition();
} else if (location.hash) {
requestAnimationFrame(() => this.scrollHashIntoView(location.hash, true));
}
}
};