2020-07-28 20:31:26 +08:00
|
|
|
export const eventKeys = {
|
|
|
|
tab: 9,
|
|
|
|
enter: 13,
|
|
|
|
space: 32,
|
|
|
|
left: 37,
|
|
|
|
up: 38,
|
|
|
|
right: 39,
|
|
|
|
down: 40,
|
|
|
|
esc: 27,
|
2020-08-04 19:03:20 +08:00
|
|
|
backspace: 8,
|
|
|
|
delete: 46,
|
2020-07-28 20:31:26 +08:00
|
|
|
}
|
2020-07-28 17:48:40 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @desc Determine if target element is focusable
|
|
|
|
* @param element {HTMLElement}
|
|
|
|
* @returns {Boolean} true if it is focusable
|
|
|
|
*/
|
2020-07-28 20:31:26 +08:00
|
|
|
export const isFocusable = (element: HTMLElement): boolean => {
|
2020-07-28 17:48:40 +08:00
|
|
|
if (
|
|
|
|
element.tabIndex > 0 ||
|
|
|
|
(element.tabIndex === 0 && element.getAttribute('tabIndex') !== null)
|
|
|
|
) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// HTMLButtonElement has disabled
|
|
|
|
if ((element as HTMLButtonElement).disabled) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (element.nodeName) {
|
|
|
|
case 'A': {
|
|
|
|
// casting current element to Specific HTMLElement in order to be more type precise
|
|
|
|
return !!(element as HTMLAnchorElement).href && (element as HTMLAnchorElement).rel !== 'ignore'
|
|
|
|
}
|
|
|
|
case 'INPUT':{
|
2020-07-28 20:31:26 +08:00
|
|
|
return !((element as HTMLInputElement).type === 'hidden' || (element as HTMLInputElement).type === 'file')
|
2020-07-28 17:48:40 +08:00
|
|
|
}
|
|
|
|
case 'BUTTON':
|
|
|
|
case 'SELECT':
|
|
|
|
case 'TEXTAREA': {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*/
|
2020-07-28 20:31:26 +08:00
|
|
|
export const attemptFocus = (element: HTMLElement): boolean => {
|
2020-07-28 17:48:40 +08:00
|
|
|
if (!isFocusable(element)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
Utils.IgnoreUtilFocusChanges = true
|
|
|
|
// Remove the old try catch block since there will be no error to be thrown
|
|
|
|
element.focus && element.focus()
|
|
|
|
Utils.IgnoreUtilFocusChanges = false
|
|
|
|
return document.activeElement === element
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger an event
|
|
|
|
* mouseenter, mouseleave, mouseover, keyup, change, click, etc.
|
|
|
|
* @param {HTMLElement} elm
|
|
|
|
* @param {String} name
|
|
|
|
* @param {*} opts
|
|
|
|
*/
|
2020-07-28 20:31:26 +08:00
|
|
|
export const triggerEvent = function(elm: HTMLElement, name: string, ...opts: Array<boolean>): HTMLElement {
|
2020-07-28 17:48:40 +08:00
|
|
|
let eventName: string
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
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: HTMLElement): boolean {
|
|
|
|
for (let i = 0; i < element.childNodes.length; i++) {
|
|
|
|
const child = element.childNodes[i]
|
|
|
|
if (
|
|
|
|
attemptFocus(child as HTMLElement) ||
|
|
|
|
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: HTMLElement): boolean {
|
|
|
|
for (let i = element.childNodes.length - 1; i >= 0; i--) {
|
|
|
|
const child = element.childNodes[i]
|
|
|
|
if (
|
|
|
|
attemptFocus(child as HTMLElement) ||
|
|
|
|
this.focusLastDescendant(child)
|
|
|
|
) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Utils
|