import { useCallback, useEffect, useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import clsx from 'clsx'
import { Hero } from '@/components/Hero'
import { Logo } from '@/components/Logo'
import { MobileNavigation } from '@/components/MobileNavigation'
import { Navigation } from '@/components/Navigation'
import { Prose } from '@/components/Prose'
import { Search } from '@/components/Search'
import { ThemeSelector } from '@/components/ThemeSelector'
function Header({ navigation }) {
let [isScrolled, setIsScrolled] = useState(false)
useEffect(() => {
function onScroll() {
setIsScrolled(window.scrollY > 0)
}
onScroll()
window.addEventListener('scroll', onScroll)
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [])
return (
)
}
export function Layout({ children, title, navigation, tableOfContents }) {
let router = useRouter()
let isHomePage = router.pathname === '/'
let allLinks = navigation.flatMap((section) => section.links)
let linkIndex = allLinks.findIndex((link) => link.href === router.pathname)
let previousPage = allLinks[linkIndex - 1]
let nextPage = allLinks[linkIndex + 1]
let section = navigation.find((section) =>
section.links.find((link) => link.href === router.pathname)
)
let currentSection = useTableOfContents(tableOfContents)
function isActive(section) {
if (section.id === currentSection) {
return true
}
if (!section.children) {
return false
}
return section.children.findIndex(isActive) > -1
}
return (
<>
{isHomePage && }
{(title || section) && (
{section && (
{section.title}
)}
{title && (
{title}
)}
)}
{children}
{previousPage && (
)}
{nextPage && (
)}
>
)
}
function useTableOfContents(tableOfContents) {
let [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id)
let getHeadings = useCallback(() => {
function* traverse(node) {
if (Array.isArray(node)) {
for (let child of node) {
yield* traverse(child)
}
} else {
let el = document.getElementById(node.id)
if (!el) return
let style = window.getComputedStyle(el)
let scrollMt = parseFloat(style.scrollMarginTop)
let top = window.scrollY + el.getBoundingClientRect().top - scrollMt
yield { id: node.id, top }
for (let child of node.children ?? []) {
yield* traverse(child)
}
}
}
return Array.from(traverse(tableOfContents))
}, [tableOfContents])
useEffect(() => {
let headings = getHeadings()
if (tableOfContents.length === 0 || headings.length === 0) return
function onScroll() {
let sortedHeadings = headings.concat([]).sort((a, b) => a.top - b.top)
let top = window.pageYOffset
let current = sortedHeadings[0].id
for (let i = 0; i < sortedHeadings.length; i++) {
if (top >= sortedHeadings[i].top) {
current = sortedHeadings[i].id
}
}
setCurrentSection(current)
}
window.addEventListener('scroll', onScroll, {
capture: true,
passive: true,
})
onScroll()
return () => {
window.removeEventListener('scroll', onScroll, {
capture: true,
passive: true,
})
}
}, [getHeadings, tableOfContents])
return currentSection
}