Skip to content

Commit d1a2c76

Browse files
yizhi996brc-dd
andauthored
feat(theme): support multi-level sidebar (#851)
Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
1 parent 7295033 commit d1a2c76

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

src/client/theme-default/components/VPSidebarLink.vue

+24-9
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,47 @@
11
<script lang="ts" setup>
22
import type { DefaultTheme } from 'vitepress/theme'
3-
import { inject } from 'vue'
3+
import { computed, inject } from 'vue'
44
import { useData } from 'vitepress'
55
import { isActive } from '../support/utils'
66
import VPLink from './VPLink.vue'
77
8-
defineProps<{
9-
item: DefaultTheme.SidebarItem
10-
}>()
11-
12-
const { page } = useData()
8+
withDefaults(defineProps<{ item: DefaultTheme.SidebarItem; depth?: number }>(), { depth: 1 })
139
10+
const { page, frontmatter } = useData()
11+
const maxDepth = computed<number>(() => frontmatter.value.sidebarDepth || Infinity)
1412
const closeSideBar = inject('close-sidebar') as () => void
1513
</script>
1614

1715
<template>
1816
<VPLink
19-
:class="{ active: isActive(page.relativePath, item.link) }"
17+
class="link"
18+
:class="{ active: isActive(page.relativePath, item.link), offset: depth > 1 }"
2019
:href="item.link"
2120
@click="closeSideBar"
2221
>
23-
<span class="link-text">{{ item.text }}</span>
22+
<span class="link-text" :class="{ light: depth > 1 }">{{ item.text }}</span>
23+
<template
24+
v-if="'items' in item && depth < maxDepth"
25+
v-for="child in item.items"
26+
:key="child.link"
27+
>
28+
<VPSidebarLink :item="child" :depth="depth + 1" />
29+
</template>
2430
</VPLink>
2531
</template>
2632

2733
<style scoped>
2834
.link {
2935
display: block;
30-
padding: 4px 0;
36+
margin: 4px 0;
3137
color: var(--vp-c-text-2);
3238
transition: color 0.5s;
3339
}
3440
41+
.link.offset {
42+
padding-left: 16px;
43+
}
44+
3545
.link:hover {
3646
color: var(--vp-c-text-1);
3747
}
@@ -51,4 +61,9 @@ const closeSideBar = inject('close-sidebar') as () => void
5161
font-size: 14px;
5262
font-weight: 500;
5363
}
64+
65+
.link-text.light {
66+
font-size: 13px;
67+
font-weight: 400;
68+
}
5469
</style>

src/client/theme-default/support/sidebar.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@ export function getFlatSideBarLinks(
3232
): DefaultTheme.SidebarItem[] {
3333
const links: DefaultTheme.SidebarItem[] = []
3434

35-
for (const group of sidebar) {
36-
for (const link of group.items) {
37-
links.push(link)
35+
function recursivelyExtractLinks(items: DefaultTheme.SidebarItem[]) {
36+
for (const item of items) {
37+
item.link && links.push(item)
38+
if ('items' in item) {
39+
recursivelyExtractLinks(item.items)
40+
}
3841
}
3942
}
4043

44+
for (const group of sidebar) {
45+
recursivelyExtractLinks(group.items)
46+
}
4147
return links
4248
}

types/default-theme.d.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,9 @@ export namespace DefaultTheme {
139139
collapsed?: boolean
140140
}
141141

142-
export interface SidebarItem {
143-
text: string
144-
link: string
145-
}
142+
export type SidebarItem =
143+
| { text: string; link: string }
144+
| { text: string; link?: string; items: SidebarItem[] }
146145

147146
// edit link -----------------------------------------------------------------
148147

0 commit comments

Comments
 (0)