feat: extract CC
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type VerseEntry = {
|
||||
verse: number;
|
||||
verse_end?: 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-muted-foreground 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-md text-sm text-center transition-colors ${
|
||||
activeVerse === v.verse
|
||||
? "bg-primary/10 text-primary font-medium"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{v.verse_end ? `${v.verse}–${v.verse_end}` : v.verse}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user