feat(theme): allow setting base path in sidebar items (#2734)

pull/2736/head
Divyansh Singh 2 years ago committed by GitHub
parent b55b529f23
commit 52884d9d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -28,15 +28,19 @@ describe('client/theme-default/support/sidebar', () => {
} }
test('gets `/` sidebar', () => { test('gets `/` sidebar', () => {
expect(getSidebar(normalSidebar, '/')).toBe(root) expect(getSidebar(normalSidebar, '/')).toStrictEqual(root)
}) })
test('gets `/multi-sidebar/` sidebar', () => { test('gets `/multi-sidebar/` sidebar', () => {
expect(getSidebar(normalSidebar, '/multi-sidebar/')).toBe(another) expect(getSidebar(normalSidebar, '/multi-sidebar/')).toStrictEqual(
another
)
}) })
test('gets `/` sidebar again', () => { test('gets `/` sidebar again', () => {
expect(getSidebar(normalSidebar, '/some-entry.html')).toBe(root) expect(getSidebar(normalSidebar, '/some-entry.html')).toStrictEqual(
root
)
}) })
}) })
@ -47,15 +51,19 @@ describe('client/theme-default/support/sidebar', () => {
} }
test('gets `/` sidebar', () => { test('gets `/` sidebar', () => {
expect(getSidebar(reversedSidebar, '/')).toBe(root) expect(getSidebar(reversedSidebar, '/')).toStrictEqual(root)
}) })
test('gets `/multi-sidebar/` sidebar', () => { test('gets `/multi-sidebar/` sidebar', () => {
expect(getSidebar(reversedSidebar, '/multi-sidebar/')).toBe(another) expect(getSidebar(reversedSidebar, '/multi-sidebar/')).toStrictEqual(
another
)
}) })
test('gets `/` sidebar again', () => { test('gets `/` sidebar again', () => {
expect(getSidebar(reversedSidebar, '/some-entry.html')).toBe(root) expect(getSidebar(reversedSidebar, '/some-entry.html')).toStrictEqual(
root
)
}) })
}) })
@ -74,19 +82,25 @@ describe('client/theme-default/support/sidebar', () => {
} }
test('gets `/` sidebar', () => { test('gets `/` sidebar', () => {
expect(getSidebar(nestedSidebar, '/')).toBe(root) expect(getSidebar(nestedSidebar, '/')).toStrictEqual(root)
}) })
test('gets `/multi-sidebar/` sidebar', () => { test('gets `/multi-sidebar/` sidebar', () => {
expect(getSidebar(nestedSidebar, '/multi-sidebar/')).toBe(another) expect(getSidebar(nestedSidebar, '/multi-sidebar/')).toStrictEqual(
another
)
}) })
test('gets `/multi-sidebar/nested/` sidebar', () => { test('gets `/multi-sidebar/nested/` sidebar', () => {
expect(getSidebar(nestedSidebar, '/multi-sidebar/nested/')).toBe(nested) expect(
getSidebar(nestedSidebar, '/multi-sidebar/nested/')
).toStrictEqual(nested)
}) })
test('gets `/` sidebar again', () => { test('gets `/` sidebar again', () => {
expect(getSidebar(nestedSidebar, '/some-entry.html')).toBe(root) expect(getSidebar(nestedSidebar, '/some-entry.html')).toStrictEqual(
root
)
}) })
}) })
}) })

