Files
vuepress/lib/default-theme/Layout.vue
T

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>