Source

hooks/useTrapFocus.js

import { useEffect } from "react";

/**
 * "When the modal is open, listen for the tab key and prevent the user from tabbing out of the modal."
 *
 * The function add a keydown event listener to the document.
 * The event listener is only added when the modal is open
 * @param ref - The ref of the element that should be focused when the modal is open.
 * @param isOpen - boolean
 * @category Hooks
 * @returns an function to trap the focus inside the ref.
 */
export const useTrapFocus = (ref, isOpen) => {
	useEffect(() => {
		const handleTabKey = (e) => {
			const focusableElements = ref.current.querySelectorAll(
				'a[href], area[href], input:not([disabled],[tabindex="-1"]), select:not([disabled],[tabindex="-1"]), textarea:not([disabled],[tabindex="-1"]), button:not([disabled],[tabindex="-1"]), *[tabindex]:not([tabindex="-1"])'
			);

			const firstElement = focusableElements[0];
			const lastElement = focusableElements[focusableElements.length - 1];

			if (!e.shiftKey && document.activeElement === lastElement) {
				firstElement.focus();
				return e.preventDefault();
			}

			if (e.shiftKey && document.activeElement === firstElement) {
				lastElement.focus();
				e.preventDefault();
			}
		};
		const keyListenersMap = new Map([[9, handleTabKey]]);

		if (isOpen) {
			const keyListener = (e) => {
				const listener = keyListenersMap.get(e.keyCode);
				return listener && listener(e);
			};
			document.addEventListener("keydown", keyListener);

			return () => document.removeEventListener("keydown", keyListener);
		}
	}, [ref, isOpen]);
};