fix: show immediate full-screen loader during route navigation
This commit is contained in:
32
app/app.vue
32
app/app.vue
@@ -1,11 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<NuxtLoadingIndicator :height="3" />
|
<NuxtLoadingIndicator :height="4" :duration="3000" color="#16a34a" />
|
||||||
|
|
||||||
|
<Transition name="route-loader-fade">
|
||||||
|
<div
|
||||||
|
v-if="routeLoading"
|
||||||
|
class="fixed inset-0 z-[999] flex items-center justify-center bg-base-100/45 backdrop-blur-sm"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-busy="true"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-3 rounded-full border border-base-300 bg-base-100/90 px-5 py-3 shadow-xl">
|
||||||
|
<span class="loading loading-spinner loading-md text-primary" />
|
||||||
|
<span class="text-sm font-medium text-base-content">{{ t('common.loading') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const routeLoading = useState<boolean>('route-loading', () => false)
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
'data-theme': 'cupcake',
|
'data-theme': 'cupcake',
|
||||||
@@ -13,3 +31,15 @@ useHead({
|
|||||||
script: []
|
script: []
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.route-loader-fade-enter-active,
|
||||||
|
.route-loader-fade-leave-active {
|
||||||
|
transition: opacity 0.16s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-loader-fade-enter-from,
|
||||||
|
.route-loader-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
41
app/plugins/route-loading.client.ts
Normal file
41
app/plugins/route-loading.client.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
const routeLoading = useState<boolean>('route-loading', () => false)
|
||||||
|
let hideTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
if (hideTimer) {
|
||||||
|
clearTimeout(hideTimer)
|
||||||
|
hideTimer = null
|
||||||
|
}
|
||||||
|
routeLoading.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
if (hideTimer) {
|
||||||
|
clearTimeout(hideTimer)
|
||||||
|
}
|
||||||
|
// Small delay to prevent flicker on very fast routes.
|
||||||
|
hideTimer = setTimeout(() => {
|
||||||
|
routeLoading.value = false
|
||||||
|
hideTimer = null
|
||||||
|
}, 120)
|
||||||
|
}
|
||||||
|
|
||||||
|
nuxtApp.$router.beforeEach((_to, _from, next) => {
|
||||||
|
show()
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.$router.afterEach(() => {
|
||||||
|
hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.$router.onError(() => {
|
||||||
|
hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.hook('page:finish', () => {
|
||||||
|
hide()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Reference in New Issue
Block a user