Skip to content

feat($theme-default): refine sidebar groups #1257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 3, 2019
19 changes: 12 additions & 7 deletions packages/@vuepress/theme-default/components/Page.vue
Original file line number Diff line number Diff line change
Expand Up @@ -177,20 +177,25 @@ function resolveNext (page, items) {

function find (page, items, offset) {
const res = []
items.forEach(item => {
if (item.type === 'group') {
res.push(...item.children || [])
} else {
res.push(item)
}
})
flattern(items, res)
for (let i = 0; i < res.length; i++) {
const cur = res[i]
if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) {
return res[i + offset]
}
}
}

function flattern (items, res) {
for (let i = 0, l = items.length; i < l; i++) {
if (items[i].type === 'group') {
flattern(items[i].children || [], res)
} else {
res.push(items[i])
}
}
}

</script>

<style lang="stylus">
Expand Down
74 changes: 9 additions & 65 deletions packages/@vuepress/theme-default/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,21 @@
<aside class="sidebar">
<NavLinks/>
<slot name="top"/>
<ul class="sidebar-links" v-if="items.length">
<li v-for="(item, i) in items" :key="i">
<SidebarGroup
v-if="item.type === 'group'"
:item="item"
:first="i === 0"
:open="i === openGroupIndex"
:collapsable="item.collapsable || item.collapsible"
@toggle="toggleGroup(i)"
/>
<SidebarLink v-else :item="item"/>
</li>
</ul>
<SidebarLinks :depth="0" :items="items"/>
<slot name="bottom"/>
</aside>
</template>

<script>
import SidebarGroup from './SidebarGroup.vue'
import SidebarLink from './SidebarLink.vue'
import SidebarLinks from './SidebarLinks.vue'
import NavLinks from './NavLinks.vue'
import { isActive } from '../util'

export default {
components: { SidebarGroup, SidebarLink, NavLinks },
name: 'Sidebar',

props: ['items'],
components: { SidebarLinks, NavLinks },

data () {
return {
openGroupIndex: 0
}
},

created () {
this.refreshIndex()
},

watch: {
'$route' () {
this.refreshIndex()
}
},

methods: {
refreshIndex () {
const index = resolveOpenGroupIndex(
this.$route,
this.items
)
if (index > -1) {
this.openGroupIndex = index
}
},

toggleGroup (index) {
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
},

isActive (page) {
return isActive(this.$route, page.regularPath)
}
}
}

function resolveOpenGroupIndex (route, items) {
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
return i
}
}
return -1
props: ['items']
}
</script>

Expand All @@ -97,15 +39,17 @@ function resolveOpenGroupIndex (route, items) {
line-height 1.25rem
font-size 1.1em
padding 0.5rem 0 0.5rem 1.5rem
.sidebar-links
& > .sidebar-links
padding 1.5rem 0
& > li:not(:first-child)
margin-top .75rem

@media (max-width: $MQMobile)
.sidebar
.nav-links
display block
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
top calc(1rem - 2px)
.sidebar-links
& > .sidebar-links
padding 1rem 0
</style>
90 changes: 71 additions & 19 deletions packages/@vuepress/theme-default/components/SidebarGroup.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
<template>
<section
class="sidebar-group"
:class="{ first, collapsable }"
:class="[
{
collapsable,
'is-sub-group': depth !== 0
},
`depth-${depth}`
]"
>
<router-link
v-if="item.path"
class="sidebar-heading clickable"
:class="{
open,
'active': isActive($route, item.path)
}"
:to="item.path"
@click.native="$emit('toggle')"
>
<span>{{ item.title }}</span>
<span
class="arrow"
v-if="collapsable"
:class="open ? 'down' : 'right'">
</span>
</router-link>

<p
v-else
class="sidebar-heading"
:class="{ open }"
@click="$emit('toggle')"
Expand All @@ -17,40 +42,59 @@
</p>

