Skip to content

Commit c4d3c36

Browse files
committed
auto-infer prev/next links
1 parent bad3d92 commit c4d3c36

13 files changed

+151
-149
lines changed

docs/guide/README.md

-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
---
2-
next: ./getting-started
3-
---
4-
51
# Introduction
62

73
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.

docs/guide/assets.md

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
---
2-
prev: ./basic-config
3-
next: ./markdown
4-
---
5-
61
# Asset Handling
72

83
## Relative URLs

docs/guide/basic-config.md

-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
---
2-
prev: ./getting-started
3-
next: ./assets
4-
sidebarDepth: 2
5-
---
6-
71
# Configuration
82

93
## Config File

docs/guide/custom-themes.md

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
---
2-
prev: ./using-vue
3-
next: ./deploy
4-
---
5-
61
# Custom Themes
72

83
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:

docs/guide/deploy.md

-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
---
2-
prev: ./custom-themes
3-
---
4-
51
# Deploying
62

73
## GitHub Pages

docs/guide/getting-started.md

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
---
2-
prev: ./
3-
next: ./basic-config
4-
---
5-
61
# Getting Started
72

83
## Global Installation

docs/guide/markdown.md

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
---
2-
prev: ./assets
3-
next: ./using-vue
42
meta:
53
- name: keywords
64
content: static docs generator vue

docs/guide/using-vue.md

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
---
2-
prev: ./markdown
3-
next: ./custom-themes
4-
---
5-
61
# Using Vue in Markdown
72

83
## DOM Access Restrictions

lib/default-theme/Layout.vue

+10-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
@touchstart="onTouchStart"
99
@touchend="onTouchEnd">
1010
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar"/>
11-
<Sidebar @toggle-sidebar="toggleSidebar"/>
11+
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar"/>
1212
<div class="custom-layout" v-if="$page.frontmatter.layout">
1313
<component :is="$page.frontmatter.layout"/>
1414
</div>
1515
<Home v-else-if="$page.frontmatter.home"/>
16-
<Page v-else/>
16+
<Page v-else :sidebar-items="sidebarItems"/>
1717
</div>
1818
</template>
1919

@@ -25,6 +25,7 @@ import Navbar from './Navbar.vue'
2525
import Page from './Page.vue'
2626
import Sidebar from './Sidebar.vue'
2727
import { pathToComponentName, getTitle, getLang } from '../app/util'
28+
import { resolveSidebarItems } from './util'
2829
2930
export default {
3031
components: { Home, Page, Sidebar, Navbar },
@@ -54,6 +55,13 @@ export default {
5455
frontmatter.sidebar !== false
5556
)
5657
)
58+
},
59+
sidebarItems () {
60+
return resolveSidebarItems(
61+
this.$page,
62+
this.$route,
63+
this.$site
64+
)
5765
}
5866
},
5967

lib/default-theme/Page.vue

