mirror of https://github.com/vuejs/vitepress
parent
621ca3e26f
commit
1836934f7c
@ -1,70 +1,69 @@
|
||||
<template>
|
||||
<div class="nav-item">
|
||||
<div class="navbar-link">
|
||||
<a
|
||||
class="nav-link"
|
||||
class="item"
|
||||
:class="classes"
|
||||
:href="href"
|
||||
:target="target"
|
||||
:rel="rel"
|
||||
:aria-label="item.ariaLabel"
|
||||
>
|
||||
{{ item.text }}
|
||||
<OutboundLink v-if="isExternalLink" />
|
||||
{{ item.text }} <OutboundLink v-if="isExternalLink" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./NavBarLink"></script>
|
||||
|
||||
<style>
|
||||
.nav-item {
|
||||
<style scoped>
|
||||
.navbar-link {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-left: 1.5rem;
|
||||
line-height: 2rem;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 719px) {
|
||||
.nav-item {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
padding: 0.3rem 1.5rem;
|
||||
@media (min-width: 720px) {
|
||||
.navbar-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navbar-link + .navbar-link,
|
||||
.dropdown-wrapper + .navbar-link {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
.item {
|
||||
display: block;
|
||||
margin-bottom: -2px;
|
||||
border-bottom: 2px solid transparent;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4rem;
|
||||
line-height: 40px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--c-text);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active {
|
||||
border-bottom-color: var(--c-brand);
|
||||
.item:hover,
|
||||
.item.active {
|
||||
text-decoration: none;
|
||||
color: var(--c-brand);
|
||||
}
|
||||
|
||||
.nav-link.external:hover {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
@media (min-width: 720px) {
|
||||
.item {
|
||||
border-bottom: 2px solid transparent;
|
||||
line-height: 1.5rem;
|
||||
font-size: .9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 719px) {
|
||||
.nav-link {
|
||||
line-height: 1.7;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
.item:hover,
|
||||
.item.active {
|
||||
color: var(--c-text);
|
||||
border-bottom-color: var(--c-brand);
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active {
|
||||
color: var(--c-brand);
|
||||
.item.external:hover {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -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 []
|
||||
}
|
@ -1,96 +1,72 @@
|
||||
<template>
|
||||
<NavBarLinks class="show-mobile" />
|
||||
<aside class="sidebar" :class="{ open }">
|
||||
<div class="nav">
|
||||
<NavBarLinks class="show-mobile" />
|
||||
</div>
|
||||
|
||||
<slot name="top" />
|
||||
<slot name="sidebar-top" />
|
||||
|
||||
<ul class="sidebar">
|
||||
<SideBarItem v-for="item of items" :item="item" />
|
||||
</ul>
|
||||
<SideBarLinks />
|
||||
|
||||
<slot name="bottom" />
|
||||
<slot name="sidebar-bottom" />
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script src="./SideBar"></script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import NavBarLinks from './NavBarLinks.vue'
|
||||
import SideBarLinks from './SideBarLinks.vue'
|
||||
|
||||
<style>
|
||||
.show-mobile {
|
||||
display: none;
|
||||
}
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NavBarLinks,
|
||||
SideBarLinks
|
||||
},
|
||||
|
||||
@media screen and (max-width: 719px) {
|
||||
.show-mobile {
|
||||
display: block;
|
||||
props: {
|
||||
open: { type: Boolean, required: true }
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar,
|
||||
.sidebar-items {
|
||||
list-style-type: none;
|
||||
line-height: 2;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sidebar {
|
||||
padding: 1.5rem 0;
|
||||
position: fixed;
|
||||
top: var(--header-height);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: var(--z-index-sidebar);
|
||||
border-right: 1px solid var(--c-divider);
|
||||
width: 16.4rem;
|
||||
background-color: var(--c-bg);
|
||||
overflow-y: auto;
|
||||
transform: translateX(-100%);
|
||||
transition: transform .25s ease;
|
||||
}
|
||||
|
||||
.sidebar-data {
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 719px) {
|
||||
.sidebar-data {
|
||||
padding: 1rem;
|
||||
@media (min-width: 720px) {
|
||||
.sidebar {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-items .sidebar-items {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.sidebar-items .sidebar-items .sidebar-link {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.sidebar-items .sidebar-items .sidebar-link.active {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sidebar-items .sidebar-link {
|
||||
padding: 0.35rem 1rem 0.35rem 2rem;
|
||||
line-height: 1.4;
|
||||
font-size: 0.95em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.sidebar-item + .sidebar-item {
|
||||
padding-top: 0.75rem;
|
||||
@media (min-width: 960px) {
|
||||
.sidebar {
|
||||
width: 20rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-items > .sidebar-item + .sidebar-item {
|
||||
padding-top: 0;
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.sidebar-link {
|
||||
.nav {
|
||||
display: block;
|
||||
margin: 0;
|
||||
border-left: 0.25rem solid transparent;
|
||||
padding: 0.35rem 1.5rem 0.35rem 1.25rem;
|
||||
line-height: 1.7;
|
||||
font-size: 1.05em;
|
||||
font-weight: 700;
|
||||
color: var(--c-text);
|
||||
}
|
||||
|
||||
a.sidebar-link:hover {
|
||||
text-decoration: none;
|
||||
color: var(--c-brand);
|
||||
}
|
||||
|
||||
a.sidebar-link.active {
|
||||
border-left-color: var(--c-brand);
|
||||
font-weight: 600;
|
||||
color: var(--c-brand);
|
||||
@media (min-width: 720px) {
|
||||
.nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<ul v-if="items.length > 0" class="sidebar-links">
|
||||
<SideBarLink
|
||||
v-for="item of items"
|
||||
:key="item.text"
|
||||
:item="item"
|
||||
/>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { useSideBar } from '../composables/sideBar'
|
||||
import { SideBarLink } from './SideBarLink'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SideBarLink
|
||||
},
|
||||
|
||||
setup() {
|
||||
const items = useSideBar()
|
||||
|
||||
return {
|
||||
items
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -0,0 +1,77 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useSiteDataByRoute } from 'vitepress'
|
||||
import { Header } from '/@types/shared'
|
||||
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
|
||||
import { getSideBarConfig } from '../support/sideBar'
|
||||
import { DefaultTheme } from '../config'
|
||||
|
||||
export function useSideBar() {
|
||||
const route = useRoute()
|
||||
const site = useSiteDataByRoute()
|
||||
|
||||
useActiveSidebarLinks()
|
||||
|
||||
return computed(() => {
|
||||
// at first, we'll check if we can find the sidebar setting in frontmatter.
|
||||
const headers = route.data.headers
|
||||
const frontSidebar = route.data.frontmatter.sidebar
|
||||
const sidebarDepth = route.data.frontmatter.sidebarDepth
|
||||
|
||||
// if it's `false`, we'll just return an empty array here.
|
||||
if (frontSidebar === false) {
|
||||
return []
|
||||
}
|
||||
|
||||
// if it's `atuo`, render headers of the current page
|
||||
if (frontSidebar === 'auto') {
|
||||
return resolveAutoSidebar(headers, sidebarDepth)
|
||||
}
|
||||
|
||||
// now, there's no sidebar setting at frontmatter; let's see the configs
|
||||
const themeSidebar = getSideBarConfig(
|
||||
site.value.themeConfig.sidebar,
|
||||
route.path
|
||||
)
|
||||
|
||||
if (themeSidebar === false) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (themeSidebar === 'auto') {
|
||||
return resolveAutoSidebar(headers, sidebarDepth)
|
||||
}
|
||||
|
||||
return themeSidebar
|
||||
})
|
||||
}
|
||||
|
||||
function resolveAutoSidebar(
|
||||
headers: Header[],
|
||||
depth: number
|
||||
): DefaultTheme.SideBarItem[] {
|
||||
const ret: DefaultTheme.SideBarItem[] = []
|
||||
|
||||
if (headers === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
let lastH2: DefaultTheme.SideBarItem | undefined = undefined
|
||||
headers.forEach(({ level, title, slug }) => {
|
||||
if (level - 1 > depth) {
|
||||
return
|
||||
}
|
||||
|
||||
const item: DefaultTheme.SideBarItem = {
|
||||
text: title,
|
||||
link: `#${slug}`
|
||||
}
|
||||
if (level === 2) {
|
||||
lastH2 = item
|
||||
ret.push(item)
|
||||
} else if (lastH2) {
|
||||
;((lastH2 as any).children || ((lastH2 as any).children = [])).push(item)
|
||||
}
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
.sidebar-links {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.sidebar-link-item {
|
||||
display: block;
|
||||
margin: 0;
|
||||
border-left: .25rem solid transparent;
|
||||
color: var(--c-text);
|
||||
}
|
||||
|
||||
a.sidebar-link-item:hover {
|
||||
text-decoration: none;
|
||||
color: var(--c-brand);
|
||||
}
|
||||
|
||||
a.sidebar-link-item.active {
|
||||
color: var(--c-brand);
|
||||
}
|
||||
|
||||
.sidebar > .sidebar-links {
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.sidebar > .sidebar-links > .sidebar-link + .sidebar-link {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.sidebar > .sidebar-links > .sidebar-link > .sidebar-link-item {
|
||||
padding: .35rem 1.5rem .35rem 1.25rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sidebar > .sidebar-links > .sidebar-link > a.sidebar-link-item.active {
|
||||
border-left-color: var(--c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sidebar > .sidebar-links > .sidebar-link > .sidebar-links > .sidebar-link > .sidebar-link-item {
|
||||
display: block;
|
||||
padding: .35rem 1.5rem .35rem 2rem;
|
||||
line-height: 1.4;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.sidebar > .sidebar-links > .sidebar-link > .sidebar-links > .sidebar-link > a.sidebar-link-item.active {
|
||||
border-left-color: var(--c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sidebar >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-link-item {
|
||||
display: block;
|
||||
padding: .3rem 1.5rem .3rem 3rem;
|
||||
line-height: 1.4;
|
||||
font-size: .9rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.sidebar >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-links >
|
||||
.sidebar-link >
|
||||
.sidebar-link-item {
|
||||
display: block;
|
||||
padding: .3rem 1.5rem .3rem 4rem;
|
||||
line-height: 1.4;
|
||||
font-size: .9rem;
|
||||
font-weight: 400;
|
||||
}
|
Loading…
Reference in new issue