main
vue 135 lines 2.89 KB
Raw
1 <template>
2 <aside
3 id="app-sidebar"
4 class="sidebar flex flex-col"
5 :class="{ collapsed: sidebarCollapsed, opened: !sidebarCollapsed }"
6 >
7 <div ref="sidebar" class="sidebar-wrap flex grow flex-col">
8 <div :class="{ 'px-7': !sidebarClosed, 'px-2': sidebarClosed }" class="transition-all">
9 <SidebarHeader :logo-small="sidebarClosed" />
10 </div>
11 <n-scrollbar :trigger="sidebarClosed ? 'hover' : 'none'">
12 <div :class="{ 'px-2': !sidebarClosed }" class="transition-all">
13 <Navbar :collapsed="sidebarClosed" />
14 </div>
15 </n-scrollbar>
16 <div class="p-2">
17 <SidebarFooter :collapsed="sidebarClosed" />
18 </div>
19 </div>
20 </aside>
21 </template>
22
23 <script lang="ts" setup>
24 import { onClickOutside, useElementHover } from "@vueuse/core"
25 import { NScrollbar } from "naive-ui"
26 import { computed, onMounted, ref, watch } from "vue"
27 import Navbar from "@/app-layouts/common/Navbar"
28 import { useThemeStore } from "@/stores/theme"
29 import { isMobile } from "@/utils"
30 import SidebarFooter from "./SidebarFooter.vue"
31 import SidebarHeader from "./SidebarHeader.vue"
32
33 const themeStore = useThemeStore()
34 const sidebar = ref(null)
35 const sidebarHovered = useElementHover(sidebar)
36 const sidebarCollapsed = computed(() => themeStore.sidebar.collapsed)
37 const sidebarClosed = computed(() => !sidebarHovered.value && sidebarCollapsed.value)
38
39 function clickListener() {
40 if (sidebar.value) {
41 onClickOutside(sidebar, e => {
42 if (!sidebarCollapsed.value) {
43 e.stopPropagation()
44 themeStore.closeSidebar()
45 }
46 })
47 }
48 }
49
50 onMounted(() => {
51 watch(
52 sidebarCollapsed,
53 val => {
54 if (val) {
55 if (isMobile()) {
56 sidebarHovered.value = false
57 }
58 }
59 },
60 {
61 immediate: true
62 }
63 )
64
65 if (window.innerWidth <= 700) {
66 clickListener()
67 }
68 })
69 </script>
70
71 <style lang="scss" scoped>
72 @import "./variables";
73
74 .sidebar {
75 position: fixed;
76 z-index: 5;
77 top: 0;
78 left: 0;
79 width: var(--sidebar-open-width);
80 height: 100vh;
81 height: 100svh;
82 overflow-x: hidden;
83 overflow-y: auto;
84 background-color: var(--bg-sidebar-color);
85 transition:
86 width var(--sidebar-anim-ease) var(--sidebar-anim-duration),
87 box-shadow var(--sidebar-anim-ease) var(--sidebar-anim-duration),
88 color 0.3s var(--bezier-ease) 0s,
89 background-color 0.3s var(--bezier-ease) 0s;
90
91 .sidebar-wrap {
92 overflow: hidden;
93 }
94
95 :deep(.n-scrollbar-rail) {
96 opacity: 0.15;
97 }
98
99 &.collapsed {
100 width: var(--sidebar-close-width);
101
102 &:hover {
103 width: var(--sidebar-open-width);
104 box-shadow: 0px 0px 80px 0px rgba(0, 0, 0, 0.2);
105 }
106 }
107
108 @media (max-width: $sidebar-bp) {
109 z-index: -1;
110 transition: all 0.3s var(--bezier-ease) 0s;
111 transform: translateX(-100%);
112
113 &.opened {
114 z-index: 2100;
115 transform: translateX(0);
116 box-shadow: 0px 0px 80px 0px rgba(0, 0, 0, 0.2);
117 }
118 }
119 }
120
121 .direction-rtl {
122 .sidebar {
123 left: unset;
124 right: 0;
125
126 @media (max-width: $sidebar-bp) {
127 transform: translateX(100%);
128
129 &.opened {
130 transform: translateX(0%);
131 }
132 }
133 }
134 }
135 </style>