refactor: migrate default theme to use script-setup

pull/137/head
Evan You 5 years ago
parent e435eec94a
commit ce783e456e

@ -47,7 +47,7 @@
<Debug />
</template>
<script>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import NavBar from './components/NavBar.vue'
import Home from './components/Home.vue'
@ -56,76 +56,56 @@ import SideBar from './components/SideBar.vue'
import Page from './components/Page.vue'
import { useRoute, useSiteData, useSiteDataByRoute } from 'vitepress'
export default {
components: {
Home,
NavBar,
ToggleSideBarButton,
SideBar,
Page
},
const route = useRoute()
const siteData = useSiteData()
const siteRouteData = useSiteDataByRoute()
setup() {
const route = useRoute()
const siteData = useSiteData()
const siteRouteData = useSiteDataByRoute()
const openSideBar = ref(false)
const enableHome = computed(() => !!route.data.frontmatter.home)
const openSideBar = ref(false)
const enableHome = computed(() => !!route.data.frontmatter.home)
const showNavbar = computed(() => {
const { themeConfig } = siteRouteData.value
const { frontmatter } = route.data
if (
frontmatter.navbar === false
|| themeConfig.navbar === false) {
return false
}
return (
siteData.value.title
|| themeConfig.logo
|| themeConfig.repo
|| themeConfig.nav
)
})
const showSidebar = computed(() => {
const { frontmatter } = route.data
const { themeConfig } = siteRouteData.value
return (
!frontmatter.home
&& frontmatter.sidebar !== false
&& ((typeof themeConfig.sidebar === 'object') && (Object.keys(themeConfig.sidebar).length != 0)
|| (Array.isArray(themeConfig.sidebar) && themeConfig.sidebar.length != 0))
)
})
const showNavbar = computed(() => {
const { themeConfig } = siteRouteData.value
const { frontmatter } = route.data
if (frontmatter.navbar === false || themeConfig.navbar === false) {
return false
}
return (
siteData.value.title ||
themeConfig.logo ||
themeConfig.repo ||
themeConfig.nav
)
})
const pageClasses = computed(() => {
return [{
'no-navbar': !showNavbar.value,
'sidebar-open': openSideBar.value,
'no-sidebar': !showSidebar.value
}]
})
const showSidebar = computed(() => {
const { frontmatter } = route.data
const { themeConfig } = siteRouteData.value
return (
!frontmatter.home &&
frontmatter.sidebar !== false &&
((typeof themeConfig.sidebar === 'object' &&
Object.keys(themeConfig.sidebar).length != 0) ||
(Array.isArray(themeConfig.sidebar) && themeConfig.sidebar.length != 0))
)
})
const toggleSidebar = (to) => {
openSideBar.value = typeof to === 'boolean' ? to : !openSideBar.value
const pageClasses = computed(() => {
return [
{
'no-navbar': !showNavbar.value,
'sidebar-open': openSideBar.value,
'no-sidebar': !showSidebar.value
}
]
})
const hideSidebar = toggleSidebar.bind(null, false)
// close the sidebar when navigating to a different location
watch(route, hideSidebar)
// TODO: route only changes when the pathname changes
// listening to hashchange does nothing because it's prevented in router
return {
showNavbar,
showSidebar,
openSideBar,
pageClasses,
enableHome,
toggleSidebar
}
}
const toggleSidebar = (to) => {
openSideBar.value = typeof to === 'boolean' ? to : !openSideBar.value
}
const hideSidebar = toggleSidebar.bind(null, false)
// close the sidebar when navigating to a different location
watch(route, hideSidebar)
// TODO: route only changes when the pathname changes
// listening to hashchange does nothing because it's prevented in router
</script>

@ -2,15 +2,11 @@
<div class="theme">
<h1>404</h1>
<blockquote>{{ getMsg() }}</blockquote>
<a :href="$site.base" aria-label="go to home">
Take me home.
</a>
<a :href="$site.base" aria-label="go to home"> Take me home. </a>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
<script setup>
const msgs = [
`There's nothing here.`,
`How did we get here?`,
@ -18,11 +14,7 @@ const msgs = [
`Looks like we've got some broken links.`
]
export default defineComponent({
setup: () => ({
getMsg() {
return msgs[Math.floor(Math.random() * msgs.length)]
}
})
})
function getMsg() {
return msgs[Math.floor(Math.random() * msgs.length)]
}
</script>

@ -6,25 +6,11 @@
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
<script setup lang="ts">
import { useEditLink } from '../composables/editLink'
import OutboundLink from './icons/OutboundLink.vue'
export default defineComponent({
components: {
OutboundLink
},
setup() {
const { url, text } = useEditLink()
return {
url,
text
}
}
})
const { url, text } = useEditLink()
</script>
<style scoped>

@ -4,88 +4,53 @@
v-if="data.heroImage"
:src="heroImageSrc"
:alt="data.heroAlt || 'hero'"
>
/>
<h1
v-if="data.heroText !== null"
id="main-title"
>
<h1 v-if="data.heroText !== null" id="main-title">
{{ data.heroText || siteTitle || 'Hello' }}
</h1>
<p
v-if="data.tagline !== null"
class="description"
>
<p v-if="data.tagline !== null" class="description">
{{ data.tagline || siteDescription || 'Welcome to your VitePress site' }}
</p>
<p
v-if="data.actionText && data.actionLink"
class="action"
>
<p v-if="data.actionText && data.actionLink" class="action">
<NavBarLink :item="actionLink" />
</p>
<slot name="hero" />
</header>
<div
v-if="data.features && data.features.length"
class="features"
>
<div
v-for="(feature, index) in data.features"
:key="index"
class="feature"
>
<div v-if="data.features && data.features.length" class="features">
<div v-for="(feature, index) in data.features" :key="index" class="feature">
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
<slot name="features" />
</div>
<div
v-if="data.footer"
class="footer"
>
<div v-if="data.footer" class="footer">
{{ data.footer }}
<slot name="footer" />
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue'
<script setup lang="ts">
import { computed } from 'vue'
import NavBarLink from './NavBarLink.vue'
import { withBase } from '../utils'
import { useRoute, useSiteData } from 'vitepress'
export default defineComponent({
components: {
NavBarLink
},
setup() {
const route = useRoute()
const siteData = useSiteData()
const data = computed(() => route.data.frontmatter)
const actionLink = computed(() => ({
link: data.value.actionLink,
text: data.value.actionText
}))
const heroImageSrc = computed(() => withBase(data.value.heroImage))
const siteTitle = computed(() => siteData.value.title)
const siteDescription = computed(() => siteData.value.description)
return {
data,
actionLink,
heroImageSrc,
siteTitle,
siteDescription
}
}
})
const route = useRoute()
const siteData = useSiteData()
const data = computed(() => route.data.frontmatter)
const actionLink = computed(() => ({
link: data.value.actionLink,
text: data.value.actionText
}))
const heroImageSrc = computed(() => withBase(data.value.heroImage))
const siteTitle = computed(() => siteData.value.title)
const siteDescription = computed(() => siteData.value.description)
</script>
<style scoped>
@ -126,7 +91,7 @@ export default defineComponent({
margin-left: 0;
padding: 0.8rem 1.6rem;
border-radius: 4px;
transition: background-color .1s ease;
transition: background-color 0.1s ease;
box-sizing: border-box;
/* TODO: calculating darken 10% color with using style vars from `--accent-color` */
border-bottom: 1px solid #389d70;

@ -1,12 +0,0 @@
import { withBase } from '../utils'
import NavBarLinks from './NavBarLinks.vue'
export default {
components: {
NavBarLinks
},
setup() {
return { withBase }
}
}

@ -17,7 +17,10 @@
<slot name="search" />
</template>
<script src="./NavBar"></script>
<script setup lang="ts">
import { withBase } from '../utils'
import NavBarLinks from './NavBarLinks.vue'
</script>
<style>
.title {

@ -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
}
}
})

@ -14,7 +14,67 @@
</div>
</template>
<script src="./NavBarLink"></script>
<script setup lang="ts">
import { computed, defineOptions } from 'vue'
import { useRoute } from 'vitepress'
import { withBase, isExternal } from '../utils'
import type { 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
}
const { props } = defineOptions<{
props: {
item: DefaultTheme.NavItemWithLink
}
}>()
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' : ''
})
</script>
<style>
.nav-item {

@ -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
}
}
}

