/** * Copy/pasted from https://github.com/andreasbm/focus-trap */ /** * Traverses the slots of the open shadowroots and returns all children matching the query. * We need to traverse each child-depth one at a time because if an element should be skipped * (for example because it is hidden) we need to skip all of it's children. If we use querySelectorAll("*") * the information of whether the children is within a hidden parent is lost. * @param {ShadowRoot | HTMLElement} root * @param skipNode * @param isMatch * @param {number} maxDepth * @param {number} depth * @returns {HTMLElement[]} */ export function queryShadowRoot(root, skipNode, isMatch, maxDepth = 20, depth = 0) { const matches = []; // If the depth is above the max depth, abort the searching here. if (depth >= maxDepth) { return matches; } // Traverses a slot element const traverseSlot = ($slot) => { // Only check nodes that are of the type Node.ELEMENT_NODE // Read more here https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType const assignedNodes = $slot .assignedNodes() .filter((node) => node.nodeType === 1); if (assignedNodes.length > 0) { const $slotParent = assignedNodes[0].parentElement; return queryShadowRoot($slotParent, skipNode, isMatch, maxDepth, depth + 1); } return []; }; // Go through each child and continue the traversing if necessary // Even though the typing says that children can't be undefined, Edge 15 sometimes gives an undefined value. // Therefore we fallback to an empty array if it is undefined. const children = Array.from(root.children || []); for (const $child of children) { // Check if the element and its descendants should be skipped if (skipNode($child)) { // console.log('-- SKIP', $child); continue; } // console.log('$child', $child); // If the element matches we always add it if (isMatch($child)) { matches.push($child); } if ($child.shadowRoot != null) { // If the element has a shadow root we need to traverse it matches.push(...queryShadowRoot($child.shadowRoot, skipNode, isMatch, maxDepth, depth + 1)); } else if ($child.tagName === 'SLOT') { // If the child is a slot we need to traverse each assigned node matches.push(...traverseSlot($child)); } else { // Traverse the children of the element matches.push(...queryShadowRoot($child, skipNode, isMatch, maxDepth, depth + 1)); } } return matches; } /** * Returns whether the element is hidden. * @param $elem */ export function isHidden($elem) { return ($elem.hasAttribute('hidden') || ($elem.hasAttribute('aria-hidden') && $elem.getAttribute('aria-hidden') !== 'false') || // A quick and dirty way to check whether the element is hidden. // For a more fine-grained check we could use "window.getComputedStyle" but we don't because of bad performance. // If the element has visibility set to "hidden" or "collapse", display set to "none" or opacity set to "0" through CSS // we won't be able to catch it here. We accept it due to the huge performance benefits. $elem.style.display === `none` || $elem.style.opacity === `0` || $elem.style.visibility === `hidden` || $elem.style.visibility === `collapse`); // If offsetParent is null we can assume that the element is hidden // https://stackoverflow.com/questions/306305/what-would-make-offsetparent-null // || $elem.offsetParent == null; } /** * Returns whether the element is disabled. * @param $elem */ export function isDisabled($elem) { return ($elem.hasAttribute('disabled') || ($elem.hasAttribute('aria-disabled') && $elem.getAttribute('aria-disabled') !== 'false')); } /** * Determines whether an element is focusable. * Read more here: https://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus/1600194#1600194 * Or here: https://stackoverflow.com/questions/18261595/how-to-check-if-a-dom-element-is-focusable * @param $elem */ export function isFocusable($elem) { // Discard elements that are removed from the tab order. if ($elem.getAttribute('tabindex') === '-1' || isHidden($elem) || isDisabled($elem)) { return false; } return ( // At this point we know that the element can have focus (eg. won't be -1) if the tabindex attribute exists $elem.hasAttribute('tabindex') || // Anchor tags or area tags with a href set (($elem instanceof HTMLAnchorElement || $elem instanceof HTMLAreaElement) && $elem.hasAttribute('href')) || // Form elements which are not disabled $elem instanceof HTMLButtonElement || $elem instanceof HTMLInputElement || $elem instanceof HTMLTextAreaElement || $elem instanceof HTMLSelectElement || // IFrames $elem instanceof HTMLIFrameElement); } //# sourceMappingURL=focus-trap.js.map