focus trap hook
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
export function useFocusTrap(active: boolean) {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const previousFocus = useRef<HTMLElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!active || !ref.current) return
|
||||
const activeElement = document.activeElement
|
||||
previousFocus.current = activeElement instanceof HTMLElement ? activeElement : null
|
||||
|
||||
const container = ref.current
|
||||
const sel = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
||||
const initial = container.querySelectorAll<HTMLElement>(sel)
|
||||
if (initial.length > 0) initial[0].focus()
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key !== 'Tab') return
|
||||
const focusable = container.querySelectorAll<HTMLElement>(sel)
|
||||
if (focusable.length === 0) return
|
||||
const first = focusable[0]
|
||||
const last = focusable[focusable.length - 1]
|
||||
if (e.shiftKey && document.activeElement === first) {
|
||||
e.preventDefault(); last.focus()
|
||||
} else if (!e.shiftKey && document.activeElement === last) {
|
||||
e.preventDefault(); first.focus()
|
||||
}
|
||||
}
|
||||
|
||||
container.addEventListener('keydown', handleKeyDown)
|
||||
return () => {
|
||||
container.removeEventListener('keydown', handleKeyDown)
|
||||
previousFocus.current?.focus()
|
||||
}
|
||||
}, [active])
|
||||
|
||||
return ref
|
||||
}
|
||||
Reference in New Issue
Block a user