forked from ai/keyux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hotkey.js
126 lines (111 loc) · 3.07 KB
/
hotkey.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
let NON_ENGLISH_LAYOUT = /^[^\x00-\x7F]$/
let IGNORE_INPUTS = {
checkbox: true,
file: true,
radio: true
}
let CLICK_INPUTS = {
button: true,
reset: true,
submit: true
}
let KEY_REPLACERS = {
' ': 'space',
'+': 'plus'
}
function isInsideIgnored(parent, node) {
if (node.tagName !== 'BODY' && parent !== node) {
if (
node.hasAttribute('data-keyux-ignore-hotkeys') ||
node.getAttribute('aria-hidden') === 'true' ||
node.hasAttribute('inert')
) {
return true
} else {
return isInsideIgnored(parent, node.parentNode)
}
}
}
function findNonIgnored(activeElement, elements) {
for (let element of elements) {
if (isInsideIgnored(activeElement, element)) continue
return element
}
}
function checkHotkey(window, code, transformers) {
let realCode = code
for (let [transform] of transformers) {
realCode = transform(realCode, window)
if (!realCode) return false
}
let where = window.document
let activeElement = where.activeElement
let areaId = activeElement.getAttribute('data-keyux-hotkeys')
if (areaId) {
let area = where.querySelector(`#${areaId}`)
if (area) {
let element = area.querySelector(`[aria-keyshortcuts="${realCode}" i]`)
if (element) return element
}
}
return (
findNonIgnored(
activeElement,
activeElement.querySelectorAll(`[aria-keyshortcuts="${realCode}" i]`)
) ||
findNonIgnored(
where,
where.querySelectorAll(`[aria-keyshortcuts="${realCode}" i]`)
)
)
}
function findHotKey(event, window, transformers) {
let prefix = ''
if (event.metaKey) prefix += 'meta+'
if (event.ctrlKey) prefix += 'ctrl+'
if (event.altKey) prefix += 'alt+'
if (event.shiftKey) prefix += 'shift+'
let code = prefix
code += KEY_REPLACERS[event.key] ?? event.key.toLowerCase()
let hotkey = checkHotkey(window, code, transformers)
if (
!hotkey &&
NON_ENGLISH_LAYOUT.test(event.key) &&
/^Key.$/.test(event.code)
) {
let enKey = event.code.replace(/^Key/, '').toLowerCase()
hotkey = checkHotkey(window, prefix + enKey, transformers)
}
return hotkey
}
export function hotkeyKeyUX(transformers = []) {
return window => {
function keyDown(event) {
let isSpecialKey = event.ctrlKey || event.metaKey || event.altKey
let insideEditable =
event.target.isContentEditable ||
event.target.tagName === 'TEXTAREA' ||
(event.target.tagName === 'INPUT' && !IGNORE_INPUTS[event.target.type])
let insideFocusGroup = event.target.role === 'menuitem'
if (!isSpecialKey && (insideEditable || insideFocusGroup)) {
return
}
let active = findHotKey(event, window, transformers)
if (!active) return
if (
active.tagName === 'TEXTAREA' ||
(active.tagName === 'INPUT' && !CLICK_INPUTS[active.type])
) {
setTimeout(() => {
active.focus()
})
} else {
active.click()
}
}
window.addEventListener('keydown', keyDown)
return () => {
window.removeEventListener('keydown', keyDown)
}
}
}