@primer/react
Version:
An implementation of GitHub's Primer Design System using React
43 lines (42 loc) • 1.61 kB
JavaScript
//#region src/internal/utils/hasInteractiveNodes.ts
const nonValidSelectors = {
disabled: "[disabled]",
hidden: "[hidden]",
inert: "[inert]",
negativeTabIndex: "[tabindex=\"-1\"]"
};
const interactiveSelector = [
`a[href]`,
`button`,
"summary",
"select",
"input:not([type=hidden])",
"textarea",
"[tabindex=\"0\"]",
`audio[controls]`,
`video[controls]`,
`[contenteditable]`
].map((selector) => `${selector}:not(${Object.values(nonValidSelectors).join("):not(")})`).join(", ");
/**
* Finds interactive nodes within the passed node.
* If the node itself is interactive, or children within are, it will return true.
*
* @param node - The HTML element to search for interactive nodes in.
* @param ignoreSelectors - A string of selectors to ignore when searching for interactive nodes. This is useful for
* ignoring nodes that are conditionally interactive based on the return value of the function.
* @returns {boolean | undefined}
*/
function hasInteractiveNodes(node, ignoreNodes) {
if (!node || isNonValidInteractiveNode(node)) return false;
const nodesToIgnore = ignoreNodes ? [node, ...ignoreNodes] : [node];
const candidates = node.querySelectorAll(interactiveSelector);
for (const candidate of candidates) if (!nodesToIgnore.includes(candidate) && !isNonValidInteractiveNode(candidate)) return true;
return false;
}
function isNonValidInteractiveNode(node) {
if (node.matches("[disabled], [hidden], [inert]")) return true;
const nodeStyle = getComputedStyle(node);
return nodeStyle.display === "none" || nodeStyle.visibility === "hidden";
}
//#endregion
export { hasInteractiveNodes };