mirror of https://github.com/vuejs/vitepress
parent
e435eec94a
commit
ce783e456e
@ -1,12 +0,0 @@
|
|||||||
import { withBase } from '../utils'
|
|
||||||
import NavBarLinks from './NavBarLinks.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NavBarLinks
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
return { withBase }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
import { defineComponent, computed, PropType } from 'vue'
|
|
||||||
import { useRoute } from 'vitepress'
|
|
||||||
import { withBase, isExternal } from '../utils'
|
|
||||||
import { DefaultTheme } from '../config'
|
|
||||||
import OutboundLink from './icons/OutboundLink.vue'
|
|
||||||
|
|
||||||
const normalizePath = (path: string): string => {
|
|
||||||
path = path
|
|
||||||
.replace(/#.*$/, '')
|
|
||||||
.replace(/\?.*$/, '')
|
|
||||||
.replace(/\.html$/, '')
|
|
||||||
if (path.endsWith('/')) {
|
|
||||||
path += 'index'
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: {
|
|
||||||
OutboundLink
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
item: {
|
|
||||||
type: Object as PropType<DefaultTheme.NavItemWithLink>,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const item = props.item
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const classes = computed(() => ({
|
|
||||||
active: isActiveLink.value,
|
|
||||||
external: isExternalLink.value
|
|
||||||
}))
|
|
||||||
|
|
||||||
const isActiveLink = computed(() => {
|
|
||||||
return normalizePath(withBase(item.link)) === normalizePath(route.path)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isExternalLink = computed(() => {
|
|
||||||
return isExternal(item.link)
|
|
||||||
})
|
|
||||||
|
|
||||||
const href = computed(() => {
|
|
||||||
return isExternalLink.value ? item.link : withBase(item.link)
|
|
||||||
})
|
|
||||||
|
|
||||||
const target = computed(() => {
|
|
||||||
if (item.target) {
|
|
||||||
return item.target
|
|
||||||
}
|
|
||||||
|
|
||||||
return isExternalLink.value ? '_blank' : ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const rel = computed(() => {
|
|
||||||
if (item.rel) {
|
|
||||||
return item.rel
|
|
||||||
}
|
|
||||||
|
|
||||||
return isExternalLink.value ? 'noopener noreferrer' : ''
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
classes,
|
|
||||||
isActiveLink,
|
|
||||||
isExternalLink,
|
|
||||||
href,
|
|
||||||
target,
|
|
||||||
rel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
@ -1,101 +0,0 @@
|
|||||||
import { computed } from 'vue'
|
|
||||||
import { useSiteData, useSiteDataByRoute, useRoute } from 'vitepress'
|
|
||||||
import { inBrowser } from '/@app/utils'
|
|
||||||
import NavBarLink from './NavBarLink.vue'
|
|
||||||
import NavDropdownLink from './NavDropdownLink.vue'
|
|
||||||
import { DefaultTheme } from '../config'
|
|
||||||
|
|
||||||
const platforms = ['GitHub', 'GitLab', 'Bitbucket'].map(
|
|
||||||
(platform) => [platform, new RegExp(platform, 'i')] as const
|
|
||||||
)
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NavBarLink,
|
|
||||||
NavDropdownLink
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const siteDataByRoute = useSiteDataByRoute()
|
|
||||||
const siteData = useSiteData()
|
|
||||||
const route = useRoute()
|
|
||||||
const repoInfo = computed(() => {
|
|
||||||
const theme = siteData.value.themeConfig as DefaultTheme.Config
|
|
||||||
const repo = theme.docsRepo || theme.repo
|
|
||||||
let text: string | undefined = theme.repoLabel
|
|
||||||
|
|
||||||
if (repo) {
|
|
||||||
const link = /^https?:/.test(repo) ? repo : `https://github.com/${repo}`
|
|
||||||
if (!text) {
|
|
||||||
// if no label is provided, deduce it from the repo url
|
|
||||||
const repoHosts = link.match(/^https?:\/\/[^/]+/)
|
|
||||||
if (repoHosts) {
|
|
||||||
const repoHost = repoHosts[0]
|
|
||||||
const foundPlatform = platforms.find(([_platform, re]) =>
|
|
||||||
re.test(repoHost)
|
|
||||||
)
|
|
||||||
text = foundPlatform && foundPlatform[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { link, text: text || 'Source' }
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
const localeCandidates = computed(() => {
|
|
||||||
const locales = siteData.value.themeConfig.locales
|
|
||||||
if (!locales) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const localeKeys = Object.keys(locales)
|
|
||||||
if (localeKeys.length <= 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle site base
|
|
||||||
const siteBase = inBrowser ? siteData.value.base : '/'
|
|
||||||
const siteBaseWithoutSuffix = siteBase.endsWith('/')
|
|
||||||
? siteBase.slice(0, -1)
|
|
||||||
: siteBase
|
|
||||||
// remove site base in browser env
|
|
||||||
const routerPath = route.path.slice(siteBaseWithoutSuffix.length)
|
|
||||||
|
|
||||||
const currentLangBase = localeKeys.find((v) => {
|
|
||||||
if (v === '/') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return routerPath.startsWith(v)
|
|
||||||
})
|
|
||||||
const currentContentPath = currentLangBase
|
|
||||||
? routerPath.substring(currentLangBase.length - 1)
|
|
||||||
: routerPath
|
|
||||||
const candidates = localeKeys.map((v) => {
|
|
||||||
const localePath = v.endsWith('/') ? v.slice(0, -1) : v
|
|
||||||
return {
|
|
||||||
text: locales[v].label || locales[v].lang,
|
|
||||||
link: `${localePath}${currentContentPath}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const currentLangKey = currentLangBase ? currentLangBase : '/'
|
|
||||||
const selectText = locales[currentLangKey].selectText
|
|
||||||
? locales[currentLangKey].selectText
|
|
||||||
: 'Languages'
|
|
||||||
return {
|
|
||||||
text: selectText,
|
|
||||||
items: candidates
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const navData = computed(() => {
|
|
||||||
return siteDataByRoute.value.themeConfig.nav
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
navData,
|
|
||||||
repoInfo,
|
|
||||||
localeCandidates
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import NavBarLink from './NavBarLink.vue'
|
|
||||||
import { defineComponent, ref, watch, PropType } from 'vue'
|
|
||||||
import { useRoute } from 'vitepress'
|
|
||||||
import { DefaultTheme } from '../config'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'DropdownLink',
|
|
||||||
components: {
|
|
||||||
NavBarLink
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
item: {
|
|
||||||
type: Object as PropType<DefaultTheme.NavItemWithChildren>,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const open = ref(false)
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route.path,
|
|
||||||
() => {
|
|
||||||
open.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const setOpen = (value: boolean) => {
|
|
||||||
open.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
const isLastItemOfArray = <T>(item: T, array: T[]) => {
|
|
||||||
return array.length && array.indexOf(item) === array.length - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
isLastItemOfArray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
@ -1,61 +0,0 @@
|
|||||||
import { computed } from 'vue'
|
|
||||||
import { useRoute, useSiteData } from 'vitepress'
|
|
||||||
import { DefaultTheme } from '../config'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
setup() {
|
|
||||||
const route = useRoute()
|
|
||||||
// TODO: could this be useSiteData<DefaultTheme.Config> or is the siteData
|
|
||||||
// resolved and has a different structure?
|
|
||||||
const siteData = useSiteData()
|
|
||||||
|
|
||||||
const resolveLink = (targetLink: string) => {
|
|
||||||
let target: DefaultTheme.SideBarLink | undefined
|
|
||||||
Object.keys(siteData.value.themeConfig.sidebar).some((k) => {
|
|
||||||
return siteData.value.themeConfig.sidebar[k].some(
|
|
||||||
(v: { children: any }) => {
|
|
||||||
if (Array.isArray(v.children)) {
|
|
||||||
target = v.children.find((value: any) => {
|
|
||||||
return value.link === targetLink
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return !!target
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = computed(() => {
|
|
||||||
const pageData = route.data
|
|
||||||
if (pageData.frontmatter.next === false) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
if (typeof pageData.frontmatter.next === 'string') {
|
|
||||||
return resolveLink(pageData.frontmatter.next)
|
|
||||||
}
|
|
||||||
return pageData.next
|
|
||||||
})
|
|
||||||
|
|
||||||
const prev = computed(() => {
|
|
||||||
const pageData = route.data
|
|
||||||
if (pageData.frontmatter.prev === false) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
if (typeof pageData.frontmatter.prev === 'string') {
|
|
||||||
return resolveLink(pageData.frontmatter.prev)
|
|
||||||
}
|
|
||||||
return pageData.prev
|
|
||||||
})
|
|
||||||
|
|
||||||
const hasLinks = computed(() => {
|
|
||||||
return !!next || !!prev
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
next,
|
|
||||||
prev,
|
|
||||||
hasLinks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
import { useRoute, useSiteDataByRoute } from 'vitepress'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { Header } from '../../../../types/shared'
|
|
||||||
import { getPathDirName } from '../utils'
|
|
||||||
import { DefaultTheme } from '../config'
|
|
||||||
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
|
|
||||||
import NavBarLinks from './NavBarLinks.vue'
|
|
||||||
import { SideBarItem } from './SideBarItem'
|
|
||||||
|
|
||||||
export interface ResolvedSidebarItem {
|
|
||||||
text: string
|
|
||||||
link?: string
|
|
||||||
isGroup?: boolean
|
|
||||||
children?: ResolvedSidebarItem[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResolvedSidebar = ResolvedSidebarItem[]
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NavBarLinks,
|
|
||||||
SideBarItem
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const route = useRoute()
|
|
||||||
const siteData = useSiteDataByRoute()
|
|
||||||
|
|
||||||
useActiveSidebarLinks()
|
|
||||||
|
|
||||||
return {
|
|
||||||
items: computed(() => {
|
|
||||||
const {
|
|
||||||
headers,
|
|
||||||
frontmatter: { sidebar, sidebarDepth = 2 }
|
|
||||||
} = route.data
|
|
||||||
|
|
||||||
if (sidebar === 'auto') {
|
|
||||||
// auto, render headers of current page
|
|
||||||
return resolveAutoSidebar(headers, sidebarDepth)
|
|
||||||
} else if (Array.isArray(sidebar)) {
|
|
||||||
// in-page array config
|
|
||||||
return resolveArraySidebar(sidebar, sidebarDepth)
|
|
||||||
} else if (sidebar === false) {
|
|
||||||
return []
|
|
||||||
} else {
|
|
||||||
// no explicit page sidebar config
|
|
||||||
// check global theme config
|
|
||||||
const { sidebar: themeSidebar } = siteData.value.themeConfig
|
|
||||||
if (themeSidebar === 'auto') {
|
|
||||||
return resolveAutoSidebar(headers, sidebarDepth)
|
|
||||||
} else if (Array.isArray(themeSidebar)) {
|
|
||||||
return resolveArraySidebar(themeSidebar, sidebarDepth)
|
|
||||||
} else if (themeSidebar === false) {
|
|
||||||
return []
|
|
||||||
} else if (typeof themeSidebar === 'object') {
|
|
||||||
return resolveMultiSidebar(
|
|
||||||
themeSidebar,
|
|
||||||
route.path,
|
|
||||||
headers,
|
|
||||||
sidebarDepth
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveAutoSidebar(headers: Header[], depth: number): ResolvedSidebar {
|
|
||||||
const ret: ResolvedSidebar = []
|
|
||||||
|
|
||||||
if (headers === undefined) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastH2: ResolvedSidebarItem | undefined = undefined
|
|
||||||
headers.forEach(({ level, title, slug }) => {
|
|
||||||
if (level - 1 > depth) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const item: ResolvedSidebarItem = {
|
|
||||||
text: title,
|
|
||||||
link: `#${slug}`
|
|
||||||
}
|
|
||||||
if (level === 2) {
|
|
||||||
lastH2 = item
|
|
||||||
ret.push(item)
|
|
||||||
} else if (lastH2) {
|
|
||||||
;(lastH2.children || (lastH2.children = [])).push(item)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveArraySidebar(
|
|
||||||
config: DefaultTheme.SideBarItem[],
|
|
||||||
depth: number
|
|
||||||
): ResolvedSidebar {
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveMultiSidebar(
|
|
||||||
config: DefaultTheme.MultiSideBarConfig,
|
|
||||||
path: string,
|
|
||||||
headers: Header[],
|
|
||||||
depth: number
|
|
||||||
): ResolvedSidebar {
|
|
||||||
const paths = [path, Object.keys(config)[0]]
|
|
||||||
const item = paths.map((x) => config[getPathDirName(x)]).find(Boolean)
|
|
||||||
|
|
||||||
if (Array.isArray(item)) {
|
|
||||||
return resolveArraySidebar(item, depth)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item === 'auto') {
|
|
||||||
return resolveAutoSidebar(headers, depth)
|
|
||||||
}
|
|
||||||
|
|
||||||
return []
|
|
||||||
}
|
|
Loading…
Reference in new issue