// https://github.com/alexandrzavalii/focus-trap-js/blob/master/src/index.js v1.0.9 export const candidateSelectors = [ 'input', 'select', 'textarea', 'a[href]', 'button', '[tabindex]', 'audio[controls]', 'video[controls]', '[contenteditable]:not([contenteditable="false"])', ]; function isHidden(node: any) { // offsetParent being null will allow detecting cases where an element is invisible or inside an invisible element, // as long as the element does not use position: fixed. For them, their visibility has to be checked directly as well. return ( node.offsetParent === null || getComputedStyle(node).visibility === 'hidden' ); } export function getAllTabbingElements(parentElem: any) { var tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(',')); var onlyTabbable = []; for (var i = 0; i < tabbableNodes.length; i++) { var node = tabbableNodes[i]; if (!node.disabled && getTabindex(node) > -1 && !isHidden(node)) { onlyTabbable.push(node); } } return onlyTabbable; } export function tabTrappingKey(event: any, parentElem: any) { // check if current event keyCode is tab if (!event || event.key !== 'Tab') return; if (!parentElem || !parentElem.contains) { if (process && process.env.NODE_ENV === 'development') { console.warn('focus-trap-js: parent element is not defined'); } return false; } if (!parentElem.contains(event.target)) { return false; } var allTabbingElements = getAllTabbingElements(parentElem); var firstFocusableElement = allTabbingElements[0]; var lastFocusableElement = allTabbingElements[allTabbingElements.length - 1]; if (event.shiftKey && event.target === firstFocusableElement) { lastFocusableElement.focus(); event.preventDefault(); return true; } else if (!event.shiftKey && event.target === lastFocusableElement) { firstFocusableElement.focus(); event.preventDefault(); return true; } return false; } function getTabindex(node: any) { var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10); if (!isNaN(tabindexAttr)) return tabindexAttr; // Browsers do not return tabIndex correctly for contentEditable nodes; // so if they don't have a tabindex attribute specifically set, assume it's 0. if (isContentEditable(node)) return 0; return node.tabIndex; } function isContentEditable(node: any) { return node.getAttribute('contentEditable'); }