diff --git a/frontend/src/hooks/useFocusTrap.ts b/frontend/src/hooks/useFocusTrap.ts new file mode 100644 index 0000000..812f8ce --- /dev/null +++ b/frontend/src/hooks/useFocusTrap.ts @@ -0,0 +1,38 @@ +import { useEffect, useRef } from 'react' + +export function useFocusTrap(active: boolean) { + const ref = useRef(null) + const previousFocus = useRef(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(sel) + if (initial.length > 0) initial[0].focus() + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key !== 'Tab') return + const focusable = container.querySelectorAll(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 +}