Skip to content

Commit 01dd45b

Browse files
authored
feat($theme-default): refine sidebar groups (#1257)
Close: #814 Close: #783 Close: #287
1 parent caca1db commit 01dd45b

File tree

10 files changed

+233
-107
lines changed

10 files changed

+233
-107
lines changed

packages/@vuepress/theme-default/components/Page.vue

+12-7
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,25 @@ function resolveNext (page, items) {
177177
178178
function find (page, items, offset) {
179179
const res = []
180-
items.forEach(item => {
181-
if (item.type === 'group') {
182-
res.push(...item.children || [])
183-
} else {
184-
res.push(item)
185-
}
186-
})
180+
flattern(items, res)
187181
for (let i = 0; i < res.length; i++) {
188182
const cur = res[i]
189183
if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) {
190184
return res[i + offset]
191185
}
192186
}
193187
}
188+
189+
function flattern (items, res) {
190+
for (let i = 0, l = items.length; i < l; i++) {
191+
if (items[i].type === 'group') {
192+
flattern(items[i].children || [], res)
193+
} else {
194+
res.push(items[i])
195+
}
196+
}
197+
}
198+
194199
</script>
195200

196201
<style lang="stylus">

packages/@vuepress/theme-default/components/Sidebar.vue

+9-65
Original file line numberDiff line numberDiff line change
@@ -2,79 +2,21 @@
22
<aside class="sidebar">
33
<NavLinks/>
44
<slot name="top"/>
5-
<ul class="sidebar-links" v-if="items.length">
6-
<li v-for="(item, i) in items" :key="i">
7-
<SidebarGroup
8-
v-if="item.type === 'group'"
9-
:item="item"
10-
:first="i === 0"
11-
:open="i === openGroupIndex"
12-
:collapsable="item.collapsable || item.collapsible"
13-
@toggle="toggleGroup(i)"
14-
/>
15-
<SidebarLink v-else :item="item"/>
16-
</li>
17-
</ul>
5+
<SidebarLinks :depth="0" :items="items"/>
186
<slot name="bottom"/>
197
</aside>
208
</template>
219

2210
<script>
23-
import SidebarGroup from './SidebarGroup.vue'
24-
import SidebarLink from './SidebarLink.vue'
11+
import SidebarLinks from './SidebarLinks.vue'
2512
import NavLinks from './NavLinks.vue'
26-
import { isActive } from '../util'
2713
2814
export default {
29-
components: { SidebarGroup, SidebarLink, NavLinks },
15+
name: 'Sidebar',
3016
31-
props: ['items'],
17+
components: { SidebarLinks, NavLinks },
3218
33-
data () {
34-
return {
35-
openGroupIndex: 0
36-
}
37-
},
38-
39-
created () {
40-
this.refreshIndex()
41-
},
42-
43-
watch: {
44-
'$route' () {
45-
this.refreshIndex()
46-
}
47-
},
48-
49-
methods: {
50-
refreshIndex () {
51-
const index = resolveOpenGroupIndex(
52-
this.$route,
53-
this.items
54-
)
55-
if (index > -1) {
56-
this.openGroupIndex = index
57-
}
58-
},
59-
60-
toggleGroup (index) {
61-
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
62-
},
63-
64-
isActive (page) {
65-
return isActive(this.$route, page.regularPath)
66-
}
67-
}
68-
}
69-
70-
function resolveOpenGroupIndex (route, items) {
71-
for (let i = 0; i < items.length; i++) {
72-
const item = items[i]
73-
if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
74-
return i
75-
}
76-
}
77-
return -1
19+
props: ['items']
7820
}
7921
</script>
8022

@@ -97,15 +39,17 @@ function resolveOpenGroupIndex (route, items) {
9739
line-height 1.25rem
9840
font-size 1.1em
9941
padding 0.5rem 0 0.5rem 1.5rem
100-
.sidebar-links
42+
& > .sidebar-links
10143
padding 1.5rem 0
44+
& > li:not(:first-child)
45+
margin-top .75rem
10246
10347
@media (max-width: $MQMobile)
10448
.sidebar
10549
.nav-links
10650
display block
10751
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
10852
top calc(1rem - 2px)
109-
.sidebar-links
53+
& > .sidebar-links
11054
padding 1rem 0
11155
</style>
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
11
<template>
22
<section
33
class="sidebar-group"
4-
:class="{ first, collapsable }"
4+
:class="[
5+
{
6+
collapsable,
7+
'is-sub-group': depth !== 0
8+
},
9+
`depth-${depth}`
10+
]"
511
>
12+
<router-link
13+
v-if="item.path"
14+
class="sidebar-heading clickable"
15+
:class="{
16+
open,
17+
'active': isActive($route, item.path)
18+
}"
19+
:to="item.path"
20+
@click.native="$emit('toggle')"
21+
>
22+
<span>{{ item.title }}</span>
23+
<span
24+
class="arrow"
25+
v-if="collapsable"
26+
:class="open ? 'down' : 'right'">
27+
</span>
28+
</router-link>
29+
630
<p
31+
v-else
732
class="sidebar-heading"
833
:class="{ open }"
934
@click="$emit('toggle')"
@@ -17,40 +42,59 @@
1742
</p>
1843

