usePage.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { slugifyWithCounter } from '@sindresorhus/slugify'
  2. import { useRouter } from 'next/router'
  3. import { products } from '@/components/products'
  4. export function usePage(pageProps) {
  5. const { pathname } = useRouter()
  6. const title = pageProps.markdoc?.frontmatter.title ?? 'Metaplex Documentation'
  7. const product = getActiveProduct(pathname, pageProps)
  8. const activeSection = getActiveSection(pathname, product, pageProps)
  9. const activeHero = getActiveHero(pathname, product, pageProps)
  10. return {
  11. title,
  12. metaTitle: pageProps.markdoc?.frontmatter.metaTitle ?? title,
  13. pathname,
  14. product,
  15. activeHero,
  16. activeSection,
  17. isIndexPage: product?.path ? pathname === `/${product.path}` : false,
  18. tableOfContents: pageProps.markdoc?.content
  19. ? parseTableOfContents(pageProps.markdoc.content)
  20. : [],
  21. }
  22. }
  23. function getActiveProduct(pathname, pageProps) {
  24. const pathnameFirstSegment = pathname.replace(/^\/|\/$/, '').split('/')?.[0]
  25. const foundProduct = products.find((product) => {
  26. const defaultIsPageFromProduct = () => {
  27. if (product.isFallbackProduct) return false
  28. return product.path === pathnameFirstSegment
  29. }
  30. return product.isPageFromProduct
  31. ? product.isPageFromProduct({ pathname, product, pageProps })
  32. : defaultIsPageFromProduct()
  33. })
  34. if (foundProduct) return foundProduct
  35. const fallbackProduct = products.find((product) => product.isFallbackProduct)
  36. if (fallbackProduct) return fallbackProduct
  37. throw new Error('No product found')
  38. }
  39. function getActiveHero(pathname, product, pageProps) {
  40. if (!product?.heroes) return undefined
  41. return (
  42. product.heroes.find((hero) => {
  43. return hero.doesPageHaveHero
  44. ? hero.doesPageHaveHero({ pathname, hero, product, pageProps })
  45. : pathname === hero.path
  46. })?.component ?? undefined
  47. )
  48. }
  49. function getActiveSection(pathname, product, pageProps) {
  50. if (!product?.sections) return undefined
  51. // Find active section.
  52. const foundSection = product.sections.find((section) => {
  53. const defaultIsPageFromSection = () => {
  54. if (section.isFallbackSection) return false
  55. return (
  56. pathname.startsWith(`${section.href}/`) || pathname === section.href
  57. )
  58. }
  59. return section.isPageFromSection
  60. ? section.isPageFromSection({ pathname, section, product, pageProps })
  61. : defaultIsPageFromSection()
  62. })
  63. const fallbackSection = product.sections.find(
  64. (section) => section.isFallbackSection
  65. )
  66. const activeSection =
  67. foundSection || fallbackSection
  68. ? { ...(foundSection ?? fallbackSection) }
  69. : undefined
  70. // Add navigation helpers.
  71. if (activeSection && activeSection.navigation) {
  72. const allLinks = activeSection.navigation.flatMap((group) => group.links)
  73. const linkIndex = allLinks.findIndex((link) => link.href === pathname)
  74. activeSection.previousPage = allLinks[linkIndex - 1]
  75. activeSection.nextPage = allLinks[linkIndex + 1]
  76. activeSection.navigationGroup = activeSection.navigation.find((group) =>
  77. group.links.find((link) => link.href === pathname)
  78. )
  79. }
  80. return activeSection
  81. }
  82. function parseTableOfContents(nodes, slugify = slugifyWithCounter()) {
  83. let sections = []
  84. for (let node of nodes) {
  85. if (node.name === 'h2' || node.name === 'h3') {
  86. let title = getNodeText(node)
  87. if (title) {
  88. let id = slugify(title)
  89. node.attributes.id = id
  90. if (node.name === 'h3') {
  91. if (!sections[sections.length - 1]) {
  92. throw new Error(
  93. 'Cannot add `h3` to table of contents without a preceding `h2`'
  94. )
  95. }
  96. sections[sections.length - 1].children.push({
  97. ...node.attributes,
  98. title,
  99. })
  100. } else {
  101. sections.push({ ...node.attributes, title, children: [] })
  102. }
  103. }
  104. }
  105. sections.push(...parseTableOfContents(node.children ?? [], slugify))
  106. }
  107. return sections
  108. }
  109. function getNodeText(node) {
  110. let text = ''
  111. for (let child of node.children ?? []) {
  112. if (typeof child === 'string') {
  113. text += child
  114. }
  115. text += getNodeText(child)
  116. }
  117. return text
  118. }