import React, { useEffect, RefObject } from 'react' import { tabbable } from 'tabbable' interface Props { children: React.ReactNode | any focusFirst?: boolean } export default function FocusTrap({ children, focusFirst = false }: Props) { const root: RefObject = React.useRef() const anchor: RefObject = React.useRef(document.activeElement) const returnFocus = () => { // Returns focus to the last focused element prior to trap. if (anchor) { anchor.current.focus() } } const trapFocus = () => { // Focus the container element if (root.current) { root.current.focus() if (focusFirst) { selectFirstFocusableEl() } } } const selectFirstFocusableEl = () => { // Try to find focusable elements, if match then focus // Up to 6 seconds of load time threshold let match = false let end = 60 // Try to find match at least n times let i = 0 const timer = setInterval(() => { if (!match !== i > end) { match = !!tabbable(root.current).length if (match) { // Attempt to focus the first el tabbable(root.current)[0].focus() } i = i + 1 } else { // Clear interval after n attempts clearInterval(timer) } }, 100) } useEffect(() => { setTimeout(trapFocus, 20) return () => { returnFocus() } }, [root, children]) return React.createElement('div', { ref: root, children, className: 'outline-none focus-trap', tabIndex: -1, }) }