UNPKG

@workday/canvas-kit-react

Version:

The parent module that contains all Workday Canvas Kit React components

124 lines (123 loc) 4.61 kB
"use strict"; // refactor for v5 /// <reference types="@types/node" /> Object.defineProperty(exports, "__esModule", { value: true }); exports.tabTrappingKey = void 0; const candidateSelectors = [ 'input', 'select', 'textarea', 'a[href]', 'button', 'iframe', '[tabindex]', 'audio[controls]', 'video[controls]', '[contenteditable]:not([contenteditable="false"])', ]; function isHidden(node) { // 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'; } function getCheckedRadio(nodes, form) { for (let i = 0; i < nodes.length; i++) { if (nodes[i].checked && nodes[i].form === form) { return nodes[i]; } } return undefined; } function isNotRadioOrTabbableRadio(node) { if (node.tagName !== 'INPUT' || node.type !== 'radio' || !node.name) { return true; } const radioScope = node.form || node.ownerDocument; if (!radioScope) { return false; } const radioSet = radioScope.querySelectorAll('input[type="radio"][name="' + node.name + '"]'); const checked = getCheckedRadio(radioSet, node.form); return checked === node || (checked === undefined && radioSet[0] === node); } function getAllTabbingElements(parentElem) { var _a; const currentActiveElement = document.activeElement; const tabbableNodes = parentElem.querySelectorAll(candidateSelectors.join(',')); const onlyTabbable = []; for (let i = 0; i < tabbableNodes.length; i++) { const node = tabbableNodes[i]; if (currentActiveElement === node || (!node.disabled && getTabIndex(node) > -1 && !isHidden(node) && isNotRadioOrTabbableRadio(node))) { if (node instanceof HTMLIFrameElement) { const iframeDoc = (_a = node.contentWindow) === null || _a === void 0 ? void 0 : _a.document; if (iframeDoc) { const tabbableElementsInIframe = getAllTabbingElements(iframeDoc); const handleKeyEvent = (event) => { const blocked = tabTrappingKey(event, iframeDoc, onlyTabbable); if (blocked) { iframeDoc.removeEventListener('keydown', handleKeyEvent); } }; iframeDoc.addEventListener('keydown', handleKeyEvent); tabbableElementsInIframe.forEach(elem => { onlyTabbable.push(elem); }); } } else { onlyTabbable.push(node); } } } return onlyTabbable; } function tabTrappingKey(event, parentElem, onlyTabbable = null) { // check if current event keyCode is tab if (!event || event.key !== 'Tab') { return; } if (!parentElem || !parentElem.contains) { if (process.env.NODE_ENV === 'development') { console.warn('focus-trap-js: parent element is not defined'); } return false; } if (!parentElem.contains(event.target)) { return false; } const allTabbingElements = onlyTabbable || getAllTabbingElements(parentElem); const firstFocusableElement = allTabbingElements[0]; const 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; } exports.tabTrappingKey = tabTrappingKey; function getTabIndex(node) { const 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) { return node.getAttribute('contentEditable'); } tabTrappingKey.isNotRadioOrTabbableRadio = isNotRadioOrTabbableRadio;