webapps/packages/layers/content/plugins/scroll-behavior.ts

58 lines
1.4 KiB
TypeScript

import {isNavigationFailure} from "vue-router";
async function waitFor<T>(
obtain: () => T | Promise<T>,
tries = 30,
delay = 100
): Promise<T | undefined> {
for (let i = 0; i < tries; i++) {
const result = await obtain()
if (result !== undefined) {
return result
}
await new Promise(resolve => setTimeout(resolve, delay))
}
return undefined
}
export default defineNuxtPlugin((app) => {
app.hook('app:mounted', () => {
const router = useRouter()
router.options.scrollBehavior = (to, from, savedPosition) => {
if (to.query.freeze) {
return false
}
if (to.hash && to.path === from.path) {
return false
}
return {top: 0}
}
router.afterEach(async (to, from, failure) => {
if (isNavigationFailure(failure)) {
console.warn('failed navigation', failure)
} else if (to.hash) {
const [header, element, atTop] = await Promise.all([
waitFor(() => document.querySelector('header') ?? undefined),
waitFor(() => document.querySelector(to.hash) ?? undefined),
waitFor(() => (to.path === from.path || window.scrollY < 10) ? true : undefined)
])
if (element && atTop) {
const offset = header ? header.offsetHeight : 0
const position = element.getBoundingClientRect().top
window.scrollTo({top: position - offset + window.scrollY})
}
}
})
})
})