Skip to content

Commit

Permalink
feat(theme-default): improve sidebar headers (#196)
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhanbo authored Jun 5, 2024
1 parent 0a29075 commit c39e4e4
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 47 deletions.
4 changes: 0 additions & 4 deletions docs/themes/default/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,6 @@ export default {
- Set to `2` to include `<h2>` and `<h3>` headers.
- ...

The max value depends on which levels of headers you have extracted via [markdown.headers.level](https://v2.vuepress.vuejs.org/reference/config.html#markdown-headers).

The default value of `markdown.headers.level` is `[2, 3]`, so the default max value of `sidebarDepth` is `2`.

You can override this global option via [sidebarDepth](./frontmatter.md#sidebardepth) frontmatter in your pages.

### editLink
Expand Down
4 changes: 0 additions & 4 deletions docs/zh/themes/default/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,10 +392,6 @@ export default {
- 设为 `2` 来包含 `<h2>``<h3>` 标题。
- ...

最大值取决于你通过 [markdown.headers.level](https://v2.vuepress.vuejs.org/zh/config.html#markdown-headers) 提取了哪些级别的标题。

由于 `markdown.headers.level` 的默认值是 `[2, 3]` ,因此 `sidebarDepth` 的默认最大值是 `2`

你可以通过页面的 [sidebarDepth](./frontmatter.md#sidebardepth) frontmatter 来覆盖这个全局配置。

### editLink
Expand Down
3 changes: 3 additions & 0 deletions themes/theme-default/src/client/components/VPPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
import VPPageMeta from '@theme/VPPageMeta.vue'
import VPPageNav from '@theme/VPPageNav.vue'
import { Content } from 'vuepress/client'
import { setupHeaders } from '../composables/index.js'
defineSlots<{
'top'?: (props: Record<never, never>) => any
'bottom'?: (props: Record<never, never>) => any
'content-top'?: (props: Record<never, never>) => any
'content-bottom'?: (props: Record<never, never>) => any
}>()
setupHeaders()
</script>

<template>
Expand Down
104 changes: 65 additions & 39 deletions themes/theme-default/src/client/composables/useSidebarItems.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { useThemeLocaleData } from '@theme/useThemeData'
import { keys, startsWith } from '@vuepress/helper/client'
import { computed, inject, provide } from 'vue'
import type { ComputedRef, InjectionKey } from 'vue'
import { getHeaders, keys, startsWith } from '@vuepress/helper/client'
import type { MenuItem } from '@vuepress/helper/client'
import { computed, inject, onMounted, provide, ref, watch } from 'vue'
import type { ComputedRef, InjectionKey, Ref } from 'vue'
import {
usePageData,
usePageFrontmatter,
useRoute,
useRouteLocale,
useRouter,
} from 'vuepress/client'
import type { PageData, PageHeader } from 'vuepress/client'
import type { PageData } from 'vuepress/client'
import { isPlainObject, isString } from 'vuepress/shared'
import type {
DefaultThemeNormalPageFrontmatter,
Expand All @@ -20,6 +22,46 @@ import type {
import type { SidebarHeaderItem, SidebarItem } from '../typings.js'
import { getAutoLink, isLinkInternal, resolvePrefix } from '../utils/index.js'

export type HeadersRef = Ref<MenuItem[]>

export const headersRef: HeadersRef = ref([])

export const setupHeaders = (): void => {
const router = useRouter()
const themeLocale = useThemeLocaleData()
const frontmatter = usePageFrontmatter<DefaultThemeNormalPageFrontmatter>()
const levels = computed(
() => frontmatter.value.sidebarDepth ?? themeLocale.value.sidebarDepth ?? 2,
)

router.beforeEach((to, from) => {
if (to.path !== from.path) {
headersRef.value = []
}
})

const updateHeaders = (): void => {
if (levels.value <= 0) {
headersRef.value = []
return
}

headersRef.value = getHeaders({
selector: [...new Array(6)]
.map((_, i) => `.theme-default-content h${i + 1}`)
.join(','),
levels: [2, levels.value + 1],
ignore: ['.vp-badge'],
})
}

watch(levels, updateHeaders)

onMounted(updateHeaders)
}

export const useHeaders = (): HeadersRef => headersRef

export type SidebarItemsRef = ComputedRef<SidebarItem[]>

export const sidebarItemsSymbol: InjectionKey<SidebarItemsRef> =
Expand All @@ -45,24 +87,21 @@ export const setupSidebarItems = (): void => {
const page = usePageData()
const route = useRoute()
const routeLocale = useRouteLocale()
const headers = useHeaders()

const sidebarConfig = computed<false | SidebarOptions>(() =>
frontmatter.value.home
? false
: frontmatter.value.sidebar ?? themeLocale.value.sidebar ?? 'heading',
)

const sidebarDepth = computed(
() => frontmatter.value.sidebarDepth ?? themeLocale.value.sidebarDepth ?? 2,
)

const sidebarItems = computed(() =>
resolveSidebarItems(
sidebarConfig.value,
sidebarDepth.value,
page.value,
route.path,
routeLocale.value,
headers.value,
),
)
provide(sidebarItemsSymbol, sidebarItems)
Expand All @@ -75,32 +114,26 @@ export const setupSidebarItems = (): void => {
*/
export const resolveSidebarItems = (
sidebarConfig: false | SidebarOptions,
sidebarDepth: number,
page: PageData,
path: string,
routeLocale: string,
headers: MenuItem[],
): SidebarItem[] => {
// resolve sidebar items according to the config
if (sidebarConfig === false) {
return []
}

if (sidebarConfig === 'heading') {
return resolveSidebarHeadingItem(sidebarDepth, page)
return resolveSidebarHeadingItem(page, headers)
}

if (Array.isArray(sidebarConfig)) {
return resolveArraySidebarItems(
sidebarConfig,
sidebarDepth,
page,
path,
routeLocale,
)
return resolveArraySidebarItems(sidebarConfig, headers, path, routeLocale)
}

if (isPlainObject(sidebarConfig)) {
return resolveMultiSidebarItems(sidebarConfig, sidebarDepth, page, path)
return resolveMultiSidebarItems(sidebarConfig, page, headers, path)
}

return []
Expand All @@ -110,34 +143,28 @@ export const resolveSidebarItems = (
* Util to transform page header to sidebar item
*/
export const resolveSidebarHeaderItem = (
sidebarDepth: number,
header: PageHeader,
header: MenuItem,
): SidebarHeaderItem => ({
text: header.title,
link: header.link,
children: resolveSidebarHeaderItems(sidebarDepth, header.children),
children: resolveSidebarHeaderItems(header.children),
})

export const resolveSidebarHeaderItems = (
sidebarDepth: number,
headers: PageHeader[],
headers?: MenuItem[],
): SidebarHeaderItem[] =>
sidebarDepth > 0
? headers.map((header) =>
resolveSidebarHeaderItem(sidebarDepth - 1, header),
)
: []
headers ? headers.map((header) => resolveSidebarHeaderItem(header)) : []

/**
* Resolve current page and its header to sidebar items if the config is `heading`
*/
export const resolveSidebarHeadingItem = (
sidebarDepth: number,
page: PageData,
headers: MenuItem[],
): SidebarItem[] => [
{
text: page.title,
children: resolveSidebarHeaderItems(sidebarDepth, page.headers),
children: resolveSidebarHeaderItems(headers),
},
]

Expand All @@ -146,8 +173,7 @@ export const resolveSidebarHeadingItem = (
*/
export const resolveArraySidebarItems = (
sidebarConfig: SidebarArrayOptions,
sidebarDepth: number,
page: PageData,
headers: MenuItem[],
path: string,
prefix = '',
): SidebarItem[] => {
Expand Down Expand Up @@ -179,12 +205,12 @@ export const resolveArraySidebarItems = (
// use headers of current page as children
if (childItem.link === path) {
// skip h1 header
const headers =
page.headers[0]?.level === 1 ? page.headers[0].children : page.headers
const currentHeaders =
headers[0]?.level === 1 ? headers[0].children : headers

return {
...childItem,
children: resolveSidebarHeaderItems(sidebarDepth, headers),
children: resolveSidebarHeaderItems(currentHeaders),
}
}

Expand All @@ -199,8 +225,8 @@ export const resolveArraySidebarItems = (
*/
export const resolveMultiSidebarItems = (
sidebarConfig: SidebarObjectOptions,
sidebarDepth: number,
page: PageData,
headers: MenuItem[],
path: string,
): SidebarItem[] => {
const sidebarRoutes = keys(sidebarConfig).sort((x, y) => y.length - x.length)
Expand All @@ -212,8 +238,8 @@ export const resolveMultiSidebarItems = (

return matched
? matched === 'heading'
? resolveSidebarHeadingItem(sidebarDepth, page)
: resolveArraySidebarItems(matched, sidebarDepth, page, path, base)
? resolveSidebarHeadingItem(page, headers)
: resolveArraySidebarItems(matched, headers, path, base)
: []
}

Expand Down

0 comments on commit c39e4e4

Please sign in to comment.