refactor: split SideBarItem into separate file

Also fix page loading
pull/131/head
Evan You 4 years ago
parent a6bfdd41fa
commit 061ad3ff32

@ -14,27 +14,27 @@ export function createApp() {
let isInitialPageLoad = inBrowser
let initialPath: string
const router = createRouter((route) => {
let pagePath = pathToFile(route.path)
const router = createRouter((path) => {
let pageFilePath = pathToFile(path)
if (isInitialPageLoad) {
initialPath = pagePath
initialPath = pageFilePath
}
// use lean build if this is the initial page load or navigating back
// to the initial loaded path (the static vnodes already adopted the
// static content on that load so no need to re-fetch the page)
if (isInitialPageLoad || initialPath === pagePath) {
pagePath = pagePath.replace(/\.js$/, '.lean.js')
if (isInitialPageLoad || initialPath === pageFilePath) {
pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js')
}
if (inBrowser) {
isInitialPageLoad = false
// in browser: native dynamic import
return import(/*@vite-ignore*/ pagePath)
return import(/*@vite-ignore*/ pageFilePath)
} else {
// SSR, sync require
return require(pagePath)
return require(pageFilePath)
}
}, NotFound)

@ -34,7 +34,7 @@ interface PageModule {
}
export function createRouter(
loadPageModule: (route: Route) => PageModule | Promise<PageModule>,
loadPageModule: (path: string) => PageModule | Promise<PageModule>,
fallbackComponent?: Component
): Router {
const route = reactive(getDefaultRoute())
@ -61,7 +61,7 @@ export function createRouter(
const targetLoc = new URL(href, fakeHost)
const pendingPath = (latestPendingPath = targetLoc.pathname)
try {
let page = loadPageModule(route)
let page = loadPageModule(pendingPath)
// only await if it returns a Promise - this allows sync resolution
// on initial render in SSR.
if ('then' in page && typeof page.then === 'function') {

@ -1,31 +1,21 @@
import { useRoute, useSiteDataByRoute, useSiteData } from 'vitepress'
import { computed, h, FunctionalComponent, VNode } from 'vue'
import { useRoute, useSiteDataByRoute } from 'vitepress'
import { computed } from 'vue'
import { Header } from '../../../../types/shared'
import { isActive, joinUrl, getPathDirName } from '../utils'
import { getPathDirName } from '../utils'
import { DefaultTheme } from '../config'
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
import NavBarLinks from './NavBarLinks.vue'
import { SideBarItem } from './SideBarItem'
const SideBarItem: FunctionalComponent<{
item: ResolvedSidebarItem
}> = (props) => {
const {
item: { link: relLink, text, children }
} = props
const route = useRoute()
const siteData = useSiteData()
const link = resolveLink(siteData.value.base, relLink || '')
const active = isActive(route, link)
const headers = route.data.headers
return h('li', { class: 'sidebar-item' }, [
createLink(active, text, link),
createChildren(active, children, headers)
])
export interface ResolvedSidebarItem {
text: string
link?: string
isGroup?: boolean
children?: ResolvedSidebarItem[]
}
type ResolvedSidebar = ResolvedSidebarItem[]
export default {
components: {
NavBarLinks,
@ -77,19 +67,6 @@ export default {
}
}
interface HeaderWithChildren extends Header {
children?: Header[]
}
type ResolvedSidebar = ResolvedSidebarItem[]
interface ResolvedSidebarItem {
text: string
link?: string
isGroup?: boolean
children?: ResolvedSidebarItem[]
}
function resolveAutoSidebar(headers: Header[], depth: number): ResolvedSidebar {
const ret: ResolvedSidebar = []
@ -144,68 +121,3 @@ function resolveMultiSidebar(
return []
}
function resolveLink(base: string, path: string): string | undefined {
return path
? // keep relative hash to the same page
path.startsWith('#')
? path
: joinUrl(base, path)
: undefined
}
function createLink(active: boolean, text: string, link?: string): VNode {
const tag = link ? 'a' : 'p'
const component = {
class: { 'sidebar-link': true, active },
href: link
}
return h(tag, component, text)
}
function createChildren(
active: boolean,
children?: ResolvedSidebarItem[],
headers?: Header[]
): VNode | null {
if (children && children.length > 0) {
return h(
'ul',
{ class: 'sidebar-items' },
children.map((c) => {
return h(SideBarItem, { item: c })
})
)
}
return active && headers
? createChildren(false, resolveHeaders(headers))
: null
}
function resolveHeaders(headers: Header[]): ResolvedSidebarItem[] {
return mapHeaders(groupHeaders(headers))
}
function groupHeaders(headers: Header[]): HeaderWithChildren[] {
headers = headers.map((h) => Object.assign({}, h))
let lastH2: HeaderWithChildren
headers.forEach((h) => {
if (h.level === 2) {
lastH2 = h
} else if (lastH2) {
;(lastH2.children || (lastH2.children = [])).push(h)
}
})
return headers.filter((h) => h.level === 2)
}
function mapHeaders(headers: HeaderWithChildren[]): ResolvedSidebarItem[] {
return headers.map((header) => ({
text: header.title,
link: `#${header.slug}`,
children: header.children ? mapHeaders(header.children) : undefined
}))
}

@ -0,0 +1,91 @@
import { useRoute, useSiteData } from 'vitepress'
import { FunctionalComponent, h, VNode } from 'vue'
import { Header } from '../../../../types/shared'
import { joinUrl, isActive } from '../utils'
import { ResolvedSidebarItem } from './SideBar'
interface HeaderWithChildren extends Header {
children?: Header[]
}
export const SideBarItem: FunctionalComponent<{
item: ResolvedSidebarItem
}> = (props) => {
const {
item: { link: relLink, text, children }
} = props
const route = useRoute()
const siteData = useSiteData()
const link = resolveLink(siteData.value.base, relLink || '')
const active = isActive(route, link)
const headers = route.data.headers
const childItems = createChildren(active, children, headers)
return h('li', { class: 'sidebar-item' }, [
h(
link ? 'a' : 'p',
{
class: { 'sidebar-link': true, active },
href: link
},
text
),
childItems
])
}
function resolveLink(base: string, path: string): string | undefined {
return path
? // keep relative hash to the same page
path.startsWith('#')
? path
: joinUrl(base, path)
: undefined
}
function createChildren(
active: boolean,
children?: ResolvedSidebarItem[],
headers?: Header[]
): VNode | null {
if (children && children.length > 0) {
return h(
'ul',
{ class: 'sidebar-items' },
children.map((c) => {
return h(SideBarItem, { item: c })
})
)
}
return active && headers
? createChildren(false, resolveHeaders(headers))
: null
}
function resolveHeaders(headers: Header[]): ResolvedSidebarItem[] {
return mapHeaders(groupHeaders(headers))
}
function groupHeaders(headers: Header[]): HeaderWithChildren[] {
headers = headers.map((h) => Object.assign({}, h))
let lastH2: HeaderWithChildren
headers.forEach((h) => {
if (h.level === 2) {
lastH2 = h
} else if (lastH2) {
;(lastH2.children || (lastH2.children = [])).push(h)
}
})
return headers.filter((h) => h.level === 2)
}
function mapHeaders(headers: HeaderWithChildren[]): ResolvedSidebarItem[] {
return headers.map((header) => ({
text: header.title,
link: `#${header.slug}`,
children: header.children ? mapHeaders(header.children) : undefined
}))
}

@ -1,7 +1,7 @@
import { useSiteData, Route } from 'vitepress'
export const hashRE = /#.*$/
export const extRE = /\.(md|html)$/
export const extRE = /(index)?\.(md|html)$/
export const endingSlashRE = /\/$/
export const outboundRE = /^[a-z]+:/i

Loading…
Cancel
Save