217 lines
5.5 KiB
Vue
217 lines
5.5 KiB
Vue
|
|
<script setup>
|
|||
|
|
import { VerticalNav } from '@layouts/components'
|
|||
|
|
import { useLayoutConfigStore } from '@layouts/stores/config'
|
|||
|
|
|
|||
|
|
const props = defineProps({
|
|||
|
|
navItems: {
|
|||
|
|
type: null,
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
verticalNavAttrs: {
|
|||
|
|
type: Object,
|
|||
|
|
required: false,
|
|||
|
|
default: () => ({}),
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const { width: windowWidth } = useWindowSize()
|
|||
|
|
const configStore = useLayoutConfigStore()
|
|||
|
|
const isOverlayNavActive = ref(false)
|
|||
|
|
const isLayoutOverlayVisible = ref(false)
|
|||
|
|
const toggleIsOverlayNavActive = useToggle(isOverlayNavActive)
|
|||
|
|
|
|||
|
|
// ℹ️ This is alternative to below two commented watcher
|
|||
|
|
|
|||
|
|
// We want to show overlay if overlay nav is visible and want to hide overlay if overlay is hidden and vice versa.
|
|||
|
|
syncRef(isOverlayNavActive, isLayoutOverlayVisible)
|
|||
|
|
|
|||
|
|
// })
|
|||
|
|
|
|||
|
|
// ℹ️ Hide overlay if user open overlay nav in <md and increase the window width without closing overlay nav
|
|||
|
|
watch(windowWidth, () => {
|
|||
|
|
if (!configStore.isLessThanOverlayNavBreakpoint && isLayoutOverlayVisible.value)
|
|||
|
|
isLayoutOverlayVisible.value = false
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const verticalNavAttrs = computed(() => {
|
|||
|
|
const vNavAttrs = toRef(props, 'verticalNavAttrs')
|
|||
|
|
|
|||
|
|
const {
|
|||
|
|
wrapper: verticalNavWrapper,
|
|||
|
|
wrapperProps: verticalNavWrapperProps,
|
|||
|
|
...additionalVerticalNavAttrs
|
|||
|
|
} = vNavAttrs.value
|
|||
|
|
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
verticalNavWrapper,
|
|||
|
|
verticalNavWrapperProps,
|
|||
|
|
additionalVerticalNavAttrs,
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div
|
|||
|
|
class="layout-wrapper"
|
|||
|
|
data-allow-mismatch
|
|||
|
|
:class="configStore._layoutClasses"
|
|||
|
|
>
|
|||
|
|
<component
|
|||
|
|
:is="verticalNavAttrs.verticalNavWrapper ? verticalNavAttrs.verticalNavWrapper : 'div'"
|
|||
|
|
v-bind="verticalNavAttrs.verticalNavWrapperProps"
|
|||
|
|
class="vertical-nav-wrapper"
|
|||
|
|
>
|
|||
|
|
<VerticalNav
|
|||
|
|
:is-overlay-nav-active="isOverlayNavActive"
|
|||
|
|
:toggle-is-overlay-nav-active="toggleIsOverlayNavActive"
|
|||
|
|
:nav-items="props.navItems"
|
|||
|
|
v-bind="{ ...verticalNavAttrs.additionalVerticalNavAttrs }"
|
|||
|
|
>
|
|||
|
|
<template #nav-header>
|
|||
|
|
<slot name="vertical-nav-header" />
|
|||
|
|
</template>
|
|||
|
|
<template #before-nav-items>
|
|||
|
|
<slot name="before-vertical-nav-items" />
|
|||
|
|
</template>
|
|||
|
|
</VerticalNav>
|
|||
|
|
</component>
|
|||
|
|
<div class="layout-content-wrapper">
|
|||
|
|
<header
|
|||
|
|
class="layout-navbar"
|
|||
|
|
:class="[{ 'navbar-blur': configStore.isNavbarBlurEnabled }]"
|
|||
|
|
>
|
|||
|
|
<div class="navbar-content-container">
|
|||
|
|
<slot
|
|||
|
|
name="navbar"
|
|||
|
|
:toggle-vertical-overlay-nav-active="toggleIsOverlayNavActive"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</header>
|
|||
|
|
<main class="layout-page-content">
|
|||
|
|
<div class="page-content-container">
|
|||
|
|
<slot />
|
|||
|
|
</div>
|
|||
|
|
</main>
|
|||
|
|
<footer class="layout-footer"
|
|||
|
|
v-if="!$route.meta.hideFooter">
|
|||
|
|
<div class="footer-content-container">
|
|||
|
|
<slot name="footer" />
|
|||
|
|
</div>
|
|||
|
|
</footer>
|
|||
|
|
</div>
|
|||
|
|
<div
|
|||
|
|
class="layout-overlay"
|
|||
|
|
:class="[{ visible: isLayoutOverlayVisible }]"
|
|||
|
|
@click="() => { isLayoutOverlayVisible = !isLayoutOverlayVisible }"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style lang="scss">
|
|||
|
|
@use "@configured-variables" as variables;
|
|||
|
|
@use "@layouts/styles/placeholders";
|
|||
|
|
@use "@layouts/styles/mixins";
|
|||
|
|
|
|||
|
|
.layout-wrapper.layout-nav-type-vertical {
|
|||
|
|
// TODO(v2): Check why we need height in vertical nav & min-height in horizontal nav
|
|||
|
|
block-size: 100%;
|
|||
|
|
|
|||
|
|
.layout-content-wrapper {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
flex-grow: 1;
|
|||
|
|
min-block-size: 100dvh;
|
|||
|
|
transition: padding-inline-start 0.2s ease-in-out;
|
|||
|
|
will-change: padding-inline-start;
|
|||
|
|
|
|||
|
|
@media screen and (min-width: 1280px) {
|
|||
|
|
padding-inline-start: variables.$layout-vertical-nav-width;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.layout-navbar {
|
|||
|
|
z-index: variables.$layout-vertical-nav-layout-navbar-z-index;
|
|||
|
|
|
|||
|
|
.navbar-content-container {
|
|||
|
|
block-size: variables.$layout-vertical-nav-navbar-height;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@at-root {
|
|||
|
|
.layout-wrapper.layout-nav-type-vertical {
|
|||
|
|
.layout-navbar {
|
|||
|
|
@if variables.$layout-vertical-nav-navbar-is-contained {
|
|||
|
|
@include mixins.boxed-content;
|
|||
|
|
}
|
|||
|
|
/* stylelint-disable-next-line @stylistic/indentation */
|
|||
|
|
@else {
|
|||
|
|
.navbar-content-container {
|
|||
|
|
@include mixins.boxed-content;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.layout-navbar-sticky .layout-navbar {
|
|||
|
|
@extend %layout-navbar-sticky;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.layout-navbar-hidden .layout-navbar {
|
|||
|
|
@extend %layout-navbar-hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 👉 Footer
|
|||
|
|
.layout-footer {
|
|||
|
|
@include mixins.boxed-content;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 👉 Layout overlay
|
|||
|
|
.layout-overlay {
|
|||
|
|
position: fixed;
|
|||
|
|
z-index: variables.$layout-overlay-z-index;
|
|||
|
|
background-color: rgb(0 0 0 / 60%);
|
|||
|
|
cursor: pointer;
|
|||
|
|
inset: 0;
|
|||
|
|
opacity: 0;
|
|||
|
|
pointer-events: none;
|
|||
|
|
transition: opacity 0.25s ease-in-out;
|
|||
|
|
will-change: opacity;
|
|||
|
|
|
|||
|
|
&.visible {
|
|||
|
|
opacity: 1;
|
|||
|
|
pointer-events: auto;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Adjust right column pl when vertical nav is collapsed
|
|||
|
|
&.layout-vertical-nav-collapsed .layout-content-wrapper {
|
|||
|
|
@media screen and (min-width: 1280px) {
|
|||
|
|
padding-inline-start: variables.$layout-vertical-nav-collapsed-width;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 👉 Content height fixed
|
|||
|
|
&.layout-content-height-fixed {
|
|||
|
|
.layout-content-wrapper {
|
|||
|
|
max-block-size: 100dvh;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.layout-page-content {
|
|||
|
|
display: flex;
|
|||
|
|
overflow: hidden;
|
|||
|
|
|
|||
|
|
.page-content-container {
|
|||
|
|
inline-size: 100%;
|
|||
|
|
|
|||
|
|
> :first-child {
|
|||
|
|
max-block-size: 100%;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|