react-use-focus-trap
Version:
A react hook to trap the focus within a reference
88 lines (80 loc) • 4.92 kB
JavaScript
var $98vvg$react = require("react");
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
$parcel$export(module.exports, "useFocusTrap", () => $d508491da249d331$export$5562a1f223a0d314);
function $4bad90aa94464bfd$export$94fec357614ce4e9(stringToConvert) {
const parsed = parseInt(stringToConvert);
return parsed ? parsed : 0;
}
function $4bad90aa94464bfd$export$8f129d607746da1a(firstNode, secondNode) {
const tabIndexes = [
firstNode,
secondNode
].map((node)=>$4bad90aa94464bfd$export$b1fd826f03019552(node));
return tabIndexes.map((tabIndexValue)=>$4bad90aa94464bfd$var$sanitizeTabIndexInput(tabIndexValue, Math.max(...tabIndexes))).reduce((previousValue, currentValue)=>previousValue - currentValue);
}
/**
* Prepares a tab-index to be further processed for the tab order of the focus trap.
* It can't be less than 0, because negative values can not be part of the tab order at all.
* In case it's exactly 0 it actually needs to be higher than any positive (> 0) value, since tab-index=0 means "follow the system default order".
* The default tab order comes _after_ special tab indexes (>0).
* @param {number} tabIndex The index to sanitize
* @param {number} highestPositiveTabIndex The largest number among the tab indexes from the same context
* @throws An error if the tabIndex is less than 0
* @returns Tha sanitized tab index
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex} for further information on the tabindex and its order
*/ function $4bad90aa94464bfd$var$sanitizeTabIndexInput(tabIndex, highestPositiveTabIndex) {
if (tabIndex < 0) throw new Error(`Unable to sort given input. A negative value is not part of the tab order: ${tabIndex}`);
// 0 based tab indexes have a higher order than positive valued indicies, thus we add 1 to the max value
return tabIndex === 0 ? highestPositiveTabIndex + 1 : tabIndex;
}
function $4bad90aa94464bfd$export$b1fd826f03019552(targetNode) {
return $4bad90aa94464bfd$export$94fec357614ce4e9(targetNode.getAttribute("tabindex"));
}
const $d508491da249d331$var$focusableElementsSelector = "a[href], area[href], input:not([disabled]):not([type=hidden]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]";
const $d508491da249d331$var$TAB_KEY = 9;
function $d508491da249d331$export$5562a1f223a0d314() {
const trapRef = (0, $98vvg$react.useRef)(null);
const selectNextFocusableElem = (0, $98vvg$react.useCallback)((sortedFocusableElems, currentIndex, shiftKeyPressed = false, skipCount = 0)=>{
if (skipCount > sortedFocusableElems.length) // this means that it ran through all of elements but non was properly focusable
// hence we stop it to avoid running in an infinite loop
return false;
const backwards = !!shiftKeyPressed;
const maxIndex = sortedFocusableElems.length - 1;
if (!currentIndex) currentIndex = sortedFocusableElems.indexOf(document.activeElement) ?? 0;
let nextIndex = backwards ? currentIndex - 1 : currentIndex + 1;
if (nextIndex > maxIndex) nextIndex = 0;
if (nextIndex < 0) nextIndex = maxIndex;
const newFocusElem = sortedFocusableElems[nextIndex];
newFocusElem.focus();
if (document.activeElement !== newFocusElem) // run another round
selectNextFocusableElem(sortedFocusableElems, nextIndex, shiftKeyPressed, skipCount + 1);
});
// defining the trap function first
const trapper = (0, $98vvg$react.useCallback)((evt)=>{
const trapRefElem = trapRef.current;
if (trapRefElem !== null) {
if (evt.which === $d508491da249d331$var$TAB_KEY || evt.key === "Tab") {
evt.preventDefault();
const shiftKeyPressed = !!evt.shiftKey;
let focusableElems = Array.from(trapRefElem.querySelectorAll($d508491da249d331$var$focusableElementsSelector)).filter((focusableElement)=>(0, $4bad90aa94464bfd$export$b1fd826f03019552)(focusableElement) >= 0); // caching this is NOT a good idea in dynamic applications - so don't!
// now we need to sort it by tabIndex, highest first
focusableElems = focusableElems.sort((0, $4bad90aa94464bfd$export$8f129d607746da1a));
selectNextFocusableElem(focusableElems, undefined, shiftKeyPressed);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
(0, $98vvg$react.useEffect)(()=>{
window.addEventListener("keydown", trapper);
return ()=>{
window.removeEventListener("keydown", trapper);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return [
trapRef
];
}
//# sourceMappingURL=useFocusTrap.js.map