+40-2
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,27 @@ import { resolvePage, normalize, outboundRE, endingSlashRE } from './util'
2828
2929
export default {
3030
components: { OutboundLink },
31+
props: ['sidebarItems'],
3132
computed: {
3233
prev () {
3334
const prev = this.$page.frontmatter.prev
34-
return prev && resolvePage(this.$site.pages, prev, this.$route.path)
35+
if (prev === false) {
36+
return
37+
} else if (prev) {
38+
return resolvePage(this.$site.pages, prev, this.$route.path)
39+
} else {
40+
return resolvePrev(this.$page, this.sidebarItems)
41+
}
3542
},
3643
next () {
3744
const next = this.$page.frontmatter.next
38-
return next && resolvePage(this.$site.pages, next, this.$route.path)
45+
if (next === false) {
46+
return
47+
} else if (next) {
48+
return resolvePage(this.$site.pages, next, this.$route.path)
49+
} else {
50+
return resolveNext(this.$page, this.sidebarItems)
51+
}
3952
},
4053
editLink () {
4154
const {
@@ -66,6 +79,31 @@ export default {
6679
}
6780
}
6881
}
82+
83+
function resolvePrev (page, items) {
84+
return find(page, items, -1)
85+
}
86+
87+
function resolveNext (page, items) {
88+
return find(page, items, 1)
89+
}
90+
91+
function find (page, items, offset) {
92+
const res = []
93+
items.forEach(item => {
94+
if (item.type === 'group') {
95+
res.push(...item.children || [])
96+
} else {
97+
res.push(item)
98+
}
99+
})
100+
for (let i = 0; i < res.length; i++) {
101+
const cur = res[i]
102+
if (cur.type === 'page' && cur.path === page.path) {
103+
return res[i + offset]
104+
}
105+
}
106+
}
69107
</script>
70108

71109
<style lang="stylus">

lib/default-theme/Sidebar.vue

+5-94
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
22
<div class="sidebar">
33
<NavLinks/>
4-
<ul class="sidebar-links" v-if="sidebarItems.length">
5-
<li v-for="(item, i) in sidebarItems">
4+
<ul class="sidebar-links" v-if="items.length">
5+
<li v-for="(item, i) in items">
66
<SidebarGroup v-if="item.type === 'group'"
77
:item="item"
88
:first="i === 0"
@@ -19,10 +19,11 @@
1919
import SidebarGroup from './SidebarGroup.vue'
2020
import SidebarLink, { groupHeaders } from './SidebarLink.vue'
2121
import NavLinks from './NavLinks.vue'
22-
import { resolvePage, isActive } from './util'
22+
import { isActive, resolveSidebarItems } from './util'
2323
2424
export default {
2525
components: { SidebarGroup, SidebarLink, NavLinks },
26+
props: ['items'],
2627
data () {
2728
return {
2829
openGroupIndex: 0
@@ -36,20 +37,11 @@ export default {
3637
this.refreshIndex()
3738
}
3839
},
39-
computed: {
40-
sidebarItems () {
41-
return resolveSidebarItems(
42-
this.$page,
43-
this.$route,
44-
this.$site
45-
)
46-
}
47-
},
4840
methods: {
4941
refreshIndex () {
5042
const index = resolveOpenGroupIndex(
5143
this.$route,
52-
this.sidebarItems
44+
this.items
5345
)
5446
if (index > -1) {
5547
this.openGroupIndex = index
@@ -73,87 +65,6 @@ function resolveOpenGroupIndex (route, items) {
7365
}
7466
return -1
7567
}
76-
77-
function resolveSidebarItems (page, route, site) {
78-
const pageSidebarConfig = page.frontmatter.sidebar
79-
if (pageSidebarConfig === 'auto') {
80-
return resolveHeaders(page)
81-
}
82-
const { pages, themeConfig } = site
83-
const sidebarConfig = themeConfig.sidebar
84-
if (!sidebarConfig) {
85-
return pages.map(p => Object.assign({ type: 'page' }, p))
86-
} else {
87-
const { base, config } = resolveMatchingSidebarConfig(route, sidebarConfig)
88-
return config
89-
? config.map(item => resolveItem(item, pages, base))
90-
: []
91-
}
92-
}
93-
94-
function resolveHeaders (page) {
95-
const headers = groupHeaders(page.headers || [])
96-
return [{
97-
type: 'group',
98-
collapsable: false,
99-
title: page.title,
100-
children: headers.map(h => ({
101-
type: 'auto',
102-
title: h.title,
103-
basePath: page.path,
104-
path: page.path + '#' + h.slug,
105-
children: h.children || []
106-
}))
107-
}]
108-
}
109-
110-
function resolveMatchingSidebarConfig (route, sidebarConfig) {
111-
if (Array.isArray(sidebarConfig)) {
112-
return {
113-
base: '/',
114-
config: sidebarConfig
115-
}
116-
}
117-
for (const base in sidebarConfig) {
118-
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
119-
return {
120-
base,
121-
config: sidebarConfig[base]
122-
}
123-
}
124-
}
125-
return {}
126-
}
127-
128-
function ensureEndingSlash (path) {
129-
return /(\.html|\/)$/.test(path)
130-
? path
131-
: path + '/'
132-
}
133-
134-
function resolveItem (item, pages, base, isNested) {
135-
if (typeof item === 'string') {
136-
return resolvePage(pages, item, base)
137-
} else if (Array.isArray(item)) {
138-
return Object.assign(resolvePage(pages, item[0], base), {
139-
title: item[1]
140-
})
141-
} else {
142-
if (isNested) {
143-
console.error(
144-
'[vuepress] Nested sidebar groups are not supported. ' +
145-
'Consider using navbar + categories instead.'
146-
)
147-
}
148-
const children = item.children || []
149-
return {
150-
type: 'group',
151-
title: item.title,
152-
children: children.map(child => resolveItem(child, pages, base, true)),
153-
collapsable: item.collapsable !== false
154-
}
155-
}
156-
}
15768
</script>
15869

15970
<style lang="stylus">

lib/default-theme/SidebarLink.vue

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
import { isActive, hashRE } from './util'
2+
import { isActive, hashRE, groupHeaders } from './util'
33
44
export default {
55
functional: true,
@@ -41,20 +41,6 @@ function renderLink (h, to, text, active) {
4141
}, text)
4242
}
4343
44-
export function groupHeaders (headers) {
45-
// group h3s under h2
46-
headers = headers.map(h => Object.assign({}, h))
47-
let lastH2
48-
headers.forEach(h => {
49-
if (h.level === 2) {
50-
lastH2 = h
51-
} else if (lastH2) {
52-
(lastH2.children || (lastH2.children = [])).push(h)
53-
}
54-
})
55-
return headers.filter(h => h.level === 2)
56-
}
57-
5844
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
5945
if (!children || depth > maxDepth) return null
6046
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {

0 commit comments

Comments
 (0)