60 lines
No EOL
1.2 KiB
Vue
60 lines
No EOL
1.2 KiB
Vue
<template>
|
|
<router-view v-slot="{ Component }">
|
|
<template v-if="Component">
|
|
<suspense @pending="loading = true" @resolve="loading = false">
|
|
<!-- The key makes that components are not reused if only params changed -->
|
|
<component :is="Component" :key="JSON.stringify($route.params)"/>
|
|
</suspense>
|
|
</template>
|
|
</router-view>
|
|
<div
|
|
class="pt-10 absolute inset-0 bg-background"
|
|
:class="$style.loadingOverlay"
|
|
:data-is-visible="loading || forceLoading"
|
|
>
|
|
<div class="flex justify-center gap-3 text-2xl p-8" :class="$style.loadingIndicator">
|
|
<LoadingIcon/>
|
|
<span>{{ loadingText }}</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style module lang="scss">
|
|
.loadingOverlay {
|
|
opacity: 0;
|
|
transition: 100ms ease opacity;
|
|
|
|
&[data-is-visible="true"] {
|
|
opacity: 100%;
|
|
transition-duration: 200ms;
|
|
|
|
.loadingIndicator {
|
|
opacity: 100%;
|
|
}
|
|
}
|
|
|
|
&[data-is-visible="false"] {
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
.loadingIndicator {
|
|
opacity: 0;
|
|
transition: 200ms ease opacity;
|
|
}
|
|
</style>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from "vue"
|
|
import LoadingIcon from "./LoadingIcon.vue"
|
|
|
|
defineProps({
|
|
loadingText: {
|
|
type: String,
|
|
default: "Loading…"
|
|
},
|
|
forceLoading: Boolean
|
|
})
|
|
|
|
const loading = ref(false)
|
|
</script> |