<DropdownTransition>
<ul
ref="items"
<SidebarLinks
class="sidebar-group-items"
:items="item.children"
v-if="open || !collapsable"
>
<li v-for="child in item.children">
<SidebarLink :item="child"/>
</li>
</ul>
:sidebarDepth="item.sidebarDepth"
:depth="depth + 1"
/>
</DropdownTransition>
</section>
</template>

<script>
import SidebarLink from './SidebarLink.vue'
import { isActive } from '../util'
import DropdownTransition from './DropdownTransition.vue'

export default {
name: 'SidebarGroup',
props: ['item', 'first', 'open', 'collapsable'],
components: { SidebarLink, DropdownTransition }
props: ['item', 'open', 'collapsable', 'depth'],
components: { DropdownTransition },
// ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
beforeCreate () {
this.$options.components.SidebarLinks = require('./SidebarLinks.vue').default
},
methods: { isActive }
}
</script>

<style lang="stylus">
.sidebar-group
&:not(.first)
margin-top 1em
.sidebar-group
padding-left 0.5em
&:not(.collapsable)
.sidebar-heading
.sidebar-heading:not(.clickable)
cursor auto
color inherit
// refine styles of nested sidebar groups
&.is-sub-group
padding-left 0
& > .sidebar-heading
font-size 0.95em
line-height 1.4
font-weight normal
padding-left 2rem
&:not(.clickable)
opacity 0.5
& > .sidebar-group-items
padding-left 1rem
& > li > .sidebar-link
font-size: 0.95em;
border-left none
&.depth-2
& > .sidebar-heading
border-left none

.sidebar-heading
color #999
Expand All @@ -59,19 +103,27 @@ export default {
font-size 1.1em
font-weight bold
// text-transform uppercase
padding 0 1.5rem
margin-top 0
margin-bottom 0.5rem
padding 0.35rem 1.5rem 0.35rem 1.25rem
width 100%
box-sizing border-box
margin 0
border-left 0.25rem solid transparent
&.open, &:hover
color inherit
.arrow
position relative
top -0.12em
left 0.5em
&:.open .arrow
top -0.18em
&.clickable
&.active
font-weight 600
color $accentColor
border-left-color $accentColor
&:hover
color $accentColor

.sidebar-group-items
transition height .1s ease-out
font-size 0.95em
overflow hidden
</style>
32 changes: 26 additions & 6 deletions packages/@vuepress/theme-default/components/SidebarLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@ import { isActive, hashRE, groupHeaders } from '../util'
export default {
functional: true,

props: ['item'],
props: ['item', 'sidebarDepth'],

render (h, { parent: { $page, $site, $route }, props: { item }}) {
render (h,
{
parent: {
$page,
$site,
$route,
$themeConfig,
$themeLocaleConfig
},
props: {
item,
sidebarDepth
}
}) {
// use custom active class matching logic
// due to edge case of paths ending with / + hash
const selfActive = isActive($route, item.path)
Expand All @@ -16,11 +29,17 @@ export default {
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
: selfActive
const link = renderLink(h, item.path, item.title || item.path, active)
const configDepth = $page.frontmatter.sidebarDepth != null
? $page.frontmatter.sidebarDepth
: $site.themeConfig.sidebarDepth

const configDepth = $page.frontmatter.sidebarDepth ||
sidebarDepth ||
$themeLocaleConfig.sidebarDepth ||
$themeConfig.sidebarDepth

const maxDepth = configDepth == null ? 1 : configDepth
const displayAllHeaders = !!$site.themeConfig.displayAllHeaders

const displayAllHeaders = $themeLocaleConfig.displayAllHeaders ||
$themeConfig.displayAllHeaders

if (item.type === 'auto') {
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
Expand Down Expand Up @@ -64,6 +83,7 @@ function renderChildren (h, children, path, route, maxDepth, depth = 1) {
font-size 0.95em

a.sidebar-link
font-size 1em
font-weight 400
display inline-block
color $textColor
Expand Down
Loading