"use client" import { useEffect, useRef, useState } from "react" import { ProjectExtraLinkType } from "@/lib/types" import { cn } from "@/lib/utils" import { useTranslation } from "@/app/i18n/client" import { Icons } from "./icons" interface Section { level: number text: string id: string } interface WikiSideNavigationProps { className?: string lang?: string content?: string project?: any } export const WikiSideNavigation = ({ className, lang = "en", content = "", project, }: WikiSideNavigationProps) => { const { t } = useTranslation(lang, "common") const [sections, setSections] = useState([]) const [activeSection, setActiveSection] = useState(null) const observerRef = useRef(null) // Extract sections from content useEffect(() => { if (!content) return const sectionsRegex = /^(#{1,3})\s(.+)/gm const extractedSections: Section[] = [] let match while ((match = sectionsRegex.exec(content)) !== null) { const text = match[2] if (!extractedSections.some((section) => section.text === text)) { extractedSections.push({ level: match[1].length, text, id: text.toLowerCase().replace(/[^a-z0-9]+/g, "-"), }) } } setSections(extractedSections) // Set the first section as active by default if (extractedSections.length > 0) { setActiveSection(extractedSections[0].id) } }, [content]) // Set up intersection observer useEffect(() => { const observerOptions = { root: null, rootMargin: "-20% 0px -80% 0px", threshold: 0, } observerRef.current = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { setActiveSection(entry.target.getAttribute("data-section-id")) } }) }, observerOptions) sections.forEach((section) => { const element = document.querySelector( `[data-section-id="${section.id}"]` ) if (element) observerRef.current?.observe(element) }) return () => { if (observerRef.current) { observerRef.current.disconnect() } } }, [sections]) const scrollToSection = (sectionId: string) => { const element = document.querySelector(`[data-section-id="${sectionId}"]`) if (element) { const offset = 80 // Adjust this value based on your header height const elementPosition = element.getBoundingClientRect().top const offsetPosition = elementPosition + window.scrollY - offset window.scrollTo({ top: offsetPosition, behavior: "smooth", }) setActiveSection(sectionId) } } const ExtraLinkLabelMapping: Record< ProjectExtraLinkType, { label: string icon?: any } > = { buildWith: { label: t("buildWith"), icon: , }, play: { label: t("tryItOut"), icon: , }, research: { label: t("deepDiveResearch"), icon: , }, learn: { label: t("learnMore"), }, } const { extraLinks = {} } = project const hasExtraLinks = Object.keys(extraLinks).length > 0 if (sections.length === 0 || content.length === 0) return null return (
) }