提交 c4d3c366 编写于 作者: E Evan You

auto-infer prev/next links

上级 bad3d92a
---
next: ./getting-started
---
# Introduction
VuePress is composed of two parts: a minimalistic static site generator with a Vue-powered theming system, and a default theme optimized for writing technical documentation. It was created to support the documentation needs of Vue's own sub projects.
......
---
prev: ./basic-config
next: ./markdown
---
# Asset Handling
## Relative URLs
......
---
prev: ./getting-started
next: ./assets
sidebarDepth: 2
---
# Configuration
## Config File
......
---
prev: ./using-vue
next: ./deploy
---
# Custom Themes
VuePress uses Vue single file components for custom themes. To use a custom layout, create a `.vuepress/theme` directory in your docs root, and then create a `Layout.vue` file:
......
---
prev: ./custom-themes
---
# Deploying
## GitHub Pages
......
---
prev: ./
next: ./basic-config
---
# Getting Started
## Global Installation
......
---
prev: ./assets
next: ./using-vue
meta:
- name: keywords
content: static docs generator vue
......
---
prev: ./markdown
next: ./custom-themes
---
# Using Vue in Markdown
## DOM Access Restrictions
......
......@@ -8,12 +8,12 @@
@touchstart="onTouchStart"
@touchend="onTouchEnd">
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar"/>
<Sidebar @toggle-sidebar="toggleSidebar"/>
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar"/>
<div class="custom-layout" v-if="$page.frontmatter.layout">
<component :is="$page.frontmatter.layout"/>
</div>
<Home v-else-if="$page.frontmatter.home"/>
<Page v-else/>
<Page v-else :sidebar-items="sidebarItems"/>
</div>
</template>
......@@ -25,6 +25,7 @@ import Navbar from './Navbar.vue'
import Page from './Page.vue'
import Sidebar from './Sidebar.vue'
import { pathToComponentName, getTitle, getLang } from '../app/util'
import { resolveSidebarItems } from './util'
export default {
components: { Home, Page, Sidebar, Navbar },
......@@ -54,6 +55,13 @@ export default {
frontmatter.sidebar !== false
)
)
},
sidebarItems () {
return resolveSidebarItems(
this.$page,
this.$route,
this.$site
)
}
},
......
......@@ -28,14 +28,27 @@ import { resolvePage, normalize, outboundRE, endingSlashRE } from './util'
export default {
components: { OutboundLink },
props: ['sidebarItems'],
computed: {
prev () {
const prev = this.$page.frontmatter.prev
return prev && resolvePage(this.$site.pages, prev, this.$route.path)
if (prev === false) {
return
} else if (prev) {
return resolvePage(this.$site.pages, prev, this.$route.path)
} else {
return resolvePrev(this.$page, this.sidebarItems)
}
},
next () {
const next = this.$page.frontmatter.next
return next && resolvePage(this.$site.pages, next, this.$route.path)
if (next === false) {
return
} else if (next) {
return resolvePage(this.$site.pages, next, this.$route.path)
} else {
return resolveNext(this.$page, this.sidebarItems)
}
},
editLink () {
const {
......@@ -66,6 +79,31 @@ export default {
}
}
}
function resolvePrev (page, items) {
return find(page, items, -1)
}
function resolveNext (page, items) {
return find(page, items, 1)
}
function find (page, items, offset) {
const res = []
items.forEach(item => {
if (item.type === 'group') {
res.push(...item.children || [])
} else {
res.push(item)
}
})
for (let i = 0; i < res.length; i++) {
const cur = res[i]
if (cur.type === 'page' && cur.path === page.path) {
return res[i + offset]
}
}
}
</script>
<style lang="stylus">
......
<template>
<div class="sidebar">
<NavLinks/>
<ul class="sidebar-links" v-if="sidebarItems.length">
<li v-for="(item, i) in sidebarItems">
<ul class="sidebar-links" v-if="items.length">
<li v-for="(item, i) in items">
<SidebarGroup v-if="item.type === 'group'"
:item="item"
:first="i === 0"
......@@ -19,10 +19,11 @@
import SidebarGroup from './SidebarGroup.vue'
import SidebarLink, { groupHeaders } from './SidebarLink.vue'
import NavLinks from './NavLinks.vue'
import { resolvePage, isActive } from './util'
import { isActive, resolveSidebarItems } from './util'
export default {
components: { SidebarGroup, SidebarLink, NavLinks },
props: ['items'],
data () {
return {
openGroupIndex: 0
......@@ -36,20 +37,11 @@ export default {
this.refreshIndex()
}
},
computed: {
sidebarItems () {
return resolveSidebarItems(
this.$page,
this.$route,
this.$site
)
}
},
methods: {
refreshIndex () {
const index = resolveOpenGroupIndex(
this.$route,
this.sidebarItems
this.items
)
if (index > -1) {
this.openGroupIndex = index
......@@ -73,87 +65,6 @@ function resolveOpenGroupIndex (route, items) {
}
return -1
}
function resolveSidebarItems (page, route, site) {
const pageSidebarConfig = page.frontmatter.sidebar
if (pageSidebarConfig === 'auto') {
return resolveHeaders(page)
}
const { pages, themeConfig } = site
const sidebarConfig = themeConfig.sidebar
if (!sidebarConfig) {
return pages.map(p => Object.assign({ type: 'page' }, p))
} else {
const { base, config } = resolveMatchingSidebarConfig(route, sidebarConfig)
return config
? config.map(item => resolveItem(item, pages, base))
: []
}
}
function resolveHeaders (page) {
const headers = groupHeaders(page.headers || [])
return [{
type: 'group',
collapsable: false,
title: page.title,
children: headers.map(h => ({
type: 'auto',
title: h.title,
basePath: page.path,
path: page.path + '#' + h.slug,
children: h.children || []
}))
}]
}
function resolveMatchingSidebarConfig (route, sidebarConfig) {
if (Array.isArray(sidebarConfig)) {
return {
base: '/',
config: sidebarConfig
}
}
for (const base in sidebarConfig) {
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
return {
base,
config: sidebarConfig[base]
}
}
}
return {}
}
function ensureEndingSlash (path) {
return /(\.html|\/)$/.test(path)
? path
: path + '/'
}
function resolveItem (item, pages, base, isNested) {
if (typeof item === 'string') {
return resolvePage(pages, item, base)
} else if (Array.isArray(item)) {
return Object.assign(resolvePage(pages, item[0], base), {
title: item[1]
})
} else {
if (isNested) {
console.error(
'[vuepress] Nested sidebar groups are not supported. ' +
'Consider using navbar + categories instead.'
)
}
const children = item.children || []
return {
type: 'group',
title: item.title,
children: children.map(child => resolveItem(child, pages, base, true)),
collapsable: item.collapsable !== false
}
}
}
</script>
<style lang="stylus">
......
<script>
import { isActive, hashRE } from './util'
import { isActive, hashRE, groupHeaders } from './util'
export default {
functional: true,
......@@ -41,20 +41,6 @@ function renderLink (h, to, text, active) {
}, text)
}
export function groupHeaders (headers) {
// group h3s under h2
headers = headers.map(h => Object.assign({}, h))
let lastH2
headers.forEach(h => {
if (h.level === 2) {
lastH2 = h
} else if (lastH2) {
(lastH2.children || (lastH2.children = [])).push(h)
}
})
return headers.filter(h => h.level === 2)
}
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
if (!children || depth > maxDepth) return null
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
......
......@@ -99,3 +99,98 @@ function resolvePath (relative, base, append) {
return stack.join('/')
}
export function resolveSidebarItems (page, route, site) {
const pageSidebarConfig = page.frontmatter.sidebar
if (pageSidebarConfig === 'auto') {
return resolveHeaders(page)
}
const { pages, themeConfig } = site
const sidebarConfig = themeConfig.sidebar
if (!sidebarConfig) {
return pages.map(p => Object.assign({ type: 'page' }, p))
} else {
const { base, config } = resolveMatchingSidebarConfig(route, sidebarConfig)
return config
? config.map(item => resolveItem(item, pages, base))
: []
}
}
function resolveHeaders (page) {
const headers = groupHeaders(page.headers || [])
return [{
type: 'group',
collapsable: false,
title: page.title,
children: headers.map(h => ({
type: 'auto',
title: h.title,
basePath: page.path,
path: page.path + '#' + h.slug,
children: h.children || []
}))
}]
}
export function groupHeaders (headers) {
// group h3s under h2
headers = headers.map(h => Object.assign({}, h))
let lastH2
headers.forEach(h => {
if (h.level === 2) {
lastH2 = h
} else if (lastH2) {
(lastH2.children || (lastH2.children = [])).push(h)
}
})
return headers.filter(h => h.level === 2)
}
function resolveMatchingSidebarConfig (route, sidebarConfig) {
if (Array.isArray(sidebarConfig)) {
return {
base: '/',
config: sidebarConfig
}
}
for (const base in sidebarConfig) {
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
return {
base,
config: sidebarConfig[base]
}
}
}
return {}
}
function ensureEndingSlash (path) {
return /(\.html|\/)$/.test(path)
? path
: path + '/'
}
function resolveItem (item, pages, base, isNested) {
if (typeof item === 'string') {
return resolvePage(pages, item, base)
} else if (Array.isArray(item)) {
return Object.assign(resolvePage(pages, item[0], base), {
title: item[1]
})
} else {
if (isNested) {
console.error(
'[vuepress] Nested sidebar groups are not supported. ' +
'Consider using navbar + categories instead.'
)
}
const children = item.children || []
return {
type: 'group',
title: item.title,
children: children.map(child => resolveItem(child, pages, base, true)),
collapsable: item.collapsable !== false
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册