Add Strong display modes and book page boundaries

This commit is contained in:
2026-07-12 08:53:45 -05:00
parent c0774c8e52
commit 7674510952
8 changed files with 72 additions and 27 deletions
+9 -12
View File
@@ -125,7 +125,7 @@ textarea:focus-visible {
.page-controls {
display: grid;
grid-template-columns: minmax(130px, 1.1fr) minmax(130px, 1.3fr) minmax(86px, 0.7fr) minmax(78px, 0.6fr);
grid-template-columns: minmax(130px, 1.1fr) minmax(130px, 1.3fr) minmax(86px, 0.7fr) minmax(78px, 0.6fr) minmax(120px, 0.9fr);
gap: 8px;
align-items: end;
}
@@ -250,9 +250,13 @@ textarea:focus-visible {
.strong-token {
display: inline;
position: relative;
cursor: pointer;
}
.strong-token.highlight,
.strong-token.numbers {
border-radius: 3px;
box-shadow: inset 0 -0.45em 0 rgba(214, 168, 79, 0.22);
cursor: pointer;
}
.strong-token:hover,
@@ -266,23 +270,16 @@ textarea:focus-visible {
}
.strong-code {
display: inline-block;
margin: 0 2px 0 3px;
transform: translateY(-0.12em);
border: 1px solid rgba(138, 109, 46, 0.34);
border-radius: 4px;
padding: 0 3px;
background: #fff8e6;
margin-left: 2px;
color: #735c0f;
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 9px;
font-weight: 800;
line-height: 1.35;
line-height: 0;
vertical-align: super;
}
.strong-token.active .strong-code {
border-color: rgba(47, 111, 94, 0.42);
background: #e7f0eb;
color: #225446;
}
+51 -9
View File
@@ -7,6 +7,7 @@ import './App.css'
const tagColors = ['#2f6f5e', '#735c0f', '#8a3ffc', '#b42318', '#245ba7']
const pageSize = 34
type ViewMode = 'page' | 'study' | 'notes' | 'compare'
type StrongDisplayMode = 'off' | 'highlight' | 'numbers'
function reference(verse: Verse) {
return `${verse.book} ${verse.chapter}:${verse.verse}`
@@ -20,6 +21,7 @@ function App() {
const [selectedId, setSelectedId] = useState<number | null>(null)
const [pageStartId, setPageStartId] = useState<number | null>(null)
const [selectedStrong, setSelectedStrong] = useState<string | null>(null)
const [strongDisplay, setStrongDisplay] = useState<StrongDisplayMode>('highlight')
const [tagInput, setTagInput] = useState('')
const [noteDraft, setNoteDraft] = useState('')
const [loading, setLoading] = useState(true)
@@ -85,9 +87,19 @@ function App() {
const pageAnchors = useMemo(() => {
const source = libraryVerses.length ? libraryVerses : verses
const anchors: Verse[] = []
let index = 0
for (let index = 0; index < source.length; index += pageSize) {
anchors.push(source[index])
while (index < source.length) {
const book = source[index].book
let bookVerseIndex = 0
while (index < source.length && source[index].book === book) {
if (bookVerseIndex % pageSize === 0) {
anchors.push(source[index])
}
bookVerseIndex += 1
index += 1
}
}
return anchors
@@ -98,7 +110,10 @@ function App() {
const pageStartIndex = source.findIndex((verse) => verse.id === pageStartId)
const selectedIndex = source.findIndex((verse) => verse.id === selectedVerse.id)
const start = Math.max(0, pageStartIndex >= 0 ? pageStartIndex : selectedIndex)
return source.slice(start, start + pageSize)
const book = source[start]?.book
return source
.slice(start, start + pageSize)
.filter((verse) => verse.book === book)
}, [libraryVerses, pageStartId, selectedVerse, verses])
const pageRange = pageVerses.length
? `${reference(pageVerses[0])} - ${reference(pageVerses[pageVerses.length - 1])}`
@@ -169,8 +184,17 @@ function App() {
const source = libraryVerses.length ? libraryVerses : verses
const index = source.findIndex((entry) => entry.id === verse.id)
if (index < 0) return pageStartId ?? verse.id
const pageIndex = Math.floor(index / pageSize) * pageSize
return source[pageIndex]?.id ?? verse.id
let anchor = source[index]
for (const pageAnchor of pageAnchors) {
const anchorIndex = source.findIndex((entry) => entry.id === pageAnchor.id)
if (anchorIndex > index) break
if (pageAnchor.book === verse.book) {
anchor = pageAnchor
}
}
return anchor.id
}
function selectVerse(verse: Verse) {
@@ -184,7 +208,7 @@ function App() {
}
function renderVerseText(verse: Verse) {
if (!verse.strongs.length) return verse.text
if (!verse.strongs.length || strongDisplay === 'off') return verse.text
const parts: ReactNode[] = []
let cursor = 0
@@ -203,10 +227,15 @@ function App() {
const linkedText = verse.text.slice(index, index + surface.length)
const isActive = entry.strong === selectedStrong
const showCode = strongDisplay === 'numbers' || isActive
parts.push(
<span
key={`${verse.id}-${entry.strong}-${entry.position}`}
className={isActive ? 'strong-token active' : 'strong-token'}
className={[
'strong-token',
strongDisplay,
isActive ? 'active' : '',
].filter(Boolean).join(' ')}
role="button"
tabIndex={0}
title={`${entry.surface}: ${entry.strong}`}
@@ -222,8 +251,8 @@ function App() {
}}
>
<span className="strong-surface">{linkedText}</span>
{(verse.id === selectedId || isActive) ? (
<small className="strong-code">{entry.strong}</small>
{showCode ? (
<sup className="strong-code">{entry.strong}</sup>
) : null}
</span>,
)
@@ -388,6 +417,19 @@ function App() {
))}
</select>
</label>
<label>
Strong's
<select
value={strongDisplay}
onChange={(event) =>
setStrongDisplay(event.target.value as StrongDisplayMode)
}
>
<option value="off">Off</option>
<option value="highlight">Highlight</option>
<option value="numbers">Numbers</option>
</select>
</label>
</div>
<form
className="search-box"