// The cross-reference map for a chapter (from IQ Bible's reference data): the chapter in the middle, the chapters // it is linked to around it, in Bible order. Heavier lines mean more verse-to-verse links. Tap a chapter to see how // they connect, open it, or move the map's centre there. A list below says the same in words. import { useEffect, useMemo, useState } from 'react'; import Sheet from './Sheet.jsx'; import { useIq } from '../../hooks/useIq.js'; import { bookOptions } from '../../utils/bibleUtils.jsx'; import { chapterId, layoutMap, linkedChapters, parseGraph } from '../../utils/xrefMap.js'; const SIZE = 700; const bookName = (abbrev) => bookOptions.find((b) => b.abbrev === abbrev)?.name ?? abbrev; export default function CrossRefMap({ open, onClose, book, chapter, onOpenChapter }) { const [center, setCenter] = useState({ book, chapter }); const [depth, setDepth] = useState(1); const [picked, setPicked] = useState(null); useEffect(() => { if (open) { setCenter({ book, chapter }); setPicked(null); } }, [open, book, chapter]); const { status, data, error } = useIq(open ? 'cross-references/graph' : null, { book: center.book, chapter: center.chapter, depth }); const centerId = chapterId(center.book, center.chapter); const graph = useMemo(() => parseGraph(data), [data]); const map = useMemo(() => layoutMap(graph, centerId, SIZE), [graph, centerId]); const linked = useMemo(() => linkedChapters(graph, centerId), [graph, centerId]); const byId = new Map(map.nodes.map((n) => [n.id, n])); const sel = picked ? byId.get(picked) : null; const selLinks = sel ? graph.edges.filter((e) => (e.source === centerId && e.target === sel.id) || (e.target === centerId && e.source === sel.id)) : []; const label = (n) => `${bookName(n.book)} ${n.chapter}`; const hot = new Set([...map.nodes].filter((n) => n.id !== centerId).sort((a, b) => b.weight - a.weight).slice(0, 14).map((n) => n.id)); // labelled on the picture return (
{[[1, 'Directly linked'], [2, 'And the ones they link to']].map(([d, name]) => ( ))} {(center.book !== book || center.chapter !== chapter) && ( )} Lines are heavier where there are more verse links.
{status === 'loading' &&

Drawing the map…

} {status === 'error' &&

{error}

} {status === 'empty' &&

No cross-references are recorded for this chapter.

} {status === 'ready' && map.nodes.length > 1 && ( <> {map.edges.map((e) => )} {map.nodes.map((n) => ( setPicked(n.id === picked ? null : n.id)} className="cursor-pointer" role="button" tabIndex={0} aria-label={`${label(n)}, ${n.weight} links`} onKeyDown={(ev) => { if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); setPicked(n.id === picked ? null : n.id); } }}> {(n.id === centerId || n.id === picked || hot.has(n.id)) && ( {label(n)} )} ))} {graph.truncated &&

A large map: only the first part is shown.

} {sel && (

{label(sel)}

{selLinks.length ? `${selLinks.reduce((n, e) => n + e.weight, 0)} verse links with ${bookName(center.book)} ${center.chapter}` : `${sel.ring > 1 ? 'Linked through another chapter' : 'Linked'}`}

{sel.id !== centerId && }
)}

Most linked to {bookName(center.book)} {center.chapter}

    {linked.slice(0, 20).map((c) => (
  1. {c.weight}
  2. ))}
)}

Cross-references via IQ Bible.

); }