Fix scripture verse popup clipped by overflow:hidden parents; v1.2.2
Publish Container / docker (push) Successful in 13m53s
Publish Container / docker (push) Successful in 13m53s
Switch from position:absolute (relative to trigger) to position:fixed with getBoundingClientRect() so the popup escapes any overflow:hidden ancestor. Also closes on scroll and window resize. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
36dfa0c1d7
commit
525ae85f6a
3 files changed
+30
-11
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "siteforge",
|
"name": "siteforge",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+2
-4
@@ -8865,10 +8865,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scripture-popup {
|
.scripture-popup {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
bottom: calc(100% + 8px);
|
z-index: 1000;
|
||||||
left: 0;
|
|
||||||
z-index: 200;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
|||||||
@@ -218,7 +218,9 @@ function ScriptureTooltip({ refText, bookId, chapter, verseStart, verseEnd }: {
|
|||||||
const [verses, setVerses] = useState<VerseData[]>([])
|
const [verses, setVerses] = useState<VerseData[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
const [pos, setPos] = useState<{ top: number; left: number } | null>(null)
|
||||||
const wrapRef = useRef<HTMLSpanElement>(null)
|
const wrapRef = useRef<HTMLSpanElement>(null)
|
||||||
|
const btnRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return
|
if (!open) return
|
||||||
@@ -251,20 +253,35 @@ function ScriptureTooltip({ refText, bookId, chapter, verseStart, verseEnd }: {
|
|||||||
})
|
})
|
||||||
}, [open, bookId, chapter, verseStart, verseEnd, verses.length])
|
}, [open, bookId, chapter, verseStart, verseEnd, verses.length])
|
||||||
|
|
||||||
|
// Compute fixed position from the trigger button so the popup escapes overflow:hidden parents
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open || !btnRef.current) { setPos(null); return }
|
||||||
|
const rect = btnRef.current.getBoundingClientRect()
|
||||||
|
const POPUP_W = 360
|
||||||
|
const left = Math.max(8, Math.min(rect.left, window.innerWidth - POPUP_W - 12))
|
||||||
|
setPos({ top: rect.bottom + 8, left })
|
||||||
|
}, [open])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return
|
if (!open) return
|
||||||
function handleClickOutside(e: MouseEvent) {
|
function handleClickOutside(e: MouseEvent) {
|
||||||
if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) {
|
if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setOpen(false)
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
function handleDismiss() { setOpen(false) }
|
||||||
document.addEventListener('mousedown', handleClickOutside)
|
document.addEventListener('mousedown', handleClickOutside)
|
||||||
return () => document.removeEventListener('mousedown', handleClickOutside)
|
window.addEventListener('scroll', handleDismiss, { passive: true, capture: true })
|
||||||
|
window.addEventListener('resize', handleDismiss)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside)
|
||||||
|
window.removeEventListener('scroll', handleDismiss, { capture: true })
|
||||||
|
window.removeEventListener('resize', handleDismiss)
|
||||||
|
}
|
||||||
}, [open])
|
}, [open])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span ref={wrapRef} className="scripture-ref-wrap">
|
<span ref={wrapRef} className="scripture-ref-wrap">
|
||||||
<button
|
<button
|
||||||
|
ref={btnRef}
|
||||||
type="button"
|
type="button"
|
||||||
className="scripture-ref"
|
className="scripture-ref"
|
||||||
onClick={() => setOpen(o => !o)}
|
onClick={() => setOpen(o => !o)}
|
||||||
@@ -273,8 +290,12 @@ function ScriptureTooltip({ refText, bookId, chapter, verseStart, verseEnd }: {
|
|||||||
>
|
>
|
||||||
{refText}
|
{refText}
|
||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && pos && (
|
||||||
<span className="scripture-popup" role="tooltip">
|
<span
|
||||||
|
className="scripture-popup"
|
||||||
|
role="tooltip"
|
||||||
|
style={{ position: 'fixed', top: pos.top, left: pos.left, bottom: 'auto' }}
|
||||||
|
>
|
||||||
<span className="scripture-popup-header">
|
<span className="scripture-popup-header">
|
||||||
<strong>{refText}</strong>
|
<strong>{refText}</strong>
|
||||||
<button type="button" className="scripture-popup-close" onClick={() => setOpen(false)} aria-label="Close">✕</button>
|
<button type="button" className="scripture-popup-close" onClick={() => setOpen(false)} aria-label="Close">✕</button>
|
||||||
|
|||||||
Reference in new issue
Block a user