import React from "react"; import { useEffect, useState } from "react"; import { useHeadsObserver } from "./utils/hook"; const rnd = (() => { const gen = (min, max) => max++ && [...Array(max-min)].map((s, i) => String.fromCharCode(min+i)); const sets = { alphaLower: gen(97,122), alphaUpper: gen(65,90) }; function* iter(len, set) { if (set.length < 1) set = Object.values(sets).flat(); for (let i = 0; i < len; i++) yield set[Math.random() * set.length|0] } return Object.assign(((len, ...set) => [...iter(len, set.flat())].join('')), sets); })(); const navStyle = { position: "sticky -webkit-sticky", top: "24px", maxHeight: "calc(100vh - 40px)", overflow: "auto", } as unknown as React.CSSProperties; let navulliStyle = { marginBottom: "15px", } as React.CSSProperties; const getClassName = (level) => { switch (level) { case 1: navulliStyle = { ...navulliStyle, marginLeft: "0px", }; return "head1"; case 2: navulliStyle = { ...navulliStyle, marginLeft: "10px", }; return "head2"; case 3: navulliStyle = { ...navulliStyle, marginLeft: "20px", }; return "head3"; case 4: navulliStyle = { ...navulliStyle, marginLeft: "30px", }; return "head4"; default: return undefined; } }; interface Props { className?: string; } const Toc = (props: Props) => { const { className = "" } = props; const [headings, setHeadings] = useState<any>([]); const { activeId } = useHeadsObserver(); useEffect(() => { const elements = Array.from( document.querySelectorAll("h1, h2, h3, h4") ).map((elem) => ({ id: elem.id ? elem.id : elem.id=rnd(4), text: elem.textContent, level: Number(elem.nodeName.charAt(1)), })); setHeadings(elements); }, []); return ( <aside className={`w-[20%] fixed right-0 top-auto h-screen bg-slate-50 p-5 ${className}`} > <div className="text-2xl pb-5 flex flex-row"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6 mt-2" > <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" /> </svg> <p className="px-2 mt-0.5"> Menu interne </p> </div> <nav style={navStyle}> <ul style={navulliStyle}> {headings.map((heading) => ( <li key={heading.id} className={getClassName(heading.level)} style={navulliStyle} > <a href={`#${heading.id}`} onClick={(e) => { e.preventDefault(); const element = document.querySelector(`#${heading.id}`); if(element) { element?.scrollIntoView({ behavior: "smooth", }); } else { console.log("No element"); } }} style={{ fontWeight: activeId == heading.id ? "bold" : "normal", fontFamily: "Georgia, serif", }} > {heading.text} </a> </li> ))} </ul> </nav> </aside> ); }; export default Toc;