elk/plugins/track-scroll-position.client.ts

95 lines
2.5 KiB
TypeScript
Raw Normal View History

2023-02-10 21:13:46 +00:00
export default defineNuxtPlugin((nuxtApp) => {
const router = useRouter()
const route = useRoute()
const track = ref(false)
const { y } = useWindowScroll()
const storage = useLocalStorage<Record<string, number>>('elk-track-scroll', {})
2023-02-10 22:37:09 +00:00
const customRoutes = new Set<string>()
2023-02-10 21:13:46 +00:00
router.beforeEach(async () => {
track.value = false
})
router.onError(async () => {
track.value = true
})
const forceScrollToTop = () => {
2023-02-10 21:13:46 +00:00
storage.value[route.fullPath] = 0
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
}
2023-02-10 23:15:57 +00:00
const restoreScrollCallback = (ignoreCustomRoutes: boolean) => {
2023-02-10 21:13:46 +00:00
const path = route.fullPath
return nextTick().then(() => {
2023-02-10 23:15:57 +00:00
if (route.meta?.noScrollTrack) {
forceScrollToTop()
return Promise.resolve()
}
2023-02-10 21:13:46 +00:00
return new Promise<void>((resolve, reject) => {
setTimeout(() => {
2023-02-11 14:42:37 +00:00
const fullPath = route.fullPath
if (path !== fullPath) {
2023-02-10 21:13:46 +00:00
reject(new Error('navigation canceled'))
return
}
2023-02-11 14:42:37 +00:00
const r = ignoreCustomRoutes ? undefined : customRoutes.has(fullPath)
2023-02-10 23:23:58 +00:00
if (r) {
reject(new Error('custom routed detected'))
return
2023-02-10 21:13:46 +00:00
}
2023-02-11 14:42:37 +00:00
const scrollPosition = storage.value[fullPath]
2023-02-10 23:23:58 +00:00
if (scrollPosition)
window.scrollTo(0, scrollPosition)
2023-02-11 11:42:01 +00:00
// required for custom routes: first call will be rejected
// we need to enable scroll tracking again, it is disabled
if (!track.value) {
nextTick().then(() => {
track.value = true
})
}
2023-02-10 21:13:46 +00:00
resolve()
}, 600)
})
})
}
2023-02-10 22:37:09 +00:00
const restoreScroll = () => restoreScrollCallback(false)
2023-02-10 21:13:46 +00:00
2023-02-10 22:37:09 +00:00
const restoreScrollHook = () => {
2023-02-10 21:13:46 +00:00
if (isHydrated.value) {
restoreScroll().then(() => {
track.value = true
}).catch(noop)
}
2023-02-10 22:37:09 +00:00
}
const restoreCustomPageScroll = () => restoreScrollCallback(true)
nuxtApp.hooks.hook('app:suspense:resolve', restoreScrollHook)
nuxtApp.hooks.hook('page:finish', restoreScrollHook)
2023-02-10 21:13:46 +00:00
2023-02-10 21:28:53 +00:00
watch([track, y, () => route], ([trackEnabled, scrollPosition, r]) => {
2023-02-10 21:13:46 +00:00
if (trackEnabled && (!r.meta || !r.meta?.noScrollTrack))
storage.value[r.fullPath] = Math.floor(scrollPosition)
}, { immediate: true, flush: 'pre' })
2023-02-10 22:37:09 +00:00
const registerCustomRoute = (path: string) => {
customRoutes.add(path)
}
2023-02-10 21:13:46 +00:00
return {
provide: {
2023-02-11 11:42:01 +00:00
trackScroll: {
forceScrollToTop,
2023-02-10 22:37:09 +00:00
registerCustomRoute,
restoreCustomPageScroll,
2023-02-11 11:42:01 +00:00
},
2023-02-10 21:13:46 +00:00
},
}
})