-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
events.ts
127 lines (107 loc) · 3.79 KB
/
events.ts
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
127
import { refreshActiveHighlight } from "./highlight";
import { emit } from "./emitter";
import { getState, setState } from "./state";
import { getConfig } from "./config";
import { getFocusableElements } from "./utils";
export function requireRefresh() {
const resizeTimeout = getState("__resizeTimeout");
if (resizeTimeout) {
window.cancelAnimationFrame(resizeTimeout);
}
setState("__resizeTimeout", window.requestAnimationFrame(refreshActiveHighlight));
}
function trapFocus(e: KeyboardEvent) {
const isActivated = getState("isInitialized");
if (!isActivated) {
return;
}
const isTabKey = e.key === "Tab" || e.keyCode === 9;
if (!isTabKey) {
return;
}
const activeElement = getState("__activeElement");
const popoverEl = getState("popover")?.wrapper;
const focusableEls = getFocusableElements([
...(popoverEl ? [popoverEl] : []),
...(activeElement ? [activeElement] : []),
]);
const firstFocusableEl = focusableEls[0];
const lastFocusableEl = focusableEls[focusableEls.length - 1];
e.preventDefault();
if (e.shiftKey) {
const previousFocusableEl =
focusableEls[focusableEls.indexOf(document.activeElement as HTMLElement) - 1] || lastFocusableEl;
previousFocusableEl?.focus();
} else {
const nextFocusableEl =
focusableEls[focusableEls.indexOf(document.activeElement as HTMLElement) + 1] || firstFocusableEl;
nextFocusableEl?.focus();
}
}
function onKeyup(e: KeyboardEvent) {
const allowKeyboardControl = getConfig("allowKeyboardControl") ?? true;
if (!allowKeyboardControl) {
return;
}
if (e.key === "Escape") {
emit("escapePress");
} else if (e.key === "ArrowRight") {
emit("arrowRightPress");
} else if (e.key === "ArrowLeft") {
emit("arrowLeftPress");
}
}
/**
* Attaches click handler to the elements created by driver.js. It makes
* sure to give the listener the first chance to handle the event, and
* prevents all other pointer-events to make sure no external-library
* ever knows the click happened.
*
* @param {Element} element Element to listen for click events
* @param {(pointer: MouseEvent | PointerEvent) => void} listener Click handler
* @param {(target: HTMLElement) => boolean} shouldPreventDefault Whether to prevent default action i.e. link clicks etc
*/
export function onDriverClick(
element: Element,
listener: (pointer: MouseEvent | PointerEvent) => void,
shouldPreventDefault?: (target: HTMLElement) => boolean
) {
const listenerWrapper = (e: MouseEvent | PointerEvent, listener?: (pointer: MouseEvent | PointerEvent) => void) => {
const target = e.target as HTMLElement;
if (!element.contains(target)) {
return;
}
if (!shouldPreventDefault || shouldPreventDefault(target)) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
listener?.(e);
};
// We want to be the absolute first one to hear about the event
const useCapture = true;
// Events to disable
document.addEventListener("pointerdown", listenerWrapper, useCapture);
document.addEventListener("mousedown", listenerWrapper, useCapture);
document.addEventListener("pointerup", listenerWrapper, useCapture);
document.addEventListener("mouseup", listenerWrapper, useCapture);
// Actual click handler
document.addEventListener(
"click",
e => {
listenerWrapper(e, listener);
},
useCapture
);
}
export function initEvents() {
window.addEventListener("keyup", onKeyup, false);
window.addEventListener("keydown", trapFocus, false);
window.addEventListener("resize", requireRefresh);
window.addEventListener("scroll", requireRefresh);
}
export function destroyEvents() {
window.removeEventListener("keyup", onKeyup);
window.removeEventListener("resize", requireRefresh);
window.removeEventListener("scroll", requireRefresh);
}