@ -11,7 +11,94 @@
</nav>
</template>
<script src="./NavBarLinks"></script>
<script setup lang="ts">
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 type { DefaultTheme } from '../config'
const platforms = ['GitHub', 'GitLab', 'Bitbucket'].map(
(platform) => [platform, new RegExp(platform, 'i')] as const
)
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
})
</script>
<style>
.nav-links {

@ -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
}
}
})

@ -11,7 +11,11 @@
</button>
<ul class="nav-dropdown">
<li v-for="(subItem, index) in item.items" :key="subItem.link || index" class="dropdown-item">
<li
v-for="(subItem, index) in item.items"
:key="subItem.link || index"
class="dropdown-item"
>
<h4 v-if="subItem.items">{{ subItem.text }}</h4>
<ul v-if="subItem.items" class="dropdown-subitem-wrapper">
<li
@ -22,10 +26,10 @@
<NavBarLink
:item="childSubItem"
@focusout="
isLastItemOfArray(childSubItem, subItem.items) &&
isLastItemOfArray(subItem, item.items) &&
setOpen(false)
"
isLastItemOfArray(childSubItem, subItem.items) &&
isLastItemOfArray(subItem, item.items) &&
setOpen(false)
"
/>
</li>
</ul>
@ -40,7 +44,36 @@
</div>
</template>
<script src="./NavDropdownLink"></script>
<script setup lang="ts">
import NavBarLink from './NavBarLink.vue'
import { ref, watch, defineOptions } from 'vue'
import { useRoute } from 'vitepress'
import type { DefaultTheme } from '../config'
defineOptions<{
props: {
item: DefaultTheme.NavItemWithChildren
}
}>()
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
}
</script>
<style>
.dropdown-wrapper {

@ -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
}
}
}