1944
<DropdownTransition>
20-
<ul
21-
ref="items"
45+
<SidebarLinks
2246
class="sidebar-group-items"
47+
:items="item.children"
2348
v-if="open || !collapsable"
24-
>
25-
<li v-for="child in item.children">
26-
<SidebarLink :item="child"/>
27-
</li>
28-
</ul>
49+
:sidebarDepth="item.sidebarDepth"
50+
:depth="depth + 1"
51+
/>
2952
</DropdownTransition>
3053
</section>
3154
</template>
3255

3356
<script>
34-
import SidebarLink from './SidebarLink.vue'
57+
import { isActive } from '../util'
3558
import DropdownTransition from './DropdownTransition.vue'
3659
3760
export default {
3861
name: 'SidebarGroup',
39-
props: ['item', 'first', 'open', 'collapsable'],
40-
components: { SidebarLink, DropdownTransition }
62+
props: ['item', 'open', 'collapsable', 'depth'],
63+
components: { DropdownTransition },
64+
// ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
65+
beforeCreate () {
66+
this.$options.components.SidebarLinks = require('./SidebarLinks.vue').default
67+
},
68+
methods: { isActive }
4169
}
4270
</script>
4371

4472
<style lang="stylus">
4573
.sidebar-group
46-
&:not(.first)
47-
margin-top 1em
4874
.sidebar-group
4975
padding-left 0.5em
5076
&:not(.collapsable)
51-
.sidebar-heading
77+
.sidebar-heading:not(.clickable)
5278
cursor auto
5379
color inherit
80+
// refine styles of nested sidebar groups
81+
&.is-sub-group
82+
padding-left 0
83+
& > .sidebar-heading
84+
font-size 0.95em
85+
line-height 1.4
86+
font-weight normal
87+
padding-left 2rem
88+
&:not(.clickable)
89+
opacity 0.5
90+
& > .sidebar-group-items
91+
padding-left 1rem
92+
& > li > .sidebar-link
93+
font-size: 0.95em;
94+
border-left none
95+
&.depth-2
96+
& > .sidebar-heading
97+
border-left none
5498
5599
.sidebar-heading
56100
color #999
@@ -59,19 +103,27 @@ export default {
59103
font-size 1.1em
60104
font-weight bold
61105
// text-transform uppercase
62-
padding 0 1.5rem
63-
margin-top 0
64-
margin-bottom 0.5rem
106+
padding 0.35rem 1.5rem 0.35rem 1.25rem
107+
width 100%
108+
box-sizing border-box
109+
margin 0
110+
border-left 0.25rem solid transparent
65111
&.open, &:hover
66112
color inherit
67113
.arrow
68114
position relative
69115
top -0.12em
70116
left 0.5em
71-
&:.open .arrow
72-
top -0.18em
117+
&.clickable
118+
&.active
119+
font-weight 600
120+
color $accentColor
121+
border-left-color $accentColor
122+
&:hover
123+
color $accentColor
73124
74125
.sidebar-group-items
75126
transition height .1s ease-out
127+
font-size 0.95em
76128
overflow hidden
77129
</style>

packages/@vuepress/theme-default/components/SidebarLink.vue

+26-6
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,22 @@ import { isActive, hashRE, groupHeaders } from '../util'
44
export default {
55
functional: true,
66
7-
props: ['item'],
7+
props: ['item', 'sidebarDepth'],
88
9-
render (h, { parent: { $page, $site, $route }, props: { item }}) {
9+
render (h,
10+
{
11+
parent: {
12+
$page,
13+
$site,
14+
$route,
15+
$themeConfig,
16+
$themeLocaleConfig
17+
},
18+
props: {
19+
item,
20+
sidebarDepth
21+
}
22+
}) {
1023
// use custom active class matching logic
1124
// due to edge case of paths ending with / + hash
1225
const selfActive = isActive($route, item.path)
@@ -16,11 +29,17 @@ export default {
1629
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
1730
: selfActive
1831
const link = renderLink(h, item.path, item.title || item.path, active)
19-
const configDepth = $page.frontmatter.sidebarDepth != null
20-
? $page.frontmatter.sidebarDepth
21-
: $site.themeConfig.sidebarDepth
32+
33+
const configDepth = $page.frontmatter.sidebarDepth ||
34+
sidebarDepth ||
35+
$themeLocaleConfig.sidebarDepth ||
36+
$themeConfig.sidebarDepth
37+
2238
const maxDepth = configDepth == null ? 1 : configDepth
23-
const displayAllHeaders = !!$site.themeConfig.displayAllHeaders
39+
40+
const displayAllHeaders = $themeLocaleConfig.displayAllHeaders ||
41+
$themeConfig.displayAllHeaders
42+
2443
if (item.type === 'auto') {
2544
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
2645
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
@@ -64,6 +83,7 @@ function renderChildren (h, children, path, route, maxDepth, depth = 1) {
6483
font-size 0.95em
6584
6685
a.sidebar-link
86+
font-size 1em
6787
font-weight 400
6888
display inline-block
6989
color $textColor

0 commit comments

Comments
 (0)