SideBarLink.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { FunctionalComponent, h, VNode } from 'vue'
  2. import { useRoute, useSiteData } from 'vitepress'
  3. import { Header } from '/@types/shared'
  4. import { DefaultTheme } from '../config'
  5. import { joinUrl, isActive } from '../utils'
  6. interface HeaderWithChildren extends Header {
  7. children?: Header[]
  8. }
  9. export const SideBarLink: FunctionalComponent<{
  10. item: DefaultTheme.SideBarItem
  11. }> = (props) => {
  12. const route = useRoute()
  13. const site = useSiteData()
  14. const headers = route.data.headers
  15. const text = props.item.text
  16. const link = resolveLink(site.value.base, props.item.link)
  17. const children = (props.item as DefaultTheme.SideBarGroup).children
  18. const active = isActive(route, props.item.link)
  19. const childItems = createChildren(active, children, headers)
  20. return h('li', { class: 'sidebar-link' }, [
  21. h(
  22. link ? 'a' : 'p',
  23. {
  24. class: { 'sidebar-link-item': true, active },
  25. href: link
  26. },
  27. text
  28. ),
  29. childItems
  30. ])
  31. }
  32. function resolveLink(base: string, path?: string): string | undefined {
  33. if (path === undefined) {
  34. return path
  35. }
  36. // keep relative hash to the same page
  37. if (path.startsWith('#')) {
  38. return path
  39. }
  40. return joinUrl(base, path)
  41. }
  42. function createChildren(
  43. active: boolean,
  44. children?: DefaultTheme.SideBarItem[],
  45. headers?: Header[]
  46. ): VNode | null {
  47. if (children && children.length > 0) {
  48. return h(
  49. 'ul',
  50. { class: 'sidebar-links' },
  51. children.map((c) => {
  52. return h(SideBarLink, { item: c })
  53. })
  54. )
  55. }
  56. return active && headers
  57. ? createChildren(false, resolveHeaders(headers))
  58. : null
  59. }
  60. function resolveHeaders(headers: Header[]): DefaultTheme.SideBarItem[] {
  61. return mapHeaders(groupHeaders(headers))
  62. }
  63. function groupHeaders(headers: Header[]): HeaderWithChildren[] {
  64. headers = headers.map((h) => Object.assign({}, h))
  65. let lastH2: HeaderWithChildren
  66. headers.forEach((h) => {
  67. if (h.level === 2) {
  68. lastH2 = h
  69. } else if (lastH2) {
  70. ;(lastH2.children || (lastH2.children = [])).push(h)
  71. }
  72. })
  73. return headers.filter((h) => h.level === 2)
  74. }
  75. function mapHeaders(headers: HeaderWithChildren[]): DefaultTheme.SideBarItem[] {
  76. return headers.map((header) => ({
  77. text: header.title,
  78. link: `#${header.slug}`,
  79. children: header.children ? mapHeaders(header.children) : undefined
  80. }))
  81. }