mirror of
https://github.com/wahyd4/vuepress.git
synced 2026-08-15 08:15:54 +10:00
171 lines
4.8 KiB
Vue
171 lines
4.8 KiB
Vue
<template>
|
|
<div class="theme-container"
|
|
:class="pageClasses"
|
|
@touchstart="onTouchStart"
|
|
@touchend="onTouchEnd">
|
|
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar"/>
|
|
<div class="sidebar-mask" @click="toggleSidebar(false)"></div>
|
|
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
|
|
<slot name="sidebar-top" slot="top"/>
|
|
<slot name="sidebar-bottom" slot="bottom"/>
|
|
</Sidebar>
|
|
<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 :sidebar-items="sidebarItems">
|
|
<slot name="page-top" slot="top"/>
|
|
<slot name="page-bottom" slot="bottom"/>
|
|
</Page>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Vue from 'vue'
|
|
import nprogress from 'nprogress'
|
|
import Home from './Home.vue'
|
|
import Navbar from './Navbar.vue'
|
|
import Page from './Page.vue'
|
|
import Sidebar from './Sidebar.vue'
|
|
import { pathToComponentName } from '@app/util'
|
|
import store from '@app/store'
|
|
import { resolveSidebarItems } from './util'
|
|
import throttle from 'lodash.throttle'
|
|
|
|
export default {
|
|
components: { Home, Page, Sidebar, Navbar },
|
|
data () {
|
|
return {
|
|
isSidebarOpen: false
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
shouldShowNavbar () {
|
|
const { themeConfig } = this.$site
|
|
const { frontmatter } = this.$page
|
|
if (
|
|
frontmatter.navbar === false ||
|
|
themeConfig.navbar === false) {
|
|
return false
|
|
}
|
|
return (
|
|
this.$title ||
|
|
themeConfig.logo ||
|
|
themeConfig.repo ||
|
|
themeConfig.nav ||
|
|
this.$themeLocaleConfig.nav
|
|
)
|
|
},
|
|
shouldShowSidebar () {
|
|
const { frontmatter } = this.$page
|
|
return (
|
|
!frontmatter.layout &&
|
|
!frontmatter.home &&
|
|
frontmatter.sidebar !== false &&
|
|
this.sidebarItems.length
|
|
)
|
|
},
|
|
sidebarItems () {
|
|
return resolveSidebarItems(
|
|
this.$page,
|
|
this.$route,
|
|
this.$site,
|
|
this.$localePath
|
|
)
|
|
},
|
|
pageClasses () {
|
|
const userPageClass = this.$page.frontmatter.pageClass
|
|
return [
|
|
{
|
|
'no-navbar': !this.shouldShowNavbar,
|
|
'sidebar-open': this.isSidebarOpen,
|
|
'no-sidebar': !this.shouldShowSidebar
|
|
},
|
|
userPageClass
|
|
]
|
|
}
|
|
},
|
|
|
|
mounted () {
|
|
window.addEventListener('scroll', this.onScroll)
|
|
|
|
// configure progress bar
|
|
nprogress.configure({ showSpinner: false })
|
|
|
|
this.$router.beforeEach((to, from, next) => {
|
|
if (to.path !== from.path && !Vue.component(pathToComponentName(to.path))) {
|
|
nprogress.start()
|
|
}
|
|
next()
|
|
})
|
|
|
|
this.$router.afterEach(() => {
|
|
nprogress.done()
|
|
this.isSidebarOpen = false
|
|
})
|
|
},
|
|
|
|
beforeDestroy () {
|
|
window.removeEventListener('scroll', this.onScroll)
|
|
},
|
|
|
|
methods: {
|
|
toggleSidebar (to) {
|
|
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
|
|
},
|
|
// side swipe
|
|
onTouchStart (e) {
|
|
this.touchStart = {
|
|
x: e.changedTouches[0].clientX,
|
|
y: e.changedTouches[0].clientY
|
|
}
|
|
},
|
|
onTouchEnd (e) {
|
|
const dx = e.changedTouches[0].clientX - this.touchStart.x
|
|
const dy = e.changedTouches[0].clientY - this.touchStart.y
|
|
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
|
|
if (dx > 0 && this.touchStart.x <= 80) {
|
|
this.toggleSidebar(true)
|
|
} else {
|
|
this.toggleSidebar(false)
|
|
}
|
|
}
|
|
},
|
|
onScroll: throttle(function () {
|
|
this.setActiveHash()
|
|
}, 300),
|
|
setActiveHash () {
|
|
const sidebarLinks = [].slice.call(document.querySelectorAll('.sidebar-link'))
|
|
const anchors = [].slice.call(document.querySelectorAll('.header-anchor'))
|
|
.filter(anchor => sidebarLinks.some(sidebarLink => sidebarLink.hash === anchor.hash))
|
|
|
|
const scrollTop = Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop)
|
|
|
|
for (let i = 0; i < anchors.length; i++) {
|
|
const anchor = anchors[i]
|
|
const nextAnchor = anchors[i + 1]
|
|
|
|
const isActive = i === 0 && scrollTop === 0 ||
|
|
(scrollTop >= anchor.parentElement.offsetTop + 10 &&
|
|
(!nextAnchor || scrollTop < nextAnchor.parentElement.offsetTop - 10))
|
|
|
|
if (isActive && decodeURIComponent(this.$route.hash) !== decodeURIComponent(anchor.hash)) {
|
|
store.disableScrollBehavior = true
|
|
this.$router.replace(decodeURIComponent(anchor.hash), () => {
|
|
// execute after scrollBehavior handler.
|
|
this.$nextTick(() => {
|
|
store.disableScrollBehavior = false
|
|
})
|
|
})
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
|
<style src="./styles/theme.styl" lang="stylus"></style>
|