58 lines
1.4 KiB
TypeScript
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})
|
|
}
|
|
}
|
|
})
|
|
})
|
|
})
|