fix: show immediate full-screen loader during route navigation

This commit is contained in:
Ruslan Bakiev
2026-03-14 11:01:53 +07:00
parent b7f452cdd1
commit ebe72907a4
2 changed files with 72 additions and 1 deletions

View File

@@ -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>

View 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()
})
})