| 1 | <template> |
| 2 | <div |
| 3 | id="app-main" |
| 4 | class="main" |
| 5 | :class="{ 'sidebar-collapsed': sidebarCollapsed, 'sidebar-opened': !sidebarCollapsed }" |
| 6 | > |
| 7 | <n-scrollbar |
| 8 | ref="scrollbar" |
| 9 | trigger="none" |
| 10 | :y-placement="isRTL ? 'left' : 'right'" |
| 11 | content-class="flex min-h-full flex-col" |
| 12 | :theme-overrides="{ |
| 13 | railInsetVerticalRight: `${toolbarHeight}px 4px 4px auto`, |
| 14 | railInsetVerticalLeft: `${toolbarHeight}px auto 4px 4px` |
| 15 | }" |
| 16 | > |
| 17 | <Toolbar :boxed="toolbarBoxed" gradient="body" /> |
| 18 | <div |
| 19 | id="app-view" |
| 20 | class="view" |
| 21 | :class="{ boxed, 'view-padded': overridePadded === true, 'view-no-padded': overridePadded === false }" |
| 22 | > |
| 23 | <slot /> |
| 24 | </div> |
| 25 | <MainFooter v-if="footerShown" :boxed /> |
| 26 | </n-scrollbar> |
| 27 | </div> |
| 28 | </template> |
| 29 | |
| 30 | <script lang="ts" setup> |
| 31 | import type { RouteLocationNormalizedGeneric } from "vue-router" |
| 32 | import { NScrollbar } from "naive-ui" |
| 33 | import { computed, onMounted, ref } from "vue" |
| 34 | import { useRoute, useRouter } from "vue-router" |
| 35 | import MainFooter from "@/app-layouts/common/MainFooter.vue" |
| 36 | import Toolbar from "@/app-layouts/common/Toolbar" |
| 37 | import { useThemeStore } from "@/stores/theme" |
| 38 | |
| 39 | const themeStore = useThemeStore() |
| 40 | const router = useRouter() |
| 41 | const route = useRoute() |
| 42 | const sidebarCollapsed = computed(() => themeStore.sidebar.collapsed) |
| 43 | const footerShown = computed(() => themeStore.isFooterShown) |
| 44 | const themeBoxed = computed<boolean>(() => themeStore.isBoxed) |
| 45 | const overrideBoxed = ref<boolean | undefined>(undefined) |
| 46 | const overridePadded = ref<boolean | undefined>(undefined) |
| 47 | const boxed = computed<boolean>(() => (overrideBoxed.value !== undefined ? overrideBoxed.value : themeBoxed.value)) |
| 48 | const isRTL = computed<boolean>(() => themeStore.isRTL) |
| 49 | const toolbarBoxed = computed(() => themeStore.isToolbarBoxed) |
| 50 | const toolbarHeight = computed(() => themeStore.toolbarHeight) |
| 51 | const scrollbar = ref() |
| 52 | |
| 53 | function checkThemeOverrides(currentRoute: RouteLocationNormalizedGeneric) { |
| 54 | if (currentRoute.meta?.theme?.boxed?.enabled !== undefined) { |
| 55 | overrideBoxed.value = currentRoute.meta.theme.boxed.enabled |
| 56 | } else { |
| 57 | overrideBoxed.value = undefined |
| 58 | } |
| 59 | |
| 60 | if (currentRoute.meta?.theme?.padded?.enabled !== undefined) { |
| 61 | overridePadded.value = currentRoute.meta.theme.padded.enabled |
| 62 | } else { |
| 63 | overridePadded.value = undefined |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | router.beforeEach(checkThemeOverrides) |
| 68 | |
| 69 | onMounted(() => { |
| 70 | checkThemeOverrides(route) |
| 71 | |
| 72 | router.afterEach(() => { |
| 73 | if (scrollbar?.value?.scrollTo) { |
| 74 | scrollbar?.value.scrollTo({ top: 0, behavior: "smooth" }) |
| 75 | } |
| 76 | }) |
| 77 | }) |
| 78 | </script> |
| 79 | |
| 80 | <style lang="scss" scoped> |
| 81 | @import "./variables"; |
| 82 | |
| 83 | .main { |
| 84 | width: 100%; |
| 85 | position: relative; |
| 86 | padding-left: var(--sidebar-open-width); |
| 87 | background-color: var(--bg-body-color); |
| 88 | transition: all var(--sidebar-anim-ease) var(--sidebar-anim-duration); |
| 89 | display: flex; |
| 90 | flex-direction: column; |
| 91 | |
| 92 | &.sidebar-collapsed { |
| 93 | padding-left: var(--sidebar-close-width); |
| 94 | } |
| 95 | |
| 96 | .view { |
| 97 | padding: 0 var(--view-padding); |
| 98 | flex-grow: 1; |
| 99 | width: 100%; |
| 100 | margin: 0 auto; |
| 101 | display: flex; |
| 102 | flex-direction: column; |
| 103 | |
| 104 | &.boxed { |
| 105 | max-width: var(--boxed-width); |
| 106 | } |
| 107 | &.view-no-padded { |
| 108 | padding: 0; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | @media (max-width: $sidebar-bp) { |
| 113 | padding-left: 0px; |
| 114 | |
| 115 | &.sidebar-collapsed { |
| 116 | padding-left: 0px; |
| 117 | } |
| 118 | |
| 119 | &.sidebar-opened { |
| 120 | /* |
| 121 | transform: scale(0.8) translateX(100%) rotateY(35deg); |
| 122 | transform-origin: center left; |
| 123 | border-radius: 16px; |
| 124 | */ |
| 125 | |
| 126 | overflow: hidden; |
| 127 | opacity: 0.5; |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | @media (min-width: ($sidebar-bp + 1px)) { |
| 132 | :deep() { |
| 133 | header.toolbar { |
| 134 | .logo-box { |
| 135 | display: none; |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | .direction-rtl { |
| 143 | .main { |
| 144 | padding-left: unset; |
| 145 | padding-right: var(--sidebar-open-width); |
| 146 | |
| 147 | &.sidebar-collapsed { |
| 148 | padding-left: unset; |
| 149 | padding-right: var(--sidebar-close-width); |
| 150 | } |
| 151 | |
| 152 | @media (max-width: $sidebar-bp) { |
| 153 | padding-right: 0px; |
| 154 | |
| 155 | &.sidebar-collapsed { |
| 156 | padding-right: 0px; |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | </style> |