Skip to content

Commit 5780461

Browse files
antfukiakingyyx990803
authoredJul 21, 2020
feat: nav dropdown (#51)
* feat: add external link support for nav items * feat: support nav dropdown Co-authored-by: Kia Ishii <kia.king.08@gmail.com> Co-authored-by: Evan You <yyx990803@gmail.com>
1 parent 44e91bb commit 5780461

File tree

4 files changed

+210
-6
lines changed

4 files changed

+210
-6
lines changed
 

Diff for: ‎src/client/theme-default/components/NavBar.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { computed } from 'vue'
22
import { useSiteData } from 'vitepress'
33
import NavBarLink from './NavBarLink.vue'
4+
import NavDropdownLink from './NavDropdownLink.vue'
45

56
export default {
67
components: {
7-
NavBarLink
8+
NavBarLink,
9+
NavDropdownLink
810
},
911

1012
setup() {

Diff for: ‎src/client/theme-default/components/NavBar.vue

+4-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
<span>{{ $site.title }}</span>
1414
</a>
1515
<nav class="nav-links" v-if="navData">
16-
<NavBarLink
17-
v-for="item of navData"
18-
:key="item.link"
19-
:item="item"
20-
/>
16+
<template v-for="item of navData">
17+
<NavDropdownLink v-if='item.items' :item="item"/>
18+
<NavBarLink v-else :item="item"/>
19+
</template>
2120
</nav>
2221
</template>
2322

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import NavBarLink from './NavBarLink.vue'
2+
import { defineComponent, ref, watch, PropType } from 'vue'
3+
import { useRoute } from 'vitepress'
4+
import { DefaultTheme } from '../config'
5+
6+
export default defineComponent({
7+
name: 'DropdownLink',
8+
components: {
9+
NavBarLink
10+
},
11+
props: {
12+
item: {
13+
type: Object as PropType<DefaultTheme.NavItemWithChildren>,
14+
required: true
15+
}
16+
},
17+
setup(props) {
18+
const open = ref(false)
19+
const route = useRoute()
20+
21+
watch(
22+
() => route.path,
23+
() => {
24+
open.value = false
25+
}
26+
)
27+
28+
const setOpen = (value: boolean) => {
29+
open.value = value
30+
}
31+
32+
const isLastItemOfArray = <T>(item: T, array: T[]) => {
33+
return array.length && array.indexOf(item) === array.length - 1
34+
}
35+
36+
return {
37+
open,
38+
setOpen,
39+
isLastItemOfArray
40+
}
41+
}
42+
})
+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<template>
2+
<div class="dropdown-wrapper" :class="{ open }">
3+
<button
4+
class="dropdown-title"
5+
type="button"
6+
:aria-label="item.ariaLabel"
7+
@click="setOpen(!open)"
8+
>
9+
<span>{{ item.text }}</span>
10+
<span class="arrow" :class="open ? 'down' : 'right'" />
11+
</button>
12+
13+
<ul v-show="open" class="nav-dropdown">
14+
<li v-for="(subItem, index) in item.items" :key="subItem.link || index" class="dropdown-item">
15+
<h4 v-if="subItem.items">{{ subItem.text }}</h4>
16+
<ul v-if="subItem.items" class="dropdown-subitem-wrapper">
17+
<li
18+
v-for="childSubItem in subItem.items"
19+
:key="childSubItem.link"
20+
class="dropdown-subitem"
21+
>
22+
<NavBarLink
23+
:item="childSubItem"
24+
@focusout="
25+
isLastItemOfArray(childSubItem, subItem.items) &&
26+
isLastItemOfArray(subItem, item.items) &&
27+
setOpen(false)
28+
"
29+
/>
30+
</li>
31+
</ul>
32+
33+
<NavBarLink
34+
v-else
35+
:item="subItem"
36+
@focusout="isLastItemOfArray(subItem, item.items) && setOpen(false)"
37+
/>
38+
</li>
39+
</ul>
40+
</div>
41+
</template>
42+
43+
<script src="./NavDropdownLink"></script>
44+
45+
<style>
46+
.dropdown-wrapper {
47+
position: relative;
48+
cursor: pointer;
49+
display: inline-block;
50+
margin-left: 1.5rem;
51+
}
52+
.dropdown-wrapper .dropdown-title {
53+
font: inherit;
54+
color: var(--text-color);
55+
font-weight: 600;
56+
display: inline-block;
57+
height: 1.75rem;
58+
line-height: 1.75rem;
59+
padding: inherit;
60+
background: transparent;
61+
border: none;
62+
}
63+
.dropdown-wrapper .dropdown-title:hover {
64+
border-color: transparent;
65+
}
66+
67+
.dropdown-wrapper .dropdown-title .arrow {
68+
display: inline-block;
69+
vertical-align: middle;
70+
margin-top: -1px;
71+
margin-left: 0.4rem;
72+
}
73+
74+
.dropdown-wrapper .nav-dropdown .dropdown-item {
75+
color: inherit;
76+
line-height: 1.7rem;
77+
}
78+
.dropdown-wrapper .nav-dropdown .dropdown-item h4 {
79+
margin: 0.45rem 0 0;
80+
border-top: 1px solid #eee;
81+
padding: 0.45rem 1.5rem 0 1.25rem;
82+
}
83+
.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper {
84+
padding: 0;
85+
list-style: none;
86+
}
87+
.dropdown-wrapper
88+
.nav-dropdown
89+
.dropdown-item
90+
.dropdown-subitem-wrapper
91+
.dropdown-subitem {
92+
font-size: 0.9em;
93+
}
94+
.dropdown-wrapper .nav-dropdown .dropdown-item a {
95+
display: block;
96+
line-height: 1.7rem;
97+
position: relative;
98+
border-bottom: none;
99+
font-weight: 400;
100+
margin-bottom: 0;
101+
margin-left: 0;
102+
padding: 0 1.5rem 0 1.25rem;
103+
}
104+
.dropdown-wrapper .nav-dropdown .dropdown-item a:hover {
105+
color: var(--accent-color);
106+
}
107+
.dropdown-wrapper .nav-dropdown .dropdown-item a.active {
108+
color: var(--accent-color);
109+
}
110+
.dropdown-wrapper .nav-dropdown .dropdown-item a.active::after {
111+
content: '';
112+
width: 0;
113+
height: 0;
114+
border-left: 5px solid var(--accent-color);
115+
border-top: 3px solid transparent;
116+
border-bottom: 3px solid transparent;
117+
position: absolute;
118+
top: calc(50% - 2px);
119+
left: 9px;
120+
}
121+
.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4 {
122+
margin-top: 0;
123+
padding-top: 0;
124+
border-top: 0;
125+
}
126+
127+
.dropdown-wrapper {
128+
height: 1.8rem;
129+
}
130+
.dropdown-wrapper:hover .nav-dropdown,
131+
.dropdown-wrapper.open .nav-dropdown {
132+
display: block !important;
133+
}
134+
.dropdown-wrapper.open:blur {
135+
display: none;
136+
}
137+
.dropdown-wrapper .dropdown-title .arrow {
138+
border-left: 4px solid transparent;
139+
border-right: 4px solid transparent;
140+
border-top: 6px solid #aaa;
141+
border-bottom: 0;
142+
}
143+
.dropdown-wrapper .nav-dropdown {
144+
display: none;
145+
height: auto !important;
146+
box-sizing: border-box;
147+
max-height: calc(100vh - 2.7rem);
148+
overflow-y: auto;
149+
position: absolute;
150+
top: 100%;
151+
right: 0;
152+
background-color: #fff;
153+
padding: 0.6rem 0;
154+
border: 1px solid #ddd;
155+
border-bottom-color: #ccc;
156+
text-align: left;
157+
border-radius: 0.25rem;
158+
white-space: nowrap;
159+
margin: 0;
160+
}
161+
</style>

0 commit comments

Comments
 (0)