a new start
This commit is contained in:
78
packages/hydecorp/push-state/src/scroll.ts
Normal file
78
packages/hydecorp/push-state/src/scroll.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user