Files
tehran_taj/apps/user-app/src/layout/MasterLayout.vue
T

93 lines
3.1 KiB
Vue

<!--
Ported from legacy henza_pwa/src/layout/master.vue.
This layout renders its own <router-view/> rather than a <slot/>, so the
keep-alive/suspense wrapper App.vue builds around its own <router-view> never
reaches these routes. The keep-alive below is the one that applies: the main
tabs stay mounted between switches instead of refetching on every visit.
-->
<template>
<section ref="content">
<router-view v-slot="{ Component, route }">
<transition
name="page"
mode="out-in"
@before-leave="pageLeaveStarted"
@after-leave="pageLeaveEnded"
@leave-cancelled="pageLeaveEnded"
>
<keep-alive :include="KEEP_ALIVE">
<component :is="Component" :key="route.name" />
</keep-alive>
</transition>
</router-view>
<Navigation />
<div class="bg-primary top-[-2rem] fixed h-[2rem] w-full z-10" />
</section>
</template>
<script setup lang="ts">
import { onBeforeMount, ref } from 'vue'
import { useResizeObserver } from '@vueuse/core'
import Navigation from '../components/ui/Navigation.vue'
import { useWindowDataStore } from '../stores/windowDataStore'
import { pageLeaveStarted, pageLeaveEnded } from '../composables/usePageTransition'
// Component names (from the SFC file names) of the bottom-nav tabs. Forms and
// the product page are left out: they should open fresh every time.
const KEEP_ALIVE = ['HomeView', 'SearchView', 'OrdersView', 'DashboardView']
const windowData = useWindowDataStore()
const content = ref<HTMLElement | null>(null)
// Route changes here are tab switches, not a hierarchy, so there is no "back"
// or "forward" direction to honour. The outgoing page fades out quickly and
// the new one fades in; the sense of the page settling into place comes from
// each screen's own section reveal (composables/useReveal.ts).
onBeforeMount(() => {
windowData.setWidth(window.innerWidth)
})
useResizeObserver(content, (entries) => {
const entry = entries[0]
const { width } = entry.contentRect
if (windowData.width !== width) {
windowData.setWidth(width)
}
})
</script>
<!--
Not scoped: these classes land on the routed page component, which carries
its own scope id, not this layout's.
Opacity only, deliberately. A transform (or filter) on the page root makes it
the containing block for every position: fixed element inside the page — the
top bars, PageBar, the cart's checkout bar — so for the length of the
transition they would ride along with the page, then snap back to the screen
edge when it ends. Opacity has no such effect, so nothing jumps.
-->
<style lang="less">
// Leave is deliberately shorter than enter. With mode="out-in" the two run
// back to back, so a slow exit is dead time the user feels as lag; a quick
// fade out and an unhurried fade in reads as responsive but smooth.
.page-leave-active {
transition: opacity 0.16s cubic-bezier(0.4, 0, 1, 1);
}
.page-enter-active {
transition: opacity 0.42s cubic-bezier(0.32, 0.72, 0, 1);
}
.page-enter-from,
.page-leave-to {
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.page-enter-active,
.page-leave-active {
transition: opacity 0.12s linear;
}
}
</style>