@ -15,28 +15,12 @@
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useNextAndPrevLinks } from '../composables/nextAndPrevLinks'
<script setup lang="ts">
import ArrowLeft from './icons/ArrowLeft.vue'
import ArrowRight from './icons/ArrowRight.vue'
import { useNextAndPrevLinks } from '../composables/nextAndPrevLinks'
export default defineComponent({
components: {
ArrowLeft,
ArrowRight
},
setup () {
const { hasLinks, prev, next } = useNextAndPrevLinks()
return {
hasLinks,
prev,
next
}
}
})
const { hasLinks, prev, next } = useNextAndPrevLinks()
</script>
<style scoped>

@ -13,17 +13,9 @@
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
<script setup lang="ts">
import EditLink from './EditLink.vue'
import NextAndPrevLinks from './NextAndPrevLinks.vue'
export default defineComponent({
components: {
EditLink,
NextAndPrevLinks
}
})
</script>
<style scoped>

@ -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 []
}

@ -10,7 +10,114 @@
<slot name="bottom" />
</template>
<script src="./SideBar"></script>
<script setup lang="ts">
import { useRoute, useSiteDataByRoute } from 'vitepress'
import { computed } from 'vue'
import { getPathDirName } from '../utils'
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
import NavBarLinks from './NavBarLinks.vue'
import { SideBarItem } from './SideBarItem'
import type { DefaultTheme } from '../config'
import type { Header } from '../../../../types/shared'
import type { ResolvedSidebarItem } from './SideBarItem'
type ResolvedSidebar = ResolvedSidebarItem[]
const route = useRoute()
const siteData = useSiteDataByRoute()
useActiveSidebarLinks()
const 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 []
}
</script>
<style>
.show-mobile {

@ -2,7 +2,13 @@ import { useRoute, useSiteData } from 'vitepress'
import { FunctionalComponent, h, VNode } from 'vue'
import { Header } from '../../../../types/shared'
import { joinUrl, isActive } from '../utils'
import { ResolvedSidebarItem } from './SideBar'
export interface ResolvedSidebarItem {
text: string
link?: string
isGroup?: boolean
children?: ResolvedSidebarItem[]
}
interface HeaderWithChildren extends Header {
children?: Header[]

Loading…
Cancel
Save