mirror of https://github.com/vuejs/vitepress
parent
1ba209a4d2
commit
c3b7172951
@ -0,0 +1,8 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from '../router'
|
||||
|
||||
export function usePageData() {
|
||||
const route = useRoute()
|
||||
|
||||
return computed(() => route.data)
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="edit-link">
|
||||
<a v-if="url" class="link" :href="url" target="_blank" rel="noopener noreferrer">
|
||||
{{ text }} <OutboundLink class="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { useEditLink } from '../composables/editLink'
|
||||
import OutboundLink from './icons/OutboundLink.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
OutboundLink
|
||||
},
|
||||
|
||||
setup() {
|
||||
const { url, text } = useEditLink()
|
||||
|
||||
return {
|
||||
url,
|
||||
text
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.edit-link {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-weight: 500;
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
text-decoration: none;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
margin-left: 4px;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
</style>
|
@ -1,35 +1,92 @@
|
||||
<template>
|
||||
<div v-if="hasLinks" class="links-wrapper">
|
||||
<div class="prev-link">
|
||||
<div v-if="prev">
|
||||
← <a :href="prev.link">{{ prev.text }}</a>
|
||||
</div>
|
||||
<div v-if="hasLinks" class="next-and-prev-link">
|
||||
<div class="prev">
|
||||
<a v-if="prev" class="link" :href="prev.link">
|
||||
<ArrowLeft class="icon icon-prev" />
|
||||
<span class="text">{{ prev.text }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="next-link">
|
||||
<div v-if="next">
|
||||
<a :href="next.link">{{ next.text }}</a> →
|
||||
</div>
|
||||
<div class="next">
|
||||
<a v-if="next" class="link" :href="next.link">
|
||||
<span class="text">{{ next.text }}</span>
|
||||
<ArrowRight class="icon icon-next" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./NextAndPrevLinks"></script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { useNextAndPrevLinks } from '../composables/nextAndPrevLinks'
|
||||
import ArrowLeft from './icons/ArrowLeft.vue'
|
||||
import ArrowRight from './icons/ArrowRight.vue'
|
||||
|
||||
<style>
|
||||
.links-wrapper {
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ArrowLeft,
|
||||
ArrowRight
|
||||
},
|
||||
|
||||
setup () {
|
||||
const { hasLinks, prev, next } = useNextAndPrevLinks()
|
||||
|
||||
return {
|
||||
hasLinks,
|
||||
prev,
|
||||
next
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.next-and-prev-link {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 4rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.links-wrapper a {
|
||||
.prev,
|
||||
.next {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.prev {
|
||||
justify-content: flex-start;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.next {
|
||||
justify-content: flex-end;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.links-wrapper a:hover {
|
||||
text-decoration: none !important;
|
||||
.text {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
fill: var(--text-color);
|
||||
}
|
||||
|
||||
.icon-prev { margin-right: 8px; }
|
||||
.icon-next { margin-left: 8px; }
|
||||
</style>
|
||||
|
@ -1,42 +1,39 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="page">
|
||||
<slot name="top" />
|
||||
<Content />
|
||||
|
||||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
|
||||
<EditLink />
|
||||
<NextAndPrevLinks />
|
||||
<PageEdit />
|
||||
|
||||
<slot name="bottom" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import EditLink from './EditLink.vue'
|
||||
import NextAndPrevLinks from './NextAndPrevLinks.vue'
|
||||
import PageEdit from './PageEdit.vue'
|
||||
export default {
|
||||
components: { NextAndPrevLinks, PageEdit }
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
EditLink,
|
||||
NextAndPrevLinks
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.content {
|
||||
<style scoped>
|
||||
.page {
|
||||
margin: 0 auto;
|
||||
padding: 0.025rem 2.5rem 2rem;
|
||||
/* if this is moved to a variable, add it to BuySellAds.vue */
|
||||
padding: 0 1.5rem 4rem;
|
||||
max-width: 50rem;
|
||||
}
|
||||
|
||||
.content a {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.content img {
|
||||
max-width: 100%;
|
||||
.content {
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
/*
|
||||
.content div > h1:first-child, .content div > h2:first-child {
|
||||
margin-top: 0;
|
||||
} */
|
||||
</style>
|
||||
|
@ -1,83 +0,0 @@
|
||||
import { computed } from 'vue'
|
||||
import OutboundLink from './icons/OutboundLink.vue'
|
||||
import { endingSlashRE, isExternal } from '../utils'
|
||||
import { useRoute, useSiteData } from 'vitepress'
|
||||
import { DefaultTheme } from '../config'
|
||||
|
||||
function createEditLink(
|
||||
repo: string,
|
||||
docsRepo: string,
|
||||
docsDir: string,
|
||||
docsBranch: string,
|
||||
path: string
|
||||
) {
|
||||
const bitbucket = /bitbucket.org/
|
||||
if (bitbucket.test(repo)) {
|
||||
const base = isExternal(docsRepo) ? docsRepo : repo
|
||||
return (
|
||||
base.replace(endingSlashRE, '') +
|
||||
`/src` +
|
||||
`/${docsBranch}/` +
|
||||
(docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') +
|
||||
path +
|
||||
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
|
||||
)
|
||||
}
|
||||
|
||||
const base = isExternal(docsRepo)
|
||||
? docsRepo
|
||||
: `https://github.com/${docsRepo}`
|
||||
return (
|
||||
base.replace(endingSlashRE, '') +
|
||||
`/edit` +
|
||||
`/${docsBranch}/` +
|
||||
(docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') +
|
||||
path
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
OutboundLink
|
||||
},
|
||||
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const siteData = useSiteData<DefaultTheme.Config>()
|
||||
|
||||
const editLink = computed(() => {
|
||||
const pageData = route.data
|
||||
const showEditLink: boolean | undefined =
|
||||
pageData.frontmatter.editLink == null
|
||||
? siteData.value.themeConfig.editLinks
|
||||
: pageData.frontmatter.editLink
|
||||
const {
|
||||
repo,
|
||||
docsDir = '',
|
||||
docsBranch = 'master',
|
||||
docsRepo = repo
|
||||
} = siteData.value.themeConfig
|
||||
|
||||
const { relativePath } = pageData
|
||||
if (showEditLink && relativePath && repo) {
|
||||
return createEditLink(
|
||||
repo,
|
||||
docsRepo || repo,
|
||||
docsDir,
|
||||
docsBranch,
|
||||
relativePath
|
||||
)
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
const editLinkText = computed(
|
||||
() => siteData.value.themeConfig.editLinkText || 'Edit this page'
|
||||
)
|
||||
|
||||
return {
|
||||
editLink,
|
||||
editLinkText
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<footer class="page-edit">
|
||||
<a
|
||||
v-if="editLink"
|
||||
:href="editLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{{ editLinkText }}
|
||||
<OutboundLink />
|
||||
</a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script src="./PageEdit"></script>
|
||||
|
||||
<style>
|
||||
.page-edit {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.page-edit a {
|
||||
color: var(--text-color);
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,5 @@
|
||||
<template functional>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z" />
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,5 @@
|
||||
<template functional>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z" />
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,91 @@
|
||||
import { computed } from 'vue'
|
||||
import { useSiteDataByRoute, usePageData } from 'vitepress'
|
||||
import { endingSlashRE, isNullish, isExternal } from '../utils'
|
||||
|
||||
const bitbucketRE = /bitbucket.org/
|
||||
|
||||
export function useEditLink() {
|
||||
const site = useSiteDataByRoute()
|
||||
const page = usePageData()
|
||||
|
||||
const url = computed(() => {
|
||||
const showEditLink = isNullish(page.value.frontmatter.editLink)
|
||||
? site.value.themeConfig.editLinks
|
||||
: page.value.frontmatter.editLink
|
||||
|
||||
const {
|
||||
repo,
|
||||
docsDir = '',
|
||||
docsBranch = 'master',
|
||||
docsRepo = repo
|
||||
} = site.value.themeConfig
|
||||
|
||||
const { relativePath } = page.value
|
||||
|
||||
if (!showEditLink || !relativePath || !repo) {
|
||||
return null
|
||||
}
|
||||
|
||||
return createUrl(repo, docsRepo, docsDir, docsBranch, relativePath)
|
||||
})
|
||||
|
||||
const text = computed(() => {
|
||||
return site.value.themeConfig.editLinkText || 'Edit this page'
|
||||
})
|
||||
|
||||
return {
|
||||
url,
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
function createUrl(
|
||||
repo: string,
|
||||
docsRepo: string,
|
||||
docsDir: string,
|
||||
docsBranch: string,
|
||||
path: string
|
||||
): string {
|
||||
return bitbucketRE.test(repo)
|
||||
? createBitbucketUrl(repo, docsRepo, docsDir, docsBranch, path)
|
||||
: createGitHubUrl(repo, docsRepo, docsDir, docsBranch, path)
|
||||
}
|
||||
|
||||
function createGitHubUrl(
|
||||
repo: string,
|
||||
docsRepo: string,
|
||||
docsDir: string,
|
||||
docsBranch: string,
|
||||
path: string
|
||||
): string {
|
||||
const base = isExternal(docsRepo)
|
||||
? docsRepo
|
||||
: `https://github.com/${docsRepo}`
|
||||
|
||||
return (
|
||||
base.replace(endingSlashRE, '') +
|
||||
`/edit` +
|
||||
`/${docsBranch}/` +
|
||||
(docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') +
|
||||
path
|
||||
)
|
||||
}
|
||||
|
||||
function createBitbucketUrl(
|
||||
repo: string,
|
||||
docsRepo: string,
|
||||
docsDir: string,
|
||||
docsBranch: string,
|
||||
path: string
|
||||
): string {
|
||||
const base = isExternal(docsRepo) ? docsRepo : repo
|
||||
|
||||
return (
|
||||
base.replace(endingSlashRE, '') +
|
||||
`/src` +
|
||||
`/${docsBranch}/` +
|
||||
(docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '') +
|
||||
path +
|
||||
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
|
||||
)
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useSiteData } from 'vitepress'
|
||||
import { DefaultTheme } from '../config'
|
||||
|
||||
export function useNextAndPrevLinks() {
|
||||
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.value || !!prev.value
|
||||
})
|
||||
|
||||
return {
|
||||
next,
|
||||
prev,
|
||||
hasLinks
|
||||
}
|
||||
}
|
Loading…
Reference in new issue