62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
type VerseEntry = {
|
|
verse: number;
|
|
reference: string;
|
|
};
|
|
|
|
export function VerseIndex({
|
|
verses,
|
|
chapter,
|
|
}: {
|
|
verses: VerseEntry[];
|
|
chapter: string;
|
|
}) {
|
|
const [activeVerse, setActiveVerse] = useState<number | null>(null);
|
|
|
|
useEffect(() => {
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
for (const entry of entries) {
|
|
if (entry.isIntersecting) {
|
|
const id = entry.target.id;
|
|
const num = parseInt(id.replace("verse-", ""));
|
|
if (!isNaN(num)) setActiveVerse(num);
|
|
}
|
|
}
|
|
},
|
|
{ rootMargin: "-20% 0px -60% 0px" }
|
|
);
|
|
|
|
const articles = document.querySelectorAll("article[id^='verse-']");
|
|
articles.forEach((el) => observer.observe(el));
|
|
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
return (
|
|
<aside className="hidden lg:block w-48 shrink-0 sticky top-[89px] self-start max-h-[calc(100vh-89px)] overflow-y-auto py-8 pr-4">
|
|
<h2 className="text-xs font-bold uppercase tracking-wider text-amber-600 dark:text-amber-500 mb-3 px-2">
|
|
Verses
|
|
</h2>
|
|
<nav className="grid grid-cols-3 gap-0.5">
|
|
{verses.map((v) => (
|
|
<a
|
|
key={v.verse}
|
|
href={`#verse-${v.verse}`}
|
|
className={`px-2 py-1 rounded text-sm text-center transition-colors ${
|
|
activeVerse === v.verse
|
|
? "bg-amber-200 dark:bg-amber-900/40 text-amber-900 dark:text-amber-300 font-medium"
|
|
: "text-zinc-600 dark:text-zinc-400 hover:bg-amber-100 dark:hover:bg-zinc-800 hover:text-zinc-900 dark:hover:text-zinc-200"
|
|
}`}
|
|
>
|
|
{v.verse}
|
|
</a>
|
|
))}
|
|
</nav>
|
|
</aside>
|
|
);
|
|
}
|