const candidateSelectors = [
'input',
'select',
'textarea',
'a[href]',
'button',
'[tabindex]',
'audio[controls]',
'video[controls]',
'[contenteditable]:not([contenteditable="false"])',
'details>summary:first-of-type',
'details',
];
const candidateSelector = /* #__PURE__ */ candidateSelectors.join(',');
const matches =
typeof Element === 'undefined'
? function () {}
: Element.prototype.matches ||
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
const getCandidates = function (el, includeContainer, filter) {
let candidates = Array.prototype.slice.apply(
el.querySelectorAll(candidateSelector)
);
if (includeContainer && matches.call(el, candidateSelector)) {
candidates.unshift(el);
}
candidates = candidates.filter(filter);
return candidates;
};
const isContentEditable = function (node) {
return node.contentEditable === 'true';
};
const getTabindex = function (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;
}
// in Chrome, , and elements get a default
// `tabIndex` of -1 when the 'tabindex' attribute isn't specified in the DOM,
// yet they are still part of the regular tab order; in FF, they get a default
// `tabIndex` of 0; since Chrome still puts those elements in the regular tab
// order, consider their tab index to be 0.
if (
(node.nodeName === 'AUDIO' ||
node.nodeName === 'VIDEO' ||
node.nodeName === 'DETAILS') &&
node.getAttribute('tabindex') === null
) {
return 0;
}
return node.tabIndex;
};
const sortOrderedTabbables = function (a, b) {
return a.tabIndex === b.tabIndex
? a.documentOrder - b.documentOrder
: a.tabIndex - b.tabIndex;
};
const isInput = function (node) {
return node.tagName === 'INPUT';
};
const isHiddenInput = function (node) {
return isInput(node) && node.type === 'hidden';
};
const isDetailsWithSummary = function (node) {
const r =
node.tagName === 'DETAILS' &&
Array.prototype.slice
.apply(node.children)
.some((child) => child.tagName === 'SUMMARY');
return r;
};
const getCheckedRadio = function (nodes, form) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].checked && nodes[i].form === form) {
return nodes[i];
}
}
};
const isTabbableRadio = function (node) {
if (!node.name) {
return true;
}
const radioScope = node.form || node.ownerDocument;
const queryRadios = function (name) {
return radioScope.querySelectorAll(
'input[type="radio"][name="' + name + '"]'
);
};
let radioSet;
if (
typeof window !== 'undefined' &&
typeof window.CSS !== 'undefined' &&
typeof window.CSS.escape === 'function'
) {
radioSet = queryRadios(window.CSS.escape(node.name));
} else {
try {
radioSet = queryRadios(node.name);
} catch (err) {
// eslint-disable-next-line no-console
console.error(
'Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s',
err.message
);
return false;
}
}
const checked = getCheckedRadio(radioSet, node.form);
return !checked || checked === node;
};
const isRadio = function (node) {
return isInput(node) && node.type === 'radio';
};
const isNonTabbableRadio = function (node) {
return isRadio(node) && !isTabbableRadio(node);
};
const isHidden = function (node, displayCheck) {
if (getComputedStyle(node).visibility === 'hidden') {
return true;
}
const isDirectSummary = matches.call(node, 'details>summary:first-of-type');
const nodeUnderDetails = isDirectSummary ? node.parentElement : node;
if (matches.call(nodeUnderDetails, 'details:not([open]) *')) {
return true;
}
if (!displayCheck || displayCheck === 'full') {
while (node) {
if (getComputedStyle(node).display === 'none') {
return true;
}
node = node.parentElement;
}
} else if (displayCheck === 'non-zero-area') {
const { width, height } = node.getBoundingClientRect();
return width === 0 && height === 0;
}
return false;
};
// form fields (nested) inside a disabled fieldset are not focusable/tabbable
// unless they are in the _first_