You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vitepress/lib/app/composables/router.js

142 lines
3.1 KiB

import { shallowReactive, provide, inject, nextTick } from 'vue'
import Theme from '/@theme/index'
const NotFound = Theme.NotFound || (() => '404 Not Found')
/**
* @typedef {{
* path: string
* contentComponent: import('vue').Component | null
* pageData: { path: string } | null
* }} Route
*/
/**
* @type {import('vue').InjectionKey<Route>}
*/
const RouteSymbol = Symbol()
/**
* @returns {Route}
*/
const getDefaultRoute = () => ({
path: location.pathname,
contentComponent: null,
pageData: null
})
export function useRouter() {
const loc = location
const route = shallowReactive(getDefaultRoute())
window.addEventListener(
'click',
/**
* @param {*} e
*/
(e) => {
if (e.target.tagName === 'A') {
const { href, target } = e.target
const url = new URL(href)
if (
target !== `_blank` &&
url.protocol === loc.protocol &&
url.hostname === loc.hostname
) {
if (url.pathname === loc.pathname) {
// smooth scroll bewteen hash anchors in the same page
if (url.hash !== loc.hash) {
e.preventDefault()
window.scrollTo({
left: 0,
top: e.target.offsetTop,
behavior: 'smooth'
})
}
} else {
e.preventDefault()
// save scroll position before changing url
saveScrollPosition()
history.pushState(null, '', href)
loadPage(route)
}
}
}
},
{ capture: true }
)
window.addEventListener(
'popstate',
/**
* @param {*} e
*/
(e) => {
loadPage(route, (e.state && e.state.scrollPosition) || 0)
}
)
provide(RouteSymbol, route)
loadPage(route)
}
export function useRoute() {
return inject(RouteSymbol) || getDefaultRoute()
}
/**
* @param {Route} route
* @param {number} scrollPosition
*/
function loadPage(route, scrollPosition = 0) {
const pendingPath = (route.path = location.pathname)
let pagePath = pendingPath.replace(/\.html$/, '')
if (pagePath.endsWith('/')) {
pagePath += 'index'
}
// awlays force re-fetch content in dev
import(`${pagePath}.md?t=${Date.now()}`)
.then(async (m) => {
if (route.path === pendingPath) {
route.contentComponent = m.default
route.pageData = m.__pageData
await nextTick()
5 years ago
if (location.hash && !scrollPosition) {
/**
* @type {HTMLElement | null}
*/
const target = document.querySelector(location.hash)
if (target) {
scrollPosition = target.offsetTop
}
}
window.scrollTo({
left: 0,
top: scrollPosition,
behavior: 'auto'
})
}
})
.catch((err) => {
if (!err.message.match(/fetch/)) {
throw err
} else if (route.path === pendingPath) {
route.contentComponent = NotFound
}
})
}
function saveScrollPosition() {
history.replaceState(
{
scrollPosition: window.scrollY
},
document.title,
''
)
}