import {isNavigationFailure} from "vue-router"; async function waitFor( obtain: () => T | Promise, tries = 30, delay = 100 ): Promise { 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}) } } }) }) })