element-plus
Version:
A Component Library for Vue 3
1 lines • 7.28 kB
Source Map (JSON)
{"version":3,"file":"aria.mjs","sources":["../../../../packages/utils/aria.ts"],"sourcesContent":["import type { Nullable } from '@element-plus/utils/types'\nexport const EVENT_CODE = {\n tab: 'Tab',\n enter: 'Enter',\n space: 'Space',\n left: 'ArrowLeft', // 37\n up: 'ArrowUp', // 38\n right: 'ArrowRight', // 39\n down: 'ArrowDown', // 40\n esc: 'Escape',\n delete: 'Delete',\n backspace: 'Backspace',\n}\n\nconst 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])`\n\n/**\n * Determine if the testing element is visible on screen no matter if its on the viewport or not\n */\nexport const isVisible = (element: HTMLElement) => {\n if (process.env.NODE_ENV === 'test') return true\n const computed = getComputedStyle(element)\n // element.offsetParent won't work on fix positioned\n // WARNING: potential issue here, going to need some expert advices on this issue\n return computed.position === 'fixed' ? false : element.offsetParent !== null\n}\n\nexport const obtainAllFocusableElements = (\n element: HTMLElement\n): HTMLElement[] => {\n return Array.from(\n element.querySelectorAll<HTMLElement>(FOCUSABLE_ELEMENT_SELECTORS)\n ).filter((item: HTMLElement) => isFocusable(item) && isVisible(item))\n}\n\n/**\n * @desc Determine if target element is focusable\n * @param element {HTMLElement}\n * @returns {Boolean} true if it is focusable\n */\nexport const isFocusable = (element: HTMLElement): boolean => {\n if (\n element.tabIndex > 0 ||\n (element.tabIndex === 0 && element.getAttribute('tabIndex') !== null)\n ) {\n return true\n }\n // HTMLButtonElement has disabled\n if ((element as HTMLButtonElement).disabled) {\n return false\n }\n\n switch (element.nodeName) {\n case 'A': {\n // casting current element to Specific HTMLElement in order to be more type precise\n return (\n !!(element as HTMLAnchorElement).href &&\n (element as HTMLAnchorElement).rel !== 'ignore'\n )\n }\n case 'INPUT': {\n return !(\n (element as HTMLInputElement).type === 'hidden' ||\n (element as HTMLInputElement).type === 'file'\n )\n }\n case 'BUTTON':\n case 'SELECT':\n case 'TEXTAREA': {\n return true\n }\n default: {\n return false\n }\n }\n}\n\n/**\n * @desc Set Attempt to set focus on the current node.\n * @param element\n * The node to attempt to focus on.\n * @returns\n * true if element is focused.\n */\nexport const attemptFocus = (element: HTMLElement): boolean => {\n if (!isFocusable(element)) {\n return false\n }\n Utils.IgnoreUtilFocusChanges = true\n // Remove the old try catch block since there will be no error to be thrown\n element.focus?.()\n Utils.IgnoreUtilFocusChanges = false\n return document.activeElement === element\n}\n\n/**\n * Trigger an event\n * mouseenter, mouseleave, mouseover, keyup, change, click, etc.\n * @param {HTMLElement} elm\n * @param {String} name\n * @param {*} opts\n */\nexport const triggerEvent = function (\n elm: HTMLElement,\n name: string,\n ...opts: Array<boolean>\n): HTMLElement {\n let eventName: string\n\n if (name.includes('mouse') || name.includes('click')) {\n eventName = 'MouseEvents'\n } else if (name.includes('key')) {\n eventName = 'KeyboardEvent'\n } else {\n eventName = 'HTMLEvents'\n }\n const evt = document.createEvent(eventName)\n\n evt.initEvent(name, ...opts)\n elm.dispatchEvent(evt)\n return elm\n}\n\nexport const isLeaf = (el: HTMLElement) => !el.getAttribute('aria-owns')\n\nexport const getSibling = (\n el: HTMLElement,\n distance: number,\n elClass: string\n): Nullable<Element> => {\n const { parentNode } = el\n if (!parentNode) return null\n const siblings = parentNode.querySelectorAll(elClass)\n const index = Array.prototype.indexOf.call(siblings, el)\n return siblings[index + distance] || null\n}\n\nexport const focusNode = (el) => {\n if (!el) return\n el.focus()\n !isLeaf(el) && el.click()\n}\n\nconst Utils = {\n IgnoreUtilFocusChanges: false,\n /**\n * @desc Set focus on descendant nodes until the first focusable element is\n * found.\n * @param {HTMLElement} element\n * DOM node for which to find the first focusable descendant.\n * @returns {Boolean}\n * true if a focusable element is found and focus is set.\n */\n focusFirstDescendant(element: HTMLElement): boolean {\n for (let i = 0; i < element.childNodes.length; i++) {\n const child = element.childNodes[i] as HTMLElement\n if (attemptFocus(child) || this.focusFirstDescendant(child)) {\n return true\n }\n }\n return false\n },\n /**\n * @desc Find the last descendant node that is focusable.\n * @param {HTMLElement} element\n * DOM node for which to find the last focusable descendant.\n * @returns {Boolean}\n * true if a focusable element is found and focus is set.\n */\n focusLastDescendant(element: HTMLElement): boolean {\n for (let i = element.childNodes.length - 1; i >= 0; i--) {\n const child = element.childNodes[i] as HTMLElement\n if (attemptFocus(child) || this.focusLastDescendant(child)) {\n return true\n }\n }\n return false\n },\n}\n\nexport default Utils\n"],"names":[],"mappings":"MACa,aAAa;AAAA,EACxB,KAAK;AAAA,EACL,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,WAAW;AAAA;AAGb,MAAM,8BAA8B;MAKvB,YAAY,CAAC,YAAyB;AACjD,MAAI,QAAQ,IAAI,aAAa;AAAQ,WAAO;AAC5C,QAAM,WAAW,iBAAiB;AAGlC,SAAO,SAAS,aAAa,UAAU,QAAQ,QAAQ,iBAAiB;AAAA;MAG7D,6BAA6B,CACxC,YACkB;AAClB,SAAO,MAAM,KACX,QAAQ,iBAA8B,8BACtC,OAAO,CAAC,SAAsB,YAAY,SAAS,UAAU;AAAA;MAQpD,cAAc,CAAC,YAAkC;AAC5D,MACE,QAAQ,WAAW,KAClB,QAAQ,aAAa,KAAK,QAAQ,aAAa,gBAAgB,MAChE;AACA,WAAO;AAAA;AAGT,MAAK,QAA8B,UAAU;AAC3C,WAAO;AAAA;AAGT,UAAQ,QAAQ;AAAA,SACT,KAAK;AAER,aACE,CAAC,CAAE,QAA8B,QAChC,QAA8B,QAAQ;AAAA;AAAA,SAGtC,SAAS;AACZ,aAAO,UACyB,SAAS,YACtC,QAA6B,SAAS;AAAA;AAAA,SAGtC;AAAA,SACA;AAAA,SACA,YAAY;AACf,aAAO;AAAA;AAAA,aAEA;AACP,aAAO;AAAA;AAAA;AAAA;MAYA,eAAe,CAAC,YAAkC;AApF/D;AAqFE,MAAI,CAAC,YAAY,UAAU;AACzB,WAAO;AAAA;AAET,QAAM,yBAAyB;AAE/B,gBAAQ,UAAR;AACA,QAAM,yBAAyB;AAC/B,SAAO,SAAS,kBAAkB;AAAA;MAUvB,eAAe,SAC1B,KACA,SACG,MACU;AACb,MAAI;AAEJ,MAAI,KAAK,SAAS,YAAY,KAAK,SAAS,UAAU;AACpD,gBAAY;AAAA,aACH,KAAK,SAAS,QAAQ;AAC/B,gBAAY;AAAA,SACP;AACL,gBAAY;AAAA;AAEd,QAAM,MAAM,SAAS,YAAY;AAEjC,MAAI,UAAU,MAAM,GAAG;AACvB,MAAI,cAAc;AAClB,SAAO;AAAA;MAGI,SAAS,CAAC,OAAoB,CAAC,GAAG,aAAa;MAE/C,aAAa,CACxB,IACA,UACA,YACsB;AACtB,QAAM,EAAE,eAAe;AACvB,MAAI,CAAC;AAAY,WAAO;AACxB,QAAM,WAAW,WAAW,iBAAiB;AAC7C,QAAM,QAAQ,MAAM,UAAU,QAAQ,KAAK,UAAU;AACrD,SAAO,SAAS,QAAQ,aAAa;AAAA;MAG1B,YAAY,CAAC,OAAO;AAC/B,MAAI,CAAC;AAAI;AACT,KAAG;AACH,GAAC,OAAO,OAAO,GAAG;AAAA;MAGd,QAAQ;AAAA,EACZ,wBAAwB;AAAA,EASxB,qBAAqB,SAA+B;AAClD,aAAS,IAAI,GAAG,IAAI,QAAQ,WAAW,QAAQ,KAAK;AAClD,YAAM,QAAQ,QAAQ,WAAW;AACjC,UAAI,aAAa,UAAU,KAAK,qBAAqB,QAAQ;AAC3D,eAAO;AAAA;AAAA;AAGX,WAAO;AAAA;AAAA,EAST,oBAAoB,SAA+B;AACjD,aAAS,IAAI,QAAQ,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AACvD,YAAM,QAAQ,QAAQ,WAAW;AACjC,UAAI,aAAa,UAAU,KAAK,oBAAoB,QAAQ;AAC1D,eAAO;AAAA;AAAA;AAGX,WAAO;AAAA;AAAA;;;;"}