mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
314 lines
7.3 KiB
Vue
314 lines
7.3 KiB
Vue
<script setup lang="ts">
|
|
import VPComment from '@theme/VPComment.vue'
|
|
import VPDocAside from '@theme/VPDocAside.vue'
|
|
import VPDocBreadcrumbs from '@theme/VPDocBreadcrumbs.vue'
|
|
import VPDocCopyright from '@theme/VPDocCopyright.vue'
|
|
import VPDocFooter from '@theme/VPDocFooter.vue'
|
|
import VPDocMeta from '@theme/VPDocMeta.vue'
|
|
import VPEncryptPage from '@theme/VPEncryptPage.vue'
|
|
import VPTransitionFadeSlideY from '@theme/VPTransitionFadeSlideY.vue'
|
|
import { computed, nextTick, ref, watch } from 'vue'
|
|
import { useRoute } from 'vuepress/client'
|
|
import {
|
|
useContributors,
|
|
useData,
|
|
useEncrypt,
|
|
useHeaders,
|
|
usePostsPageData,
|
|
useSidebar,
|
|
} from '../composables/index.js'
|
|
|
|
const { page, theme, frontmatter } = useData()
|
|
const route = useRoute()
|
|
|
|
const { hasSidebar, hasAside, leftAside } = useSidebar()
|
|
const { isPosts } = usePostsPageData()
|
|
const headers = useHeaders()
|
|
const { isPageDecrypted } = useEncrypt()
|
|
const { mode: contributorsMode } = useContributors()
|
|
|
|
const enableAside = computed(() => {
|
|
if (!hasAside.value)
|
|
return false
|
|
|
|
if (isPosts.value)
|
|
return headers.value.length > 0
|
|
|
|
return true
|
|
})
|
|
|
|
const pageName = computed(() =>
|
|
route.path.replace(/[./]+/g, '_').replace(/_html$/, ''),
|
|
)
|
|
const enabledExternalLinkIcon = computed(
|
|
() =>
|
|
theme.value.externalLinkIcon
|
|
&& frontmatter.value.externalLinkIcon !== false,
|
|
)
|
|
|
|
const asideEl = ref<HTMLElement>()
|
|
watch(
|
|
() => route.hash,
|
|
hash =>
|
|
nextTick(() => {
|
|
if (!asideEl.value)
|
|
return
|
|
const activeItem = asideEl.value.querySelector(
|
|
`.outline-link[href="${hash}"]`,
|
|
)
|
|
if (!activeItem || !hash) {
|
|
asideEl.value.scrollTop = 0
|
|
return
|
|
}
|
|
|
|
const { top: navTop, height: navHeight }
|
|
= asideEl.value.getBoundingClientRect()
|
|
const { top: activeTop, height: activeHeight }
|
|
= activeItem.getBoundingClientRect()
|
|
|
|
if (activeTop < navTop || activeTop + activeHeight > navTop + navHeight)
|
|
activeItem.scrollIntoView({ block: 'center' })
|
|
}),
|
|
{ immediate: true },
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="vp-doc-container" :class="{
|
|
'has-sidebar': hasSidebar,
|
|
'has-aside': enableAside,
|
|
'is-posts': isPosts,
|
|
'with-encrypt': !isPageDecrypted,
|
|
}"
|
|
>
|
|
<slot name="doc-top" />
|
|
<div class="container">
|
|
<div v-if="enableAside" class="aside" :class="{ 'left-aside': leftAside }" vp-outline>
|
|
<div class="aside-curtain" />
|
|
<VPTransitionFadeSlideY>
|
|
<div ref="asideEl" :key="page.path" class="aside-container">
|
|
<div class="aside-content">
|
|
<VPDocAside>
|
|
<template #aside-top>
|
|
<slot name="aside-top" />
|
|
</template>
|
|
<template #aside-bottom>
|
|
<slot name="aside-bottom" />
|
|
</template>
|
|
<template #aside-outline-before>
|
|
<slot name="aside-outline-before" />
|
|
</template>
|
|
<template #aside-outline-after>
|
|
<slot name="aside-outline-after" />
|
|
</template>
|
|
<template #aside-ads-before>
|
|
<slot name="aside-ads-before" />
|
|
</template>
|
|
<template #aside-ads-after>
|
|
<slot name="aside-ads-after" />
|
|
</template>
|
|
</VPDocAside>
|
|
</div>
|
|
</div>
|
|
</VPTransitionFadeSlideY>
|
|
</div>
|
|
<VPTransitionFadeSlideY>
|
|
<div :key="page.path" class="content">
|
|
<div class="content-container">
|
|
<slot name="doc-before" />
|
|
<main class="main">
|
|
<VPDocBreadcrumbs />
|
|
|
|
<slot name="doc-meta-top" />
|
|
<VPDocMeta>
|
|
<template #doc-meta-before>
|
|
<slot name="doc-meta-before" />
|
|
</template>
|
|
<template #doc-meta-after>
|
|
<slot name="doc-meta-after" />
|
|
</template>
|
|
</VPDocMeta>
|
|
<slot name="doc-meta-bottom" />
|
|
|
|
<VPEncryptPage v-if="!isPageDecrypted" />
|
|
<div
|
|
v-else class="vp-doc plume-content"
|
|
:class="[pageName, enabledExternalLinkIcon && 'external-link-icon-enabled']" vp-content
|
|
>
|
|
<slot name="doc-content-before" />
|
|
|
|
<Content />
|
|
|
|
<DocGitContributors v-if="contributorsMode === 'block'" />
|
|
<DocGitChangelog />
|
|
<VPDocCopyright />
|
|
</div>
|
|
</main>
|
|
<VPDocFooter v-if="isPageDecrypted">
|
|
<template #doc-footer-before>
|
|
<slot name="doc-footer-before" />
|
|
</template>
|
|
</VPDocFooter>
|
|
|
|
<VPComment />
|
|
|
|
<slot name="doc-after" />
|
|
</div>
|
|
</div>
|
|
</VPTransitionFadeSlideY>
|
|
</div>
|
|
<slot name="doc-bottom" />
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.vp-doc-container {
|
|
width: 100%;
|
|
padding: 32px 24px 96px;
|
|
}
|
|
|
|
.vp-doc-container.with-encrypt {
|
|
padding: 32px 24px;
|
|
}
|
|
|
|
.container {
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.aside {
|
|
position: relative;
|
|
|
|
display: none;
|
|
flex-grow: 1;
|
|
order: 2;
|
|
|
|
width: 100%;
|
|
max-width: 256px;
|
|
padding-left: 32px;
|
|
}
|
|
|
|
.left-aside {
|
|
order: 1;
|
|
padding-right: 32px;
|
|
padding-left: unset;
|
|
}
|
|
|
|
.aside-container {
|
|
position: fixed;
|
|
top: 0;
|
|
width: 224px;
|
|
height: 100vh;
|
|
padding-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 44px);
|
|
padding-bottom: var(--vp-footer-height, 0);
|
|
overflow: hidden auto;
|
|
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.aside-container::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.aside-curtain {
|
|
--vp-aside-curtain-bg: var(--vp-c-bg);
|
|
|
|
position: fixed;
|
|
bottom: 0;
|
|
z-index: 10;
|
|
width: 224px;
|
|
height: 32px;
|
|
background: linear-gradient(transparent, var(--vp-aside-curtain-bg) 70%);
|
|
transition: --vp-aside-curtain-bg var(--vp-t-color);
|
|
}
|
|
|
|
.aside-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-footer-height, 0px) + 48px));
|
|
padding-bottom: 32px;
|
|
}
|
|
|
|
.content {
|
|
position: relative;
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.content-container {
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.vp-doc-container.has-aside .content-container {
|
|
max-width: 788px;
|
|
}
|
|
|
|
@media (min-width: 768px) {
|
|
.vp-doc-container {
|
|
padding: 48px 32px 128px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 960px) {
|
|
.vp-doc-container {
|
|
padding: 48px 32px 0;
|
|
}
|
|
|
|
.vp-doc-container:not(.has-sidebar) .container {
|
|
display: flex;
|
|
justify-content: center;
|
|
max-width: 992px;
|
|
}
|
|
|
|
.vp-doc-container:not(.has-sidebar) .content {
|
|
max-width: 752px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1280px) {
|
|
.vp-doc-container .aside {
|
|
display: block;
|
|
}
|
|
|
|
.vp-doc-container .container {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1440px) {
|
|
.vp-doc-container:not(.has-sidebar) .content {
|
|
max-width: 884px;
|
|
}
|
|
|
|
.vp-doc-container.is-posts:not(.has-sidebar.has-aside) .content {
|
|
max-width: 985px;
|
|
}
|
|
|
|
.vp-doc-container:not(.has-sidebar) .container {
|
|
max-width: 1104px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 960px) {
|
|
.content {
|
|
padding: 0 32px 88px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1280px) {
|
|
.content {
|
|
order: 1;
|
|
min-width: 640px;
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
@property --vp-aside-curtain-bg {
|
|
inherits: false;
|
|
initial-value: #fff;
|
|
syntax: "<color>";
|
|
}
|
|
</style>
|