mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
fix(theme): fix sidebar link concatenation error (#722)
This commit is contained in:
parent
1686836e38
commit
c476c2059b
@ -29,6 +29,7 @@ export * from './prev-next.js'
|
||||
export * from './route-query.js'
|
||||
export * from './scroll-behavior.js'
|
||||
export * from './scroll-promise.js'
|
||||
export * from './sidebar-data.js'
|
||||
export * from './sidebar.js'
|
||||
export * from './tag-colors.js'
|
||||
export * from './theme-data.js'
|
||||
|
||||
202
theme/src/client/composables/sidebar-data.ts
Normal file
202
theme/src/client/composables/sidebar-data.ts
Normal file
@ -0,0 +1,202 @@
|
||||
import type { InjectionKey, Ref } from 'vue'
|
||||
import type { ResolvedSidebarItem, ThemeSidebar, ThemeSidebarItem } from '../../shared/index.js'
|
||||
import { sidebar as sidebarRaw } from '@internal/sidebar'
|
||||
import {
|
||||
isArray,
|
||||
isPlainObject,
|
||||
isString,
|
||||
removeLeadingSlash,
|
||||
} from '@vuepress/helper/client'
|
||||
import {
|
||||
computed,
|
||||
inject,
|
||||
provide,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import { useRouteLocale } from 'vuepress/client'
|
||||
import { normalizeLink, normalizePrefix, resolveNavLink } from '../utils/index.js'
|
||||
import { useData } from './data.js'
|
||||
|
||||
export type SidebarData = Record<string, ThemeSidebar>
|
||||
|
||||
export type SidebarDataRef = Ref<SidebarData>
|
||||
export type AutoDirSidebarRef = Ref<ThemeSidebarItem[] | {
|
||||
link: string
|
||||
items: ThemeSidebarItem[]
|
||||
}>
|
||||
export type AutoHomeDataRef = Ref<Record<string, string>>
|
||||
|
||||
const { __auto__, __home__, ...items } = sidebarRaw
|
||||
|
||||
export const sidebarData: SidebarDataRef = ref(items)
|
||||
export const autoDirSidebar: AutoDirSidebarRef = ref(__auto__)
|
||||
const autoHomeData: AutoHomeDataRef = ref(__home__)
|
||||
|
||||
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
|
||||
__VUE_HMR_RUNTIME__.updateSidebar = (data: SidebarData) => {
|
||||
const { __auto__, __home__, ...items } = data
|
||||
sidebarData.value = items
|
||||
autoDirSidebar.value = __auto__ as ThemeSidebarItem[]
|
||||
autoHomeData.value = __home__ as Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
const sidebarSymbol: InjectionKey<Ref<ResolvedSidebarItem[]>> = Symbol(
|
||||
__VUEPRESS_DEV__ ? 'sidebar' : '',
|
||||
)
|
||||
|
||||
export function setupSidebar(): void {
|
||||
const { page, frontmatter } = useData()
|
||||
|
||||
const routeLocale = useRouteLocale()
|
||||
|
||||
const hasSidebar = computed(() => {
|
||||
return (
|
||||
frontmatter.value.pageLayout !== 'home'
|
||||
&& frontmatter.value.pageLayout !== 'friends'
|
||||
&& frontmatter.value.sidebar !== false
|
||||
&& frontmatter.value.layout !== 'NotFound'
|
||||
)
|
||||
})
|
||||
|
||||
const sidebarData = computed(() => {
|
||||
return hasSidebar.value
|
||||
? getSidebar(typeof frontmatter.value.sidebar === 'string'
|
||||
? frontmatter.value.sidebar
|
||||
: page.value.path, routeLocale.value)
|
||||
: []
|
||||
})
|
||||
|
||||
provide(sidebarSymbol, sidebarData)
|
||||
}
|
||||
|
||||
export function useSidebarData(): Ref<ResolvedSidebarItem[]> {
|
||||
const sidebarData = inject(sidebarSymbol)
|
||||
if (!sidebarData) {
|
||||
throw new Error('useSidebarData() is called without provider.')
|
||||
}
|
||||
return sidebarData
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
|
||||
* sidebar config from `MultiSideBarConfig` with various path combinations such
|
||||
* as matching `guide/` and `/guide/`. If no matching config was found, it will
|
||||
* return empty array.
|
||||
*/
|
||||
export function getSidebar(routePath: string, routeLocal: string): ResolvedSidebarItem[] {
|
||||
const _sidebar = sidebarData.value[routeLocal]
|
||||
|
||||
if (_sidebar === 'auto') {
|
||||
return resolveSidebarItems(autoDirSidebar.value[routeLocal])
|
||||
}
|
||||
else if (isArray(_sidebar)) {
|
||||
return resolveSidebarItems(_sidebar, routeLocal)
|
||||
}
|
||||
else if (isPlainObject(_sidebar)) {
|
||||
routePath = decodeURIComponent(routePath)
|
||||
const dir
|
||||
= Object.keys(_sidebar)
|
||||
.sort((a, b) => b.split('/').length - a.split('/').length)
|
||||
.find((dir) => {
|
||||
// make sure the multi sidebar key starts with slash too
|
||||
return routePath.startsWith(`${routeLocal}${removeLeadingSlash(dir)}`)
|
||||
}) || ''
|
||||
const sidebar = dir ? _sidebar[dir] : undefined
|
||||
|
||||
if (sidebar === 'auto') {
|
||||
return resolveSidebarItems(
|
||||
dir ? autoDirSidebar.value[dir] : [],
|
||||
routeLocal,
|
||||
)
|
||||
}
|
||||
else if (isArray(sidebar)) {
|
||||
return resolveSidebarItems(sidebar, dir)
|
||||
}
|
||||
else if (isPlainObject(sidebar)) {
|
||||
const prefix = normalizePrefix(routeLocal, sidebar.prefix)
|
||||
return resolveSidebarItems(
|
||||
sidebar.items === 'auto'
|
||||
? autoDirSidebar.value[prefix]
|
||||
: sidebar.items,
|
||||
prefix,
|
||||
)
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function resolveSidebarItems(
|
||||
sidebarItems: (string | ThemeSidebarItem)[],
|
||||
_prefix = '',
|
||||
): ResolvedSidebarItem[] {
|
||||
const resolved: ResolvedSidebarItem[] = []
|
||||
sidebarItems.forEach((item) => {
|
||||
if (isString(item)) {
|
||||
resolved.push(resolveNavLink(normalizeLink(_prefix, item)))
|
||||
}
|
||||
else {
|
||||
const { link, items, prefix, dir, ...args } = item
|
||||
const navLink = { ...args } as ResolvedSidebarItem
|
||||
if (link) {
|
||||
navLink.link = link.startsWith('---') ? link : normalizeLink(_prefix, link)
|
||||
const nav = resolveNavLink(navLink.link)
|
||||
navLink.icon = nav.icon || navLink.icon
|
||||
navLink.badge = nav.badge || navLink.badge
|
||||
}
|
||||
const nextPrefix = normalizePrefix(_prefix, prefix || dir)
|
||||
if (items === 'auto') {
|
||||
navLink.items = resolveSidebarItems(autoDirSidebar.value[nextPrefix], nextPrefix)
|
||||
if (!navLink.link && autoHomeData.value[nextPrefix]) {
|
||||
navLink.link = normalizeLink(autoHomeData.value[nextPrefix])
|
||||
const nav = resolveNavLink(navLink.link)
|
||||
navLink.icon = nav.icon || navLink.icon
|
||||
navLink.badge = nav.badge || navLink.badge
|
||||
}
|
||||
}
|
||||
else {
|
||||
navLink.items = items?.length
|
||||
? resolveSidebarItems(items, nextPrefix)
|
||||
: undefined
|
||||
}
|
||||
resolved.push(navLink)
|
||||
}
|
||||
})
|
||||
return resolved
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or generate sidebar group from the given sidebar items.
|
||||
*/
|
||||
export function getSidebarGroups(sidebar: ResolvedSidebarItem[]): ResolvedSidebarItem[] {
|
||||
const groups: ResolvedSidebarItem[] = []
|
||||
|
||||
let lastGroupIndex = 0
|
||||
|
||||
for (const index in sidebar) {
|
||||
const item = sidebar[index]
|
||||
|
||||
if (item.items) {
|
||||
lastGroupIndex = groups.push(item)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!groups[lastGroupIndex]) {
|
||||
groups.push({ items: [] })
|
||||
}
|
||||
|
||||
groups[lastGroupIndex]!.items!.push(item)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
export function getSidebarFirstLink(sidebar: ResolvedSidebarItem[]): string {
|
||||
for (const item of sidebar) {
|
||||
if (item.link)
|
||||
return item.link
|
||||
if (item.items)
|
||||
return getSidebarFirstLink(item.items)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@ -1,202 +1,13 @@
|
||||
import type { ComputedRef, InjectionKey, Ref } from 'vue'
|
||||
import type { ResolvedSidebarItem, ThemeSidebar, ThemeSidebarItem } from '../../shared/index.js'
|
||||
import { sidebar as sidebarRaw } from '@internal/sidebar'
|
||||
import {
|
||||
ensureLeadingSlash,
|
||||
isArray,
|
||||
isPlainObject,
|
||||
isString,
|
||||
removeLeadingSlash,
|
||||
} from '@vuepress/helper/client'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
import type { ResolvedSidebarItem } from '../../shared/index.js'
|
||||
import { ensureLeadingSlash, isArray } from '@vuepress/helper/client'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import {
|
||||
computed,
|
||||
inject,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
provide,
|
||||
ref,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref, watch, watchEffect } from 'vue'
|
||||
import { resolveRouteFullPath, useRoute, useRouteLocale } from 'vuepress/client'
|
||||
import { isActive, normalizeLink, normalizePrefix, resolveNavLink } from '../utils/index.js'
|
||||
import { isActive } from '../utils/index.js'
|
||||
import { useData } from './data.js'
|
||||
import { useEncrypt } from './encrypt.js'
|
||||
|
||||
export type SidebarData = Record<string, ThemeSidebar>
|
||||
|
||||
export type SidebarDataRef = Ref<SidebarData>
|
||||
export type AutoDirSidebarRef = Ref<ThemeSidebarItem[] | {
|
||||
link: string
|
||||
items: ThemeSidebarItem[]
|
||||
}>
|
||||
export type AutoHomeDataRef = Ref<Record<string, string>>
|
||||
|
||||
const { __auto__, __home__, ...items } = sidebarRaw
|
||||
|
||||
const sidebarData: SidebarDataRef = ref(items)
|
||||
const autoDirSidebar: AutoDirSidebarRef = ref(__auto__)
|
||||
const autoHomeData: AutoHomeDataRef = ref(__home__)
|
||||
|
||||
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
|
||||
__VUE_HMR_RUNTIME__.updateSidebar = (data: SidebarData) => {
|
||||
const { __auto__, __home__, ...items } = data
|
||||
sidebarData.value = items
|
||||
autoDirSidebar.value = __auto__ as ThemeSidebarItem[]
|
||||
autoHomeData.value = __home__ as Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
const sidebarSymbol: InjectionKey<Ref<ResolvedSidebarItem[]>> = Symbol(
|
||||
__VUEPRESS_DEV__ ? 'sidebar' : '',
|
||||
)
|
||||
|
||||
export function setupSidebar(): void {
|
||||
const { page, frontmatter } = useData()
|
||||
|
||||
const routeLocale = useRouteLocale()
|
||||
|
||||
const hasSidebar = computed(() => {
|
||||
return (
|
||||
frontmatter.value.pageLayout !== 'home'
|
||||
&& frontmatter.value.pageLayout !== 'friends'
|
||||
&& frontmatter.value.sidebar !== false
|
||||
&& frontmatter.value.layout !== 'NotFound'
|
||||
)
|
||||
})
|
||||
|
||||
const sidebarData = computed(() => {
|
||||
return hasSidebar.value
|
||||
? getSidebar(typeof frontmatter.value.sidebar === 'string'
|
||||
? frontmatter.value.sidebar
|
||||
: page.value.path, routeLocale.value)
|
||||
: []
|
||||
})
|
||||
|
||||
provide(sidebarSymbol, sidebarData)
|
||||
}
|
||||
|
||||
export function useSidebarData(): Ref<ResolvedSidebarItem[]> {
|
||||
const sidebarData = inject(sidebarSymbol)
|
||||
if (!sidebarData) {
|
||||
throw new Error('useSidebarData() is called without provider.')
|
||||
}
|
||||
return sidebarData
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
|
||||
* sidebar config from `MultiSideBarConfig` with various path combinations such
|
||||
* as matching `guide/` and `/guide/`. If no matching config was found, it will
|
||||
* return empty array.
|
||||
*/
|
||||
export function getSidebar(routePath: string, routeLocal: string): ResolvedSidebarItem[] {
|
||||
const _sidebar = sidebarData.value[routeLocal]
|
||||
|
||||
if (_sidebar === 'auto') {
|
||||
return resolveSidebarItems(autoDirSidebar.value[routeLocal])
|
||||
}
|
||||
else if (isArray(_sidebar)) {
|
||||
return resolveSidebarItems(_sidebar, routeLocal)
|
||||
}
|
||||
else if (isPlainObject(_sidebar)) {
|
||||
routePath = decodeURIComponent(routePath)
|
||||
const dir
|
||||
= Object.keys(_sidebar)
|
||||
.sort((a, b) => b.split('/').length - a.split('/').length)
|
||||
.find((dir) => {
|
||||
// make sure the multi sidebar key starts with slash too
|
||||
return routePath.startsWith(`${routeLocal}${removeLeadingSlash(dir)}`)
|
||||
}) || ''
|
||||
const sidebar = dir ? _sidebar[dir] : undefined
|
||||
|
||||
if (sidebar === 'auto') {
|
||||
return resolveSidebarItems(
|
||||
dir ? autoDirSidebar.value[dir] : [],
|
||||
routeLocal,
|
||||
)
|
||||
}
|
||||
else if (isArray(sidebar)) {
|
||||
return resolveSidebarItems(sidebar, routeLocal)
|
||||
}
|
||||
else if (isPlainObject(sidebar)) {
|
||||
const prefix = normalizePrefix(routeLocal, sidebar.prefix)
|
||||
return resolveSidebarItems(
|
||||
sidebar.items === 'auto'
|
||||
? autoDirSidebar.value[prefix]
|
||||
: sidebar.items,
|
||||
prefix,
|
||||
)
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function resolveSidebarItems(
|
||||
sidebarItems: (string | ThemeSidebarItem)[],
|
||||
_prefix = '',
|
||||
): ResolvedSidebarItem[] {
|
||||
const resolved: ResolvedSidebarItem[] = []
|
||||
sidebarItems.forEach((item) => {
|
||||
if (isString(item)) {
|
||||
resolved.push(resolveNavLink(normalizeLink(_prefix, item)))
|
||||
}
|
||||
else {
|
||||
const { link, items, prefix, dir, ...args } = item
|
||||
const navLink = { ...args } as ResolvedSidebarItem
|
||||
if (link) {
|
||||
navLink.link = link.startsWith('---') ? link : normalizeLink(_prefix, link)
|
||||
const nav = resolveNavLink(navLink.link)
|
||||
navLink.icon = nav.icon || navLink.icon
|
||||
navLink.badge = nav.badge || navLink.badge
|
||||
}
|
||||
const nextPrefix = normalizePrefix(_prefix, prefix || dir)
|
||||
if (items === 'auto') {
|
||||
navLink.items = resolveSidebarItems(autoDirSidebar.value[nextPrefix], nextPrefix)
|
||||
if (!navLink.link && autoHomeData.value[nextPrefix]) {
|
||||
navLink.link = normalizeLink(autoHomeData.value[nextPrefix])
|
||||
const nav = resolveNavLink(navLink.link)
|
||||
navLink.icon = nav.icon || navLink.icon
|
||||
navLink.badge = nav.badge || navLink.badge
|
||||
}
|
||||
}
|
||||
else {
|
||||
navLink.items = items?.length
|
||||
? resolveSidebarItems(items, nextPrefix)
|
||||
: undefined
|
||||
}
|
||||
resolved.push(navLink)
|
||||
}
|
||||
})
|
||||
return resolved
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or generate sidebar group from the given sidebar items.
|
||||
*/
|
||||
export function getSidebarGroups(sidebar: ResolvedSidebarItem[]): ResolvedSidebarItem[] {
|
||||
const groups: ResolvedSidebarItem[] = []
|
||||
|
||||
let lastGroupIndex = 0
|
||||
|
||||
for (const index in sidebar) {
|
||||
const item = sidebar[index]
|
||||
|
||||
if (item.items) {
|
||||
lastGroupIndex = groups.push(item)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!groups[lastGroupIndex]) {
|
||||
groups.push({ items: [] })
|
||||
}
|
||||
|
||||
groups[lastGroupIndex]!.items!.push(item)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
import { getSidebarGroups, sidebarData, useSidebarData } from './sidebar-data.js'
|
||||
|
||||
/**
|
||||
* Check if the given sidebar item contains any active link.
|
||||
@ -429,13 +240,3 @@ export function useSidebarControl(item: ComputedRef<ResolvedSidebarItem>): Sideb
|
||||
toggle,
|
||||
}
|
||||
}
|
||||
|
||||
export function getSidebarFirstLink(sidebar: ResolvedSidebarItem[]): string {
|
||||
for (const item of sidebar) {
|
||||
if (item.link)
|
||||
return item.link
|
||||
if (item.items)
|
||||
return getSidebarFirstLink(item.items)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user