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