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