feat: track scroll position
This commit is contained in:
parent
0eefcfa281
commit
3270140c3f
14 changed files with 112 additions and 4 deletions
|
|
@ -1,4 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
noScrollTrack: true,
|
||||||
|
})
|
||||||
|
|
||||||
const buildInfo = useBuildInfo()
|
const buildInfo = useBuildInfo()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
noScrollTrack: true,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div min-h-screen flex justify-center items-center>
|
<div min-h-screen flex justify-center items-center>
|
||||||
<div text-center flex="~ col gap-2" items-center>
|
<div text-center flex="~ col gap-2" items-center>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
noScrollTrack: true,
|
||||||
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHeadFixed({
|
useHeadFixed({
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
noScrollTrack: true,
|
||||||
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHeadFixed({
|
useHeadFixed({
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
|
noScrollTrack: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
|
noScrollTrack: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ definePageMeta({
|
||||||
if (!useAppConfig().pwaEnabled)
|
if (!useAppConfig().pwaEnabled)
|
||||||
return navigateTo('/settings/notifications')
|
return navigateTo('/settings/notifications')
|
||||||
}],
|
}],
|
||||||
|
noScrollTrack: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
noScrollTrack: true,
|
||||||
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHeadFixed({
|
useHeadFixed({
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { mastodon } from 'masto'
|
import type { mastodon } from 'masto'
|
||||||
import { ofetch } from 'ofetch'
|
|
||||||
import { useForm } from 'slimeform'
|
import { useForm } from 'slimeform'
|
||||||
import { parse } from 'ultrahtml'
|
import { parse } from 'ultrahtml'
|
||||||
import type { Component } from 'vue'
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
|
noScrollTrack: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
|
noScrollTrack: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
|
noScrollTrack: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
import { fileOpen } from 'browser-fs-access'
|
import { fileOpen } from 'browser-fs-access'
|
||||||
import type { UserLogin } from '~/types'
|
import type { UserLogin } from '~/types'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
noScrollTrack: true,
|
||||||
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHeadFixed({
|
useHeadFixed({
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin((/* nuxtApp */) => {
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
scrollToTop: () => {
|
scrollToTop: () => {
|
||||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
// if (typeof force === 'boolean' && force)
|
||||||
|
// nuxtApp.$trackScroll.forceScroll()
|
||||||
|
|
||||||
|
// window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
plugins/track-scroll-position.client.ts
Normal file
75
plugins/track-scroll-position.client.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
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', {})
|
||||||
|
|
||||||
|
router.beforeEach(async () => {
|
||||||
|
track.value = false
|
||||||
|
})
|
||||||
|
router.onError(async () => {
|
||||||
|
track.value = true
|
||||||
|
})
|
||||||
|
|
||||||
|
const forceScroll = () => {
|
||||||
|
storage.value[route.fullPath] = 0
|
||||||
|
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const restoreScroll = () => {
|
||||||
|
const path = route.fullPath
|
||||||
|
return nextTick().then(() => {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (path !== route.fullPath) {
|
||||||
|
reject(new Error('navigation canceled'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!route.meta || !route.meta?.noScrollTrack) {
|
||||||
|
const scrollPosition = storage.value[route.fullPath]
|
||||||
|
if (scrollPosition)
|
||||||
|
window.scrollTo(0, scrollPosition)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
forceScroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve()
|
||||||
|
}, 600)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
nuxtApp.hooks.hook('app:suspense:resolve', () => {
|
||||||
|
if (isHydrated.value) {
|
||||||
|
restoreScroll().then(() => {
|
||||||
|
track.value = true
|
||||||
|
}).catch(noop)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.hooks.hook('page:finish', () => {
|
||||||
|
if (isHydrated.value) {
|
||||||
|
restoreScroll().then(() => {
|
||||||
|
track.value = true
|
||||||
|
}).catch(noop)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => [track.value, y.value, route], ([trackEnabled, scrollPosition, r]) => {
|
||||||
|
if (trackEnabled && (!r.meta || !r.meta?.noScrollTrack))
|
||||||
|
storage.value[r.fullPath] = Math.floor(scrollPosition)
|
||||||
|
}, { immediate: true, flush: 'pre' })
|
||||||
|
|
||||||
|
return {
|
||||||
|
provide: {
|
||||||
|
trackScroll: reactive({
|
||||||
|
forceScroll,
|
||||||
|
restoreScroll,
|
||||||
|
track,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue