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} */ 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() 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, '' ) }