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>
|
<template>
|
||||||
<div v-if="hasLinks" class="links-wrapper">
|
<div v-if="hasLinks" class="next-and-prev-link">
|
||||||
<div class="prev-link">
|
<div class="prev">
|
||||||
<div v-if="prev">
|
<a v-if="prev" class="link" :href="prev.link">
|
||||||
← <a :href="prev.link">{{ prev.text }}</a>
|
<ArrowLeft class="icon icon-prev" />
|
||||||
</div>
|
<span class="text">{{ prev.text }}</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="next-link">
|
<div class="next">
|
||||||
<div v-if="next">
|
<a v-if="next" class="link" :href="next.link">
|
||||||
<a :href="next.link">{{ next.text }}</a> →
|
<span class="text">{{ next.text }}</span>
|
||||||
</div>
|
<ArrowRight class="icon icon-next" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
export default defineComponent({
|
||||||
.links-wrapper {
|
components: {
|
||||||
|
ArrowLeft,
|
||||||
|
ArrowRight
|
||||||
|
},
|
||||||
|
|
||||||
|
setup () {
|
||||||
|
const { hasLinks, prev, next } = useNextAndPrevLinks()
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasLinks,
|
||||||
|
prev,
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.next-and-prev-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 4rem;
|
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
padding-top: 1rem;
|
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;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.links-wrapper a:hover {
|
.text {
|
||||||
text-decoration: none !important;
|
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>
|
</style>
|
||||||
|
@ -1,42 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="content">
|
<div class="page">
|
||||||
<slot name="top" />
|
<slot name="top" />
|
||||||
<Content />
|
|
||||||
|
<div class="content">
|
||||||
|
<Content />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditLink />
|
||||||
<NextAndPrevLinks />
|
<NextAndPrevLinks />
|
||||||
<PageEdit />
|
|
||||||
<slot name="bottom" />
|
<slot name="bottom" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import EditLink from './EditLink.vue'
|
||||||
import NextAndPrevLinks from './NextAndPrevLinks.vue'
|
import NextAndPrevLinks from './NextAndPrevLinks.vue'
|
||||||
import PageEdit from './PageEdit.vue'
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
components: { NextAndPrevLinks, PageEdit }
|
components: {
|
||||||
}
|
EditLink,
|
||||||
|
NextAndPrevLinks
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
.content {
|
.page {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0.025rem 2.5rem 2rem;
|
padding: 0 1.5rem 4rem;
|
||||||
/* if this is moved to a variable, add it to BuySellAds.vue */
|
|
||||||
max-width: 50rem;
|
max-width: 50rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content a {
|
.content {
|
||||||
color: var(--accent-color);
|
padding-bottom: 1.5rem;
|
||||||
}
|
|
||||||
|
|
||||||
.content a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
.content div > h1:first-child, .content div > h2:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
} */
|
|
||||||
</style>
|
</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