@ -1,5 +1,5 @@
import { createRequire } from 'module' import { createRequire } from 'module'
import { defineConfig } from 'vitepress' import { defineConfig, type DefaultTheme } from 'vitepress'
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json') const pkg = require('vitepress/package.json')
@ -36,8 +36,8 @@ export default defineConfig({
nav: nav(), nav: nav(),
sidebar: { sidebar: {
'/guide/': sidebarGuide(), '/guide/': { base: '/guide/', items: sidebarGuide() },
'/reference/': sidebarReference() '/reference/': { base: '/reference/', items: sidebarReference() }
}, },
editLink: { editLink: {
@ -70,9 +70,13 @@ export default defineConfig({
} }
}) })
function nav() { function nav(): DefaultTheme.NavItem[] {
return [ return [
{ text: 'Guide', link: '/guide/what-is-vitepress', activeMatch: '/guide/' }, {
text: 'Guide',
link: '/guide/what-is-vitepress',
activeMatch: '/guide/'
},
{ {
text: 'Reference', text: 'Reference',
link: '/reference/site-config', link: '/reference/site-config',
@ -94,142 +98,79 @@ function nav() {
] ]
} }
function sidebarGuide() { /* prettier-ignore */
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [ return [
{ {
text: 'Introduction', text: 'Introduction',
collapsed: false, collapsed: false,
items: [ items: [
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' }, { text: 'What is VitePress?', link: 'what-is-vitepress' },
{ text: 'Getting Started', link: '/guide/getting-started' }, { text: 'Getting Started', link: 'getting-started' },
{ text: 'Routing', link: '/guide/routing' }, { text: 'Routing', link: 'routing' },
{ text: 'Deploy', link: '/guide/deploy' } { text: 'Deploy', link: 'deploy' }
] ]
}, },
{ {
text: 'Writing', text: 'Writing',
collapsed: false, collapsed: false,
items: [ items: [
{ text: 'Markdown Extensions', link: '/guide/markdown' }, { text: 'Markdown Extensions', link: 'markdown' },
{ text: 'Asset Handling', link: '/guide/asset-handling' }, { text: 'Asset Handling', link: 'asset-handling' },
{ text: 'Frontmatter', link: '/guide/frontmatter' }, { text: 'Frontmatter', link: 'frontmatter' },
{ text: 'Using Vue in Markdown', link: '/guide/using-vue' }, { text: 'Using Vue in Markdown', link: 'using-vue' },
{ text: 'Internationalization', link: '/guide/i18n' } { text: 'Internationalization', link: 'i18n' }
] ]
}, },
{ {
text: 'Customization', text: 'Customization',
collapsed: false, collapsed: false,
items: [ items: [
{ text: 'Using a Custom Theme', link: '/guide/custom-theme' }, { text: 'Using a Custom Theme', link: 'custom-theme' },
{ { text: 'Extending the Default Theme', link: 'extending-default-theme' },
text: 'Extending the Default Theme', { text: 'Build-Time Data Loading', link: 'data-loading' },
link: '/guide/extending-default-theme' { text: 'SSR Compatibility', link: 'ssr-compat' },
}, { text: 'Connecting to a CMS', link: 'cms' }
{ text: 'Build-Time Data Loading', link: '/guide/data-loading' },
{ text: 'SSR Compatibility', link: '/guide/ssr-compat' },
{ text: 'Connecting to a CMS', link: '/guide/cms' }
] ]
}, },
{ {
text: 'Experimental', text: 'Experimental',
collapsed: false, collapsed: false,
items: [ items: [
{ { text: 'MPA Mode', link: 'mpa-mode' },
text: 'MPA Mode', { text: 'Sitemap Generation', link: 'sitemap-generation' }
link: '/guide/mpa-mode'
},
{
text: 'Sitemap Generation',
link: '/guide/sitemap-generation'
}
] ]
}, },
// { { text: 'Config & API Reference', base: '/reference/', link: 'site-config' }
// text: 'Migrations',
// collapsed: false,
// items: [
// {
// text: 'Migration from VuePress',
// link: '/guide/migration-from-vuepress'
// },
// {
// text: 'Migration from VitePress 0.x',
// link: '/guide/migration-from-vitepress-0'
// }
// ]
// },
{
text: 'Config & API Reference',
link: '/reference/site-config'
}
] ]
} }
function sidebarReference() { /* prettier-ignore */
function sidebarReference(): DefaultTheme.SidebarItem[] {
return [ return [
{ {
text: 'Reference', text: 'Reference',
items: [ items: [
{ text: 'Site Config', link: '/reference/site-config' }, { text: 'Site Config', link: 'site-config' },
{ text: 'Frontmatter Config', link: '/reference/frontmatter-config' }, { text: 'Frontmatter Config', link: 'frontmatter-config' },
{ text: 'Runtime API', link: '/reference/runtime-api' }, { text: 'Runtime API', link: 'runtime-api' },
{ text: 'CLI', link: '/reference/cli' }, { text: 'CLI', link: 'cli' },
{ {
text: 'Default Theme', text: 'Default Theme',
items: [ items: [
{ { text: 'Overview', link: 'default-theme-config' },
text: 'Overview', { text: 'Nav', link: 'default-theme-nav' },
link: '/reference/default-theme-config' { text: 'Sidebar', link: 'default-theme-sidebar' },
}, { text: 'Home Page', link: 'default-theme-home-page' },
{ { text: 'Footer', link: 'default-theme-footer' },
text: 'Nav', { text: 'Layout', link: 'default-theme-layout' },
link: '/reference/default-theme-nav' { text: 'Badge', link: 'default-theme-badge' },
}, { text: 'Team Page', link: 'default-theme-team-page' },
{ { text: 'Prev / Next Links', link: 'default-theme-prev-next-links' },
text: 'Sidebar', { text: 'Edit Link', link: 'default-theme-edit-link' },
link: '/reference/default-theme-sidebar' { text: 'Last Updated Timestamp', link: 'default-theme-last-updated' },
}, { text: 'Search', link: 'default-theme-search' },
{ { text: 'Carbon Ads', link: 'default-theme-carbon-ads' }
text: 'Home Page',
link: '/reference/default-theme-home-page'
},
{
text: 'Footer',
link: '/reference/default-theme-footer'
},
{
text: 'Layout',
link: '/reference/default-theme-layout'
},
{
text: 'Badge',
link: '/reference/default-theme-badge'
},
{
text: 'Team Page',
link: '/reference/default-theme-team-page'
},
{
text: 'Prev / Next Links',
link: '/reference/default-theme-prev-next-links'
},
{
text: 'Edit Link',
link: '/reference/default-theme-edit-link'
},
{
text: 'Last Updated Timestamp',
link: '/reference/default-theme-last-updated'
},
{
text: 'Search',
link: '/reference/default-theme-search'
},
{
text: 'Carbon Ads',
link: '/reference/default-theme-carbon-ads'
}
] ]
} }
] ]

@ -7,6 +7,8 @@ export interface SidebarLink {
link: string link: string
} }
type SidebarItem = DefaultTheme.SidebarItem
/** /**
* Get the `Sidebar` from sidebar option. This method will ensure to get correct * Get the `Sidebar` from sidebar option. This method will ensure to get correct
* sidebar config from `MultiSideBarConfig` with various path combinations such * sidebar config from `MultiSideBarConfig` with various path combinations such
@ -14,20 +16,15 @@ export interface SidebarLink {
* return empty array. * return empty array.
*/ */
export function getSidebar( export function getSidebar(
sidebar: DefaultTheme.Sidebar | undefined, _sidebar: DefaultTheme.Sidebar | undefined,
path: string path: string
): DefaultTheme.SidebarItem[] { ): SidebarItem[] {
if (Array.isArray(sidebar)) { if (Array.isArray(_sidebar)) return addBase(_sidebar)
return sidebar if (_sidebar == null) return []
}
if (sidebar == null) {
return []
}
path = ensureStartingSlash(path) path = ensureStartingSlash(path)
const dir = Object.keys(sidebar) const dir = Object.keys(_sidebar)
.sort((a, b) => { .sort((a, b) => {
return b.split('/').length - a.split('/').length return b.split('/').length - a.split('/').length
}) })
@ -36,16 +33,17 @@ export function getSidebar(
return path.startsWith(ensureStartingSlash(dir)) return path.startsWith(ensureStartingSlash(dir))
}) })
return dir ? sidebar[dir] : [] const sidebar = dir ? _sidebar[dir] : []
return Array.isArray(sidebar)
? addBase(sidebar)
: addBase(sidebar.items, sidebar.base)
} }
/** /**
* Get or generate sidebar group from the given sidebar items. * Get or generate sidebar group from the given sidebar items.
*/ */
export function getSidebarGroups( export function getSidebarGroups(sidebar: SidebarItem[]): SidebarItem[] {
sidebar: DefaultTheme.SidebarItem[] const groups: SidebarItem[] = []
): DefaultTheme.SidebarItem[] {
const groups: DefaultTheme.SidebarItem[] = []
let lastGroupIndex: number = 0 let lastGroupIndex: number = 0
@ -67,12 +65,10 @@ export function getSidebarGroups(
return groups return groups
} }
export function getFlatSideBarLinks( export function getFlatSideBarLinks(sidebar: SidebarItem[]): SidebarLink[] {
sidebar: DefaultTheme.SidebarItem[]
): SidebarLink[] {
const links: SidebarLink[] = [] const links: SidebarLink[] = []
function recursivelyExtractLinks(items: DefaultTheme.SidebarItem[]) { function recursivelyExtractLinks(items: SidebarItem[]) {
for (const item of items) { for (const item of items) {
if (item.text && item.link) { if (item.text && item.link) {
links.push({ text: item.text, link: item.link }) links.push({ text: item.text, link: item.link })
@ -94,7 +90,7 @@ export function getFlatSideBarLinks(
*/ */
export function hasActiveLink( export function hasActiveLink(
path: string, path: string,
items: DefaultTheme.SidebarItem | DefaultTheme.SidebarItem[] items: SidebarItem | SidebarItem[]
): boolean { ): boolean {
if (Array.isArray(items)) { if (Array.isArray(items)) {
return items.some((item) => hasActiveLink(path, item)) return items.some((item) => hasActiveLink(path, item))
@ -106,3 +102,13 @@ export function hasActiveLink(
? hasActiveLink(path, items.items) ? hasActiveLink(path, items.items)
: false : false
} }
function addBase(items: SidebarItem[], _base?: string): SidebarItem[] {
return [...items].map((_item) => {
const item = { ..._item }
const base = item.base || _base
if (base && item.link) item.link = base + item.link
if (item.items) item.items = addBase(item.items, base)
return item
})
}

@ -197,7 +197,7 @@ export namespace DefaultTheme {
export type Sidebar = SidebarItem[] | SidebarMulti export type Sidebar = SidebarItem[] | SidebarMulti
export interface SidebarMulti { export interface SidebarMulti {
[path: string]: SidebarItem[] [path: string]: SidebarItem[] | { items: SidebarItem[]; base: string }
} }
export type SidebarItem = { export type SidebarItem = {
@ -224,6 +224,11 @@ export namespace DefaultTheme {
* If `false`, group is collapsible but expanded by default * If `false`, group is collapsible but expanded by default
*/ */
collapsed?: boolean collapsed?: boolean
/**
* Base path for the children items.
*/
base?: string
} }
/** /**

Loading…
Cancel
Save