UNPKG

element-plus

Version:
149 lines (148 loc) 5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.triggerEvent = exports.attemptFocus = exports.isFocusable = exports.obtainAllFocusableElements = exports.isVisible = exports.EVENT_CODE = void 0; exports.EVENT_CODE = { tab: 'Tab', enter: 'Enter', space: 'Space', left: 'ArrowLeft', up: 'ArrowUp', right: 'ArrowRight', down: 'ArrowDown', esc: 'Escape', delete: 'Delete', backspace: 'Backspace', }; const FOCUSABLE_ELEMENT_SELECTORS = `a[href],button:not([disabled]),button:not([hidden]),:not([tabindex="-1"]),input:not([disabled]),input:not([type="hidden"]),select:not([disabled]),textarea:not([disabled])`; /** * Determine if the testing element is visible on screen no matter if its on the viewport or not */ const isVisible = (element) => { if (process.env.NODE_ENV === 'test') return true; const computed = getComputedStyle(element); // element.offsetParent won't work on fix positioned // WARNING: potential issue here, going to need some expert advices on this issue return computed.position === 'fixed' ? false : element.offsetParent !== null; }; exports.isVisible = isVisible; const obtainAllFocusableElements = (element) => { return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENT_SELECTORS)).filter(exports.isFocusable) .filter(exports.isVisible); }; exports.obtainAllFocusableElements = obtainAllFocusableElements; /** * @desc Determine if target element is focusable * @param element {HTMLElement} * @returns {Boolean} true if it is focusable */ const isFocusable = (element) => { if (element.tabIndex > 0 || (element.tabIndex === 0 && element.getAttribute('tabIndex') !== null)) { return true; } // HTMLButtonElement has disabled if (element.disabled) { return false; } switch (element.nodeName) { case 'A': { // casting current element to Specific HTMLElement in order to be more type precise return !!element.href && element.rel !== 'ignore'; } case 'INPUT': { return !(element.type === 'hidden' || element.type === 'file'); } case 'BUTTON': case 'SELECT': case 'TEXTAREA': { return true; } default: { return false; } } }; exports.isFocusable = isFocusable; /** * @desc Set Attempt to set focus on the current node. * @param element * The node to attempt to focus on. * @returns * true if element is focused. */ const attemptFocus = (element) => { var _a; if (!exports.isFocusable(element)) { return false; } Utils.IgnoreUtilFocusChanges = true; // Remove the old try catch block since there will be no error to be thrown (_a = element.focus) === null || _a === void 0 ? void 0 : _a.call(element); Utils.IgnoreUtilFocusChanges = false; return document.activeElement === element; }; exports.attemptFocus = attemptFocus; /** * Trigger an event * mouseenter, mouseleave, mouseover, keyup, change, click, etc. * @param {HTMLElement} elm * @param {String} name * @param {*} opts */ const triggerEvent = function (elm, name, ...opts) { let eventName; if (name.includes('mouse') || name.includes('click')) { eventName = 'MouseEvents'; } else if (name.includes('key')) { eventName = 'KeyboardEvent'; } else { eventName = 'HTMLEvents'; } const evt = document.createEvent(eventName); evt.initEvent(name, ...opts); elm.dispatchEvent(evt); return elm; }; exports.triggerEvent = triggerEvent; const Utils = { IgnoreUtilFocusChanges: false, /** * @desc Set focus on descendant nodes until the first focusable element is * found. * @param {HTMLElement} element * DOM node for which to find the first focusable descendant. * @returns {Boolean} * true if a focusable element is found and focus is set. */ focusFirstDescendant: function (element) { for (let i = 0; i < element.childNodes.length; i++) { const child = element.childNodes[i]; if (exports.attemptFocus(child) || this.focusFirstDescendant(child)) { return true; } } return false; }, /** * @desc Find the last descendant node that is focusable. * @param {HTMLElement} element * DOM node for which to find the last focusable descendant. * @returns {Boolean} * true if a focusable element is found and focus is set. */ focusLastDescendant: function (element) { for (let i = element.childNodes.length - 1; i >= 0; i--) { const child = element.childNodes[i]; if (exports.attemptFocus(child) || this.focusLastDescendant(child)) { return true; } } return false; }, }; exports.default = Utils;