/*! * Adapted from jQuery UI core * * http://jqueryui.com * * Copyright 2014 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/category/ui-core/ */ const tabbableNode = /input|select|textarea|button|object/ function hidesContents(element: HTMLElement) { const zeroSize = element.offsetWidth <= 0 && element.offsetHeight <= 0 // If the node is empty, this is good enough if (zeroSize && !element.innerHTML) return true // Otherwise we need to check some styles const style = window.getComputedStyle(element) return ( style.getPropertyValue("display") === "none" || (zeroSize && style.getPropertyValue("overflow") !== "visible") ) } function visible(element: any) { let parentElement = element while (parentElement) { if (parentElement === document.body) break if (hidesContents(parentElement)) return false parentElement = parentElement.parentNode } return true } export function focusable(element: HTMLElement, isTabIndexNotNaN: boolean) { const nodeName = element.nodeName.toLowerCase() const res = //@ts-ignore (tabbableNode.test(nodeName) && !element.disabled) || //@ts-ignore (nodeName === "a" ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) return res && visible(element) } export function tabbable(element: HTMLElement) { const tabIndexAttr = element.getAttribute("tabindex") const tabIndex = tabIndexAttr !== null ? Number(tabIndexAttr) : undefined const isTabIndexNaN = tabIndex === undefined || isNaN(tabIndex) return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN) } export default function findTabbableDescendants( element: HTMLElement | null | undefined ): HTMLElement[] { if (!(element instanceof HTMLElement)) { return [] } return Array.from(element.querySelectorAll("*")) .filter((el): el is HTMLElement => el instanceof HTMLElement) .filter(tabbable) }