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 | |
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | </div>
|
67 | </nav>
|
68 | </template>
|
69 |
|
70 | <script>
|
71 | import PerfectScrollbar from 'perfect-scrollbar';
|
72 | import { mapState } from 'vuex';
|
73 | export 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;
|
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>
|