-
-
Notifications
You must be signed in to change notification settings - Fork 902
/
Copy pathindex.ts
116 lines (98 loc) · 3.7 KB
/
index.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
import { ElementOrSelector } from "../../utils/resolve-elements"
import { isDragActive } from "../drag/state/is-active"
import { EventOptions } from "../types"
import { isNodeOrChild } from "../utils/is-node-or-child"
import { isPrimaryPointer } from "../utils/is-primary-pointer"
import { setupGesture } from "../utils/setup"
import { OnPressStartEvent } from "./types"
import { isElementKeyboardAccessible } from "./utils/is-keyboard-accessible"
import { enableKeyboardPress } from "./utils/keyboard"
import { isPressing } from "./utils/state"
/**
* Filter out events that are not primary pointer events, or are triggering
* while a Motion gesture is active.
*/
function isValidPressEvent(event: PointerEvent) {
return isPrimaryPointer(event) && !isDragActive()
}
export interface PointerEventOptions extends EventOptions {
useGlobalTarget?: boolean
}
/**
* Create a press gesture.
*
* Press is different to `"pointerdown"`, `"pointerup"` in that it
* automatically filters out secondary pointer events like right
* click and multitouch.
*
* It also adds accessibility support for keyboards, where
* an element with a press gesture will receive focus and
* trigger on Enter `"keydown"` and `"keyup"` events.
*
* This is different to a browser's `"click"` event, which does
* respond to keyboards but only for the `"click"` itself, rather
* than the press start and end/cancel. The element also needs
* to be focusable for this to work, whereas a press gesture will
* make an element focusable by default.
*
* @public
*/
export function press(
elementOrSelector: ElementOrSelector,
onPressStart: OnPressStartEvent,
options: PointerEventOptions = {}
): VoidFunction {
const [elements, eventOptions, cancelEvents] = setupGesture(
elementOrSelector,
options
)
const startPress = (startEvent: PointerEvent) => {
const element = startEvent.currentTarget as Element
if (!isValidPressEvent(startEvent) || isPressing.has(element)) return
isPressing.add(element)
const onPressEnd = onPressStart(element, startEvent)
const onPointerEnd = (endEvent: PointerEvent, success: boolean) => {
window.removeEventListener("pointerup", onPointerUp)
window.removeEventListener("pointercancel", onPointerCancel)
if (!isValidPressEvent(endEvent) || !isPressing.has(element)) {
return
}
isPressing.delete(element)
if (typeof onPressEnd === "function") {
onPressEnd(endEvent, { success })
}
}
const onPointerUp = (upEvent: PointerEvent) => {
onPointerEnd(
upEvent,
options.useGlobalTarget ||
isNodeOrChild(element, upEvent.target as Element)
)
}
const onPointerCancel = (cancelEvent: PointerEvent) => {
onPointerEnd(cancelEvent, false)
}
window.addEventListener("pointerup", onPointerUp, eventOptions)
window.addEventListener("pointercancel", onPointerCancel, eventOptions)
}
elements.forEach((element: Element) => {
if (
!isElementKeyboardAccessible(element) &&
element.getAttribute("tabindex") === null
) {
;(element as HTMLElement).tabIndex = 0
}
const target = options.useGlobalTarget ? window : element
target.addEventListener(
"pointerdown",
startPress as EventListener,
eventOptions
)
element.addEventListener(
"focus",
(event) => enableKeyboardPress(event as FocusEvent, eventOptions),
eventOptions
)
})
return cancelEvents
}