Skip to content

Commit

Permalink
feat: add support for collapsible multi-level sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleSound committed Sep 22, 2022
1 parent ce8d139 commit 8c164fe
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 21 deletions.
17 changes: 15 additions & 2 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,21 @@ function sidebarGuide() {
items: [
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Configuration', link: '/guide/configuration' },
{ text: 'Deploying', link: '/guide/deploying' }
{ text: 'Configuration', link: '/guide/configuration', items: [
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Configuration', link: '/guide/configuration' },
{ text: 'Deploying', link: '/guide/deploying', },
] },
{ text: 'Deploying', link: '/guide/deploying',
collapsible: true,
collapsed: true,
items: [
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Configuration', link: '/guide/configuration' },
{ text: 'Deploying', link: '/guide/deploying', },
] },
]
},
{
Expand Down
103 changes: 85 additions & 18 deletions src/client/theme-default/components/VPSidebarLink.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script lang="ts" setup>
import type { DefaultTheme } from 'vitepress/theme'
import { computed, inject } from 'vue'
import { computed, inject, ref, watchEffect } from 'vue'
import { useData } from 'vitepress'
import { isActive } from '../support/utils.js'
import VPLink from './VPLink.vue'
import VPIconPlusSquare from './icons/VPIconPlusSquare.vue'
import VPIconMinusSquare from './icons/VPIconMinusSquare.vue'
withDefaults(
const props = withDefaults(
defineProps<{ item: DefaultTheme.SidebarItem; depth?: number }>(),
{ depth: 1 }
)
Expand All @@ -15,25 +17,51 @@ const maxDepth = computed<number>(
() => frontmatter.value.sidebarDepth || Infinity
)
const closeSideBar = inject('close-sidebar') as () => void
const collapsible = computed(() => 'items' in props.item ? !!props.item.collapsible : false)
const collapsed = ref(false)
watchEffect(() => {
if ('items' in props.item)
collapsed.value = !!(collapsible.value && props.item.collapsed)
})
function toggle() {
if (collapsible.value) {
collapsed.value = !collapsed.value
}
}
</script>

<template>
<VPLink
class="link"
:class="{ active: isActive(page.relativePath, item.link) }"
:style="{ paddingLeft: 16 * (depth - 1) + 'px' }"
:href="item.link"
@click="closeSideBar"
>
<span class="link-text" :class="{ light: depth > 1 }">{{ item.text }}</span>
</VPLink>
<template
v-if="'items' in item && depth < maxDepth"
v-for="child in item.items"
:key="child.link"
>
<VPSidebarLink :item="child" :depth="depth + 1" />
</template>
<section class="VPSidebarLink" :class="{ collapsible, collapsed }">
<div class="link-label">
<VPLink
class="link"
:class="{ active: isActive(page.relativePath, item.link) }"
:style="{ paddingLeft: 16 * (depth - 1) + 'px' }"
:href="item.link"
@click="closeSideBar"
>
<span class="link-text" :class="{ light: depth > 1 }">{{ item.text }}</span>
</VPLink>

<button class="action" @click.stop="toggle" type="button">
<VPIconMinusSquare class="icon minus" />
<VPIconPlusSquare class="icon plus" />
</button>
</div>
<div class="items">
<template
v-if="'items' in item && depth < maxDepth"
v-for="child in item.items"
:key="child.link"
>
<VPSidebarLink :item="child" :depth="depth + 1" />
</template>
</div>
</section>
</template>

<style scoped>
Expand Down Expand Up @@ -68,4 +96,43 @@ const closeSideBar = inject('close-sidebar') as () => void
font-size: 13px;
font-weight: 400;
}
.link-label {
display: flex;
justify-content: space-between;
}
.action {
display: none;
position: relative;
margin-right: -8px;
border-radius: 4px;
width: 32px;
height: 32px;
color: var(--vp-c-text-3);
transition: color 0.25s;
}
.VPSidebarLink.collapsible > .link-label .action {
display: block;
}
.icon {
position: absolute;
top: 8px;
left: 8px;
width: 16px;
height: 16px;
fill: currentColor;
}
.VPSidebarLink.collapsed .items {
max-height: 0;
}
.icon.minus { opacity: 1; }
.icon.plus { opacity: 0; }
.VPSidebarLink.collapsed > .link-label .icon.minus { opacity: 0; }
.VPSidebarLink.collapsed > .link-label .icon.plus { opacity: 1; }
</style>
20 changes: 19 additions & 1 deletion types/default-theme.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,25 @@ export namespace DefaultTheme {

export type SidebarItem =
| { text: string; link: string }
| { text: string; link?: string; items: SidebarItem[] }
| {
text: string;
link?: string;
items: SidebarItem[];

/**
* If `true`, toggle button is shown.
*
* @default false
*/
collapsible?: boolean

/**
* If `true`, collapsible group is collapsed by default.
*
* @default false
*/
collapsed?: boolean
}

// edit link -----------------------------------------------------------------

Expand Down

0 comments on commit 8c164fe

Please sign in to comment.