UNPKG

7.87 kBPlain TextView Raw
1<template>
2 <nav class="sidebar">
3 <div
4 class="sidebar-showMore"
5 @click="isShowingMore = !isShowingMore"
6 :class="{
7 'sidebar-showMore--folded': isShowingMoreFolded,
8 'sidebar-showMore--crossed': isShowingMoreCrossed
9 }"
10 >
11 <div class="sidebar-showMoreItem sidebar-showMoreItem--top"></div>
12 <div class="sidebar-showMoreItem sidebar-showMoreItem--mid"></div>
13 <div class="sidebar-showMoreItem sidebar-showMoreItem--bottom"></div>
14 </div>
15 <div
16 class="sidebar-menu"
17 :class="{
18 'sidebar-menu--folded': !isShowingMore
19 }"
20 >
21 <div v-for="(links, group) in showLinks" :key="group">
22 <!-- 不在文件夹中 -->
23 <router-link
24 v-if="links.length === 1"
25 class="sidebar-menuItem"
26 :class="{
27 'sidebar-menuItem--active': currentDemo === links[0].label
28 }"
29 :to="`/${links[0].src}`"
30 >
31 {{ links[0].label }}
32 </router-link>
33 <div v-else class="sidebar-menu__folder">
34 <p class="sidebar-menu__folderTitle" @click="toogleVisible(group)">
35 {{ group }}
36 </p>
37 <div
38 class="sidebar-menu__folderContent"
39 v-show="unfolded.indexOf(group) > -1"
40 >
41 <router-link
42 v-for="link in links"
43 :key="link.src"
44 class="sidebar-menuItem"
45 :class="{
46 'sidebar-menuItem--active': currentDemo === link.label
47 }"
48 :to="`/${link.src}`"
49 >
50 {{ link.label }}
51 </router-link>
52 </div>
53 </div>
54 </div>
55 <!-- <router-link
56 v-for="demo in showLinks"
57 :key="demo.src"
58 class="sidebar-menuItem"
59 :class="{
60 'sidebar-menuItem--active': currentDemo === demo.label
61 }"
62 :to="`/${demo.src}`"
63 >
64 {{ demo.label }}
65 </router-link> -->
66 </div>
67 </nav>
68</template>
69
70<script>
71import PerfectScrollbar from 'perfect-scrollbar';
72import { mapState } from 'vuex';
73export default {
74 name: 'sidebar',
75 data() {
76 return {
77 isShowingMore: false,
78 showingMoreTimer: null,
79 isShowingMoreFolded: false,
80 isShowingMoreCrossed: false,
81 unfolded: []
82 };
83 },
84 watch: {
85 isShowingMore(val) {
86 clearTimeout(this.showingMoreTimer);
87 if (val) {
88 this.isShowingMoreFolded = true;
89 this.showingMoreTimer = setTimeout(() => {
90 this.isShowingMoreCrossed = true;
91 }, 300);
92 } else {
93 this.isShowingMoreCrossed = false;
94 this.showingMoreTimer = setTimeout(() => {
95 this.isShowingMoreFolded = false;
96 }, 300);
97 }
98 }
99 },
100 computed: {
101 ...mapState(['links']),
102 ...mapState({
103 showLinks(state) {
104 const groups = {};
105 state.links.forEach(link => {
106 // 定义的组
107 if ('demos' in link) {
108 if (!groups[link.group]) groups[link.group] = [];
109 groups[link.group] = [
110 ...groups[link.group],
111 ...link.demos.map(demo => ({
112 label: demo.label,
113 src: `${link.src}/${demo.src}`
114 }))
115 ];
116 } else {
117 const groupName = link.src.split('/')[0];
118 if (!groups[groupName]) groups[groupName] = [];
119 groups[groupName].push(link);
120 }
121 });
122 return groups;
123 }
124 }),
125 currentDemo() {
126 this.isShowingMore = false; // eslint-disable-line vue/no-side-effects-in-computed-properties
127 return this.$route.name;
128 },
129 currentGroup() {
130 const src = this.$route.name;
131 if (src) {
132 const group = src.split('/')[0];
133 return group;
134 }
135 return null;
136 }
137 },
138 mounted() {
139 new PerfectScrollbar(document.querySelector('.sidebar-menu'), {
140 suppressScrollX: true
141 });
142 if (this.currentGroup) {
143 const index = this.unfolded.indexOf(this.currentGroup);
144 if (index == -1) {
145 this.unfolded.push(this.currentGroup);
146 }
147 }
148 },
149 methods: {
150 toogleVisible(group) {
151 const index = this.unfolded.indexOf(group);
152 if (index > -1) {
153 this.unfolded.splice(index, 1);
154 } else {
155 this.unfolded.push(group);
156 }
157 }
158 }
159};
160</script>
161
162<style lang="scss">
163@import '@/css/index.scss';
164@import '~perfect-scrollbar/css/perfect-scrollbar.css';
165$foldedDealy: 100ms;
166.sidebar {
167 font-family: $link-font-family;
168 position: relative;
169 &-menu {
170 list-style: none;
171 margin: 0;
172 padding: 10px 0 0 0;
173 max-height: calc(100vh - 80px);
174 display: flex;
175 flex-direction: column;
176 &Item {
177 font-size: 14px;
178 line-height: 1.8;
179 padding: 0 0 0 20px;
180 color: rgba($c-font, 0.4);
181 font-weight: 300;
182 transition: 0.3s all ease;
183 cursor: pointer;
184 text-decoration: none;
185 display: block;
186 &:hover {
187 color: $c-font;
188 transform: translateX(4px);
189 }
190 &.router-link-active {
191 opacity: 1;
192 color: $c-highlight;
193 position: relative;
194 &::before {
195 content: '';
196 width: 2px;
197 height: 60%;
198 background: $c-highlight;
199 position: absolute;
200 left: 10px;
201 top: 20%;
202 }
203 &:hover {
204 color: $c-highlight;
205 }
206 }
207 }
208 &__folder {
209 &Title {
210 margin: 0;
211 padding: 0;
212 font-size: 14px;
213 line-height: 1.8;
214 padding: 0 0 0 20px;
215 color: rgba($c-font, 0.4);
216 font-weight: 300;
217 cursor: pointer;
218 transition: color 0.3s ease-out;
219 position: relative;
220 &:hover {
221 color: $c-font;
222 &:after {
223 border-color: $c-font transparent transparent transparent;
224 }
225 }
226 &:after {
227 content: '';
228 position: absolute;
229 width: 0;
230 height: 0;
231 border-style: solid;
232 border-width: 6px 4px 0 4px;
233 border-color: rgba($c-font, 0.4) transparent transparent transparent;
234 top: 10px;
235 left: 8px;
236 }
237 }
238 &Content {
239 display: flex;
240 flex-direction: column;
241 background: darken($c-bg, 3%);
242 }
243 }
244 }
245 &-showMore {
246 display: none;
247 box-sizing: border-box;
248 width: 60px;
249 height: 60px;
250 padding: 28px 15px;
251 position: relative;
252 &--folded {
253 & .sidebar-showMoreItem--top,
254 .sidebar-showMoreItem--bottom {
255 transform: translateY(0);
256 }
257 & .sidebar-showMoreItem--mid {
258 transform: scaleX(0);
259 }
260 }
261 &--crossed {
262 & .sidebar-showMoreItem--top {
263 transform: translateY(0) rotate(0.375turn);
264 }
265 & .sidebar-showMoreItem--mid {
266 transform: scaleX(0);
267 }
268 & .sidebar-showMoreItem--bottom {
269 transform: translateY(0) rotate(0.125turn);
270 }
271 }
272 &Item {
273 background: white;
274 width: 30px;
275 height: 4px;
276 position: absolute;
277 border-radius: 10px;
278 transition: 300ms all ease-out;
279 &--top {
280 transform: translateY(-10px);
281 }
282 &--bottom {
283 transform: translateY(10px);
284 }
285 }
286 }
287}
288
289@media (max-width: $c-small-screen) {
290 .sidebar-showMore {
291 display: block;
292 }
293 .sidebar-menu {
294 position: absolute;
295 top: 60px;
296 left: 0;
297 z-index: 1000;
298 width: 300px;
299 padding: 10px 0;
300 background: lighten($c-bg, 5);
301 transition: 0.3s all ease-out;
302 height: calc(100vh - 80px);
303 box-sizing: border-box;
304 transform-origin: left center;
305 &--folded {
306 opacity: 0;
307 transform: translateX(-100%);
308 // transform: perspective(3000px) rotateY(-90deg);
309 transition-duration: 0.6s;
310 }
311 }
312}
313</style>