feat: add new MainLayout to handle lock and modals with Suspense (async views)

This commit is contained in:
axolotle 2024-08-05 17:17:06 +02:00
parent 0cf60eab98
commit f05d5769ff
3 changed files with 138 additions and 45 deletions

View file

@ -5,13 +5,13 @@ import { useStore } from 'vuex'
import { useRequests } from '@/composables/useRequests'
import { useSettings } from '@/composables/useSettings'
import { useStoreGetters } from '@/store/utils'
import { HistoryConsole, ViewLockOverlay } from '@/views/_partials'
import { HistoryConsole } from '@/views/_partials'
const store = useStore()
const { connected, yunohost, routerKey, ssoLink } = useStoreGetters()
const { connected, yunohost, ssoLink } = useStoreGetters()
const { locked } = useRequests()
const { spinner, dark, transitions, transitionName } = useSettings()
const { spinner, dark } = useSettings()
async function logout() {
store.dispatch('LOGOUT')
@ -113,20 +113,7 @@ onMounted(() => {
</header>
<!-- MAIN -->
<ViewLockOverlay>
<YBreadcrumb />
<main id="main">
<!-- The `key` on RouterView make sure that if a link points to a page that
use the same component as the previous one, it will be refreshed -->
<RouterView v-slot="{ Component }" :key="routerKey">
<Transition v-if="transitions" :name="transitionName">
<Component :is="Component" class="animated" />
</Transition>
<Component v-else :is="Component" class="static" />
</RouterView>
</main>
</ViewLockOverlay>
<MainLayout />
<BModalOrchestrator />
@ -196,34 +183,6 @@ header {
}
}
main {
position: relative;
// Routes transition
.animated {
transition: all 0.15s ease-in-out;
}
.slide-left-enter-from,
.slide-right-leave-active {
position: absolute;
width: 100%;
top: 0;
transform: translate(100vw, 0);
}
.slide-left-leave-active,
.slide-right-enter-from {
position: absolute;
width: 100%;
top: 0;
transform: translate(-100vw, 0);
}
// hack to hide last transition provoqued by the <RouterView> element change
// while disabling the transitions in ToolWebAdmin
.static ~ .animated {
display: none;
}
}
#console {
// Allows the console to be tabbed before the footer links while remaining visually
// the last element of the page

View file

@ -0,0 +1,133 @@
<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'
import type { VNode } from 'vue'
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import {
ModalError,
ModalReconnecting,
ModalWaiting,
ModalWarning,
} from '@/components/modals'
import { useRequests } from '@/composables/useRequests'
import { useSettings } from '@/composables/useSettings'
import { useStoreGetters } from '@/store/utils'
import type { VueClass } from '@/types/commons'
const router = useRouter()
const { routerKey } = useStoreGetters()
const { reconnecting, currentRequest, dismissModal } = useRequests()
const { transitions, transitionName, dark } = useSettings()
const RootView = createReusableTemplate<{
Component: VNode
classes: VueClass
}>()
const skeleton = computed(
() => router.currentRoute.value.meta.skeleton || 'CardInfoSkeleton',
)
const modalComponent = computed(() => {
if (reconnecting.value) {
return {
is: ModalReconnecting,
props: {
reconnecting: reconnecting.value,
onDismiss: () => (reconnecting.value = undefined),
},
}
}
const request = currentRequest.value
if (!request) return null
const { status, err } = request
if (status === 'error' && err) {
return {
is: ModalError,
props: { request, onDismiss: () => dismissModal(request.id) },
}
} else if (status === 'warning') {
return {
is: ModalWarning,
props: { request, onDismiss: () => dismissModal(request.id) },
}
} else {
return { is: ModalWaiting, props: { request } }
}
})
</script>
<template>
<RootView.define v-slot="{ Component, classes }">
<BOverlay
opacity="0.75"
rounded
:show="!!modalComponent"
:variant="dark ? 'dark' : 'light'"
class="main-overlay"
>
<Suspense>
<Component :is="Component" :class="classes" />
<template #fallback>
<Component :is="skeleton" />
</template>
</Suspense>
<template v-if="modalComponent" #overlay>
<Component :is="modalComponent.is" v-bind="modalComponent.props" />
</template>
</BOverlay>
</RootView.define>
<YBreadcrumb />
<main id="main">
<!-- The `key` on RouterView make sure that if a link points to a page that
use the same component as the previous one, it will be refreshed -->
<RouterView v-slot="{ Component }" :key="routerKey">
<Transition v-if="transitions" :name="transitionName">
<RootView.reuse v-bind="{ Component, classes: 'animated' }" />
</Transition>
<RootView.reuse v-else v-bind="{ Component, classes: 'static' }" />
</RouterView>
</main>
</template>
<style lang="scss" scoped>
main {
position: relative;
// Routes transition
.animated {
transition: all 0.15s ease-in-out;
}
.slide-left-enter-from,
.slide-right-leave-active {
position: absolute;
width: 100%;
top: 0;
transform: translate(100vw, 0);
}
.slide-left-leave-active,
.slide-right-enter-from {
position: absolute;
width: 100%;
top: 0;
transform: translate(-100vw, 0);
}
// hack to hide last transition provoqued by the <RouterView> element change
// while disabling the transitions in ToolWebAdmin
.static ~ .animated {
display: none;
}
.main-overlay :deep(.b-overlay :first-child) {
width: calc(100% + 20px);
height: calc(100% + 20px);
transform: translate(-10px, -10px);
}
}
</style>

View file

@ -124,6 +124,7 @@ $h4-font-size: $font-size-base * 1.25;
$h5-font-size: $font-size-base * 1.1;
$list-group-item-padding-y: $spacer * 0.75;
$modal-md: 600px;
// Import default variables after the above setup to compute all other variables.
@import '~bootstrap/scss/variables';