fix: modal stole input focus on every keystroke

The focus/scroll-lock effect depended on onClose (a new function each render), so it
re-ran on every keystroke and refocused the panel. Split focus-on-open from the Escape
listener so inputs in modals keep focus while typing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-06-03 11:42:26 +02:00
parent 4086c132cb
commit 85bd428df7

View File

@@ -17,19 +17,26 @@ const sizes = { sm: 'max-w-sm', md: 'max-w-lg', lg: 'max-w-2xl' }
export function Modal({ open, onClose, title, children, footer, size = 'md' }: Props) {
const panelRef = useRef<HTMLDivElement>(null)
// Lock scroll + focus the panel only when the modal opens (not on every render,
// otherwise typing in an input would keep stealing focus back to the panel).
useEffect(() => {
if (!open) return
const prev = document.body.style.overflow
document.body.style.overflow = 'hidden'
panelRef.current?.focus()
return () => {
document.body.style.overflow = prev
}
}, [open])
// Escape-to-close listener kept separate so its onClose dependency can change freely.
useEffect(() => {
if (!open) return
function onKey(e: KeyboardEvent) {
if (e.key === 'Escape') onClose()
}
document.addEventListener('keydown', onKey)
const prev = document.body.style.overflow
document.body.style.overflow = 'hidden'
panelRef.current?.focus()
return () => {
document.removeEventListener('keydown', onKey)
document.body.style.overflow = prev
}
return () => document.removeEventListener('keydown', onKey)
}, [open, onClose])
if (!open) return null