1 | <template>
|
2 | <div
|
3 | class="dropdown-wrapper"
|
4 | :class="{ open }"
|
5 | >
|
6 | <button
|
7 | class="dropdown-title"
|
8 | type="button"
|
9 | :aria-label="dropdownAriaLabel"
|
10 | @click="handleDropdown"
|
11 | >
|
12 | <span class="title">{{ item.text }}</span>
|
13 | <span
|
14 | class="arrow down"
|
15 | />
|
16 | </button>
|
17 | <button
|
18 | class="mobile-dropdown-title"
|
19 | type="button"
|
20 | :aria-label="dropdownAriaLabel"
|
21 | @click="setOpen(!open)"
|
22 | >
|
23 | <span class="title">{{ item.text }}</span>
|
24 | <span
|
25 | class="arrow"
|
26 | :class="open ? 'down' : 'right'"
|
27 | />
|
28 | </button>
|
29 |
|
30 | <DropdownTransition>
|
31 | <ul
|
32 | v-show="open"
|
33 | class="nav-dropdown"
|
34 | >
|
35 | <li
|
36 | v-for="(subItem, index) in item.items"
|
37 | :key="subItem.link || index"
|
38 | class="dropdown-item"
|
39 | >
|
40 | <h4 v-if="subItem.type === 'links'">
|
41 | {{ subItem.text }}
|
42 | </h4>
|
43 |
|
44 | <ul
|
45 | v-if="subItem.type === 'links'"
|
46 | class="dropdown-subitem-wrapper"
|
47 | >
|
48 | <li
|
49 | v-for="childSubItem in subItem.items"
|
50 | :key="childSubItem.link"
|
51 | class="dropdown-subitem"
|
52 | >
|
53 | <NavLink
|
54 | :item="childSubItem"
|
55 | @focusout="
|
56 | isLastItemOfArray(childSubItem, subItem.items) &&
|
57 | isLastItemOfArray(subItem, item.items) &&
|
58 | setOpen(false)
|
59 | "
|
60 | />
|
61 | </li>
|
62 | </ul>
|
63 |
|
64 | <NavLink
|
65 | v-else
|
66 | :item="subItem"
|
67 | @focusout="isLastItemOfArray(subItem, item.items) && setOpen(false)"
|
68 | />
|
69 | </li>
|
70 | </ul>
|
71 | </DropdownTransition>
|
72 | </div>
|
73 | </template>
|
74 |
|
75 | <script>
|
76 | import NavLink from '@theme/components/NavLink.vue'
|
77 | import DropdownTransition from '@theme/components/DropdownTransition.vue'
|
78 | import last from 'lodash/last'
|
79 |
|
80 | export default {
|
81 | name: 'DropdownLink',
|
82 |
|
83 | components: {
|
84 | NavLink,
|
85 | DropdownTransition
|
86 | },
|
87 |
|
88 | props: {
|
89 | item: {
|
90 | required: true
|
91 | }
|
92 | },
|
93 |
|
94 | data () {
|
95 | return {
|
96 | open: false
|
97 | }
|
98 | },
|
99 |
|
100 | computed: {
|
101 | dropdownAriaLabel () {
|
102 | return this.item.ariaLabel || this.item.text
|
103 | }
|
104 | },
|
105 |
|
106 | watch: {
|
107 | $route () {
|
108 | this.open = false
|
109 | }
|
110 | },
|
111 |
|
112 | methods: {
|
113 | setOpen (value) {
|
114 | this.open = value
|
115 | },
|
116 |
|
117 | isLastItemOfArray (item, array) {
|
118 | return last(array) === item
|
119 | },
|
120 |
|
121 | /**
|
122 | * Open the dropdown when user tab and click from keyboard.
|
123 | *
|
124 | * Use event.detail to detect tab and click from keyboard. Ref: https:
|
125 | * The Tab + Click is UIEvent > KeyboardEvent, so the detail is 0.
|
126 | */
|
127 | handleDropdown () {
|
128 | const isTriggerByTab = event.detail === 0
|
129 | if (isTriggerByTab) this.setOpen(!this.open)
|
130 | }
|
131 | }
|
132 | }
|
133 | </script>
|
134 |
|
135 | <style lang="stylus">
|
136 | .dropdown-wrapper
|
137 | cursor pointer
|
138 | .dropdown-title
|
139 | display block
|
140 | font-size 0.9rem
|
141 | font-family inherit
|
142 | cursor inherit
|
143 | padding inherit
|
144 | line-height 1.4rem
|
145 | background transparent
|
146 | border none
|
147 | font-weight 500
|
148 | color $textColor
|
149 | &:hover
|
150 | border-color transparent
|
151 | .arrow
|
152 | vertical-align middle
|
153 | margin-top -1px
|
154 | margin-left 0.4rem
|
155 | .mobile-dropdown-title
|
156 | @extends .dropdown-title
|
157 | display none
|
158 | font-weight 600
|
159 | font-size inherit
|
160 | &:hover
|
161 | color $accentColor
|
162 | .nav-dropdown
|
163 | .dropdown-item
|
164 | color inherit
|
165 | line-height 1.7rem
|
166 | h4
|
167 | margin 0.45rem 0 0
|
168 | border-top 1px solid #eee
|
169 | padding 1rem 1.5rem 0.45rem 1.25rem
|
170 | .dropdown-subitem-wrapper
|
171 | padding 0
|
172 | list-style none
|
173 | .dropdown-subitem
|
174 | font-size 0.9em
|
175 | a
|
176 | display block
|
177 | line-height 1.7rem
|
178 | position relative
|
179 | border-bottom none
|
180 | font-weight 400
|
181 | margin-bottom 0
|
182 | padding 0 1.5rem 0 1.25rem
|
183 | &:hover
|
184 | color $accentColor
|
185 | &.router-link-active
|
186 | color $accentColor
|
187 | &::after
|
188 | content ""
|
189 | width 0
|
190 | height 0
|
191 | border-left 5px solid $accentColor
|
192 | border-top 3px solid transparent
|
193 | border-bottom 3px solid transparent
|
194 | position absolute
|
195 | top calc(50% - 2px)
|
196 | left 9px
|
197 | &:first-child h4
|
198 | margin-top 0
|
199 | padding-top 0
|
200 | border-top 0
|
201 |
|
202 | @media (max-width: $MQMobile)
|
203 | .dropdown-wrapper
|
204 | &.open .dropdown-title
|
205 | margin-bottom 0.5rem
|
206 | .dropdown-title
|
207 | display: none
|
208 | .mobile-dropdown-title
|
209 | display: block
|
210 | .nav-dropdown
|
211 | transition height .1s ease-out
|
212 | overflow hidden
|
213 | .dropdown-item
|
214 | h4
|
215 | border-top 0
|
216 | margin-top 0
|
217 | padding-top 0
|
218 | h4, & > a
|
219 | font-size 15px
|
220 | line-height 2rem
|
221 | .dropdown-subitem
|
222 | font-size 14px
|
223 | padding-left 1rem
|
224 |
|
225 | @media (min-width: $MQMobile)
|
226 | .dropdown-wrapper
|
227 | height 1.8rem
|
228 | &:hover .nav-dropdown,
|
229 | &.open .nav-dropdown
|
230 | // override the inline style.
|
231 | display block !important
|
232 | .nav-dropdown
|
233 | display none
|
234 | // Avoid height shaked by clicking
|
235 | height auto !important
|
236 | box-sizing border-box;
|
237 | max-height calc(100vh - 2.7rem)
|
238 | overflow-y auto
|
239 | position absolute
|
240 | top 100%
|
241 | right 0
|
242 | background-color #fff
|
243 | padding 0.6rem 0
|
244 | border 1px solid #ddd
|
245 | border-bottom-color #ccc
|
246 | text-align left
|
247 | border-radius 0.25rem
|
248 | white-space nowrap
|
249 | margin 0
|
250 | </style>
|