@dvcol/common-utils
Version:
Typescript library for common utility functions and constants
62 lines (49 loc) • 3.96 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// lib/common/utils/element.utils.ts
var focusableElementSelectors = Object.freeze([
"button:not([disabled])",
"[href]",
// Ensures only real links are focusable
"input:not([disabled])",
"select:not([disabled])",
"textarea:not([disabled])",
"[tabindex]"
// Includes manually focusable elements
]);
var tabFocusableElementSelectors = Object.freeze([
'button:not([disabled], [tabindex="-1"])',
'[href]:not([tabindex="-1"])',
'input:not([disabled], [tabindex="-1"])',
'select:not([disabled], [tabindex="-1"])',
'textarea:not([disabled], [tabindex="-1"])',
'[tabindex]:not([tabindex="-1"])'
]);
var getFocusableElement = (element, targets = tabFocusableElementSelectors) => _optionalChain([element, 'optionalAccess', _ => _.querySelector, 'call', _2 => _2(targets.join(","))]);
var getFocusableElements = (element, targets = tabFocusableElementSelectors) => {
const elements = _optionalChain([element, 'optionalAccess', _3 => _3.querySelectorAll, 'call', _4 => _4(targets.join(","))]);
if (!elements) return;
return Array.from(elements).sort((a, b) => (a.tabIndex || 0) - (b.tabIndex || 0));
};
var getLastFocusableElement = (element, targets = tabFocusableElementSelectors) => {
const elements = getFocusableElements(element, targets);
if (!elements || !elements.length) return;
return elements[elements.length - 1];
};
var clickableTags = /* @__PURE__ */ new Set(["A", "BUTTON", "INPUT", "SELECT", "TEXTAREA", "LABEL", "SUMMARY", "OPTION", "DETAILS", "VIDEO", "AUDIO"]);
var isClickable = (element) => {
if (element.hasAttribute("disabled") && ["", "true", null].includes(element.getAttribute("disabled"))) return false;
if (element.hasAttribute("readonly") && ["", "true", null].includes(element.getAttribute("readonly"))) return false;
if (element.tabIndex < 0) return false;
if (element.hasAttribute("role") && ["button", "link"].includes(_optionalChain([element, 'access', _5 => _5.getAttribute, 'call', _6 => _6("role"), 'optionalAccess', _7 => _7.toLowerCase, 'call', _8 => _8()]))) return true;
return clickableTags.has(element.tagName) || element.hasAttribute("onclick") || element.hasAttribute("onkeydown") || element.isContentEditable || element.tabIndex >= 0;
};
var getClickableAncestor = (element, boundary, selector = isClickable) => {
if (!element) return;
if (typeof boundary === "function" && boundary() === element) return;
if (boundary === element) return;
if (selector(element)) return element;
if (!element.parentElement) return;
return getClickableAncestor(element.parentElement, boundary, selector);
};
var clickableSelector = "a, button, input, textarea, select, details, summary, [role='button'], [role='link'], [onclick], audio, video, [tabindex]:not([tabindex='-1'])";
var closestClickableElement = (element) => element.closest(clickableSelector);
exports.focusableElementSelectors = focusableElementSelectors; exports.tabFocusableElementSelectors = tabFocusableElementSelectors; exports.getFocusableElement = getFocusableElement; exports.getFocusableElements = getFocusableElements; exports.getLastFocusableElement = getLastFocusableElement; exports.clickableTags = clickableTags; exports.isClickable = isClickable; exports.getClickableAncestor = getClickableAncestor; exports.clickableSelector = clickableSelector; exports.closestClickableElement = closestClickableElement;