@tamagui/react-native-use-pressable 1.0.1-beta.194

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,591 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ * @format
9
+ */
10
+ 'use strict'
11
+
12
+ const DELAY = 'DELAY'
13
+ const ERROR = 'ERROR'
14
+ const LONG_PRESS_DETECTED = 'LONG_PRESS_DETECTED'
15
+ const NOT_RESPONDER = 'NOT_RESPONDER'
16
+ const RESPONDER_ACTIVE_LONG_PRESS_START = 'RESPONDER_ACTIVE_LONG_PRESS_START'
17
+ const RESPONDER_ACTIVE_PRESS_START = 'RESPONDER_ACTIVE_PRESS_START'
18
+ const RESPONDER_INACTIVE_PRESS_START = 'RESPONDER_INACTIVE_PRESS_START'
19
+ const RESPONDER_GRANT = 'RESPONDER_GRANT'
20
+ const RESPONDER_RELEASE = 'RESPONDER_RELEASE'
21
+ const RESPONDER_TERMINATED = 'RESPONDER_TERMINATED'
22
+ const Transitions = Object.freeze({
23
+ NOT_RESPONDER: {
24
+ DELAY: ERROR,
25
+ RESPONDER_GRANT: RESPONDER_INACTIVE_PRESS_START,
26
+ RESPONDER_RELEASE: ERROR,
27
+ RESPONDER_TERMINATED: ERROR,
28
+ LONG_PRESS_DETECTED: ERROR,
29
+ },
30
+ RESPONDER_INACTIVE_PRESS_START: {
31
+ DELAY: RESPONDER_ACTIVE_PRESS_START,
32
+ RESPONDER_GRANT: ERROR,
33
+ RESPONDER_RELEASE: NOT_RESPONDER,
34
+ RESPONDER_TERMINATED: NOT_RESPONDER,
35
+ LONG_PRESS_DETECTED: ERROR,
36
+ },
37
+ RESPONDER_ACTIVE_PRESS_START: {
38
+ DELAY: ERROR,
39
+ RESPONDER_GRANT: ERROR,
40
+ RESPONDER_RELEASE: NOT_RESPONDER,
41
+ RESPONDER_TERMINATED: NOT_RESPONDER,
42
+ LONG_PRESS_DETECTED: RESPONDER_ACTIVE_LONG_PRESS_START,
43
+ },
44
+ RESPONDER_ACTIVE_LONG_PRESS_START: {
45
+ DELAY: ERROR,
46
+ RESPONDER_GRANT: ERROR,
47
+ RESPONDER_RELEASE: NOT_RESPONDER,
48
+ RESPONDER_TERMINATED: NOT_RESPONDER,
49
+ LONG_PRESS_DETECTED: RESPONDER_ACTIVE_LONG_PRESS_START,
50
+ },
51
+ ERROR: {
52
+ DELAY: NOT_RESPONDER,
53
+ RESPONDER_GRANT: RESPONDER_INACTIVE_PRESS_START,
54
+ RESPONDER_RELEASE: NOT_RESPONDER,
55
+ RESPONDER_TERMINATED: NOT_RESPONDER,
56
+ LONG_PRESS_DETECTED: NOT_RESPONDER,
57
+ },
58
+ })
59
+
60
+ const isActiveSignal = (signal) =>
61
+ signal === RESPONDER_ACTIVE_PRESS_START || signal === RESPONDER_ACTIVE_LONG_PRESS_START
62
+
63
+ const isButtonRole = (element) => element.getAttribute('role') === 'button'
64
+
65
+ const isPressStartSignal = (signal) =>
66
+ signal === RESPONDER_INACTIVE_PRESS_START ||
67
+ signal === RESPONDER_ACTIVE_PRESS_START ||
68
+ signal === RESPONDER_ACTIVE_LONG_PRESS_START
69
+
70
+ const isTerminalSignal = (signal) => signal === RESPONDER_TERMINATED || signal === RESPONDER_RELEASE
71
+
72
+ const isValidKeyPress = (event) => {
73
+ const key = event.key,
74
+ target = event.target
75
+ const role = target.getAttribute('role')
76
+ const isSpacebar = key === ' ' || key === 'Spacebar'
77
+ return key === 'Enter' || (isSpacebar && role === 'button')
78
+ }
79
+
80
+ const DEFAULT_LONG_PRESS_DELAY_MS = 450 // 500 - 50
81
+
82
+ const DEFAULT_PRESS_DELAY_MS = 50
83
+ /**
84
+ * =========================== PressResponder Tutorial ===========================
85
+ *
86
+ * The `PressResponder` class helps you create press interactions by analyzing the
87
+ * geometry of elements and observing when another responder (e.g. ScrollView)
88
+ * has stolen the touch lock. It offers hooks for your component to provide
89
+ * interaction feedback to the user:
90
+ *
91
+ * - When a press has activated (e.g. highlight an element)
92
+ * - When a press has deactivated (e.g. un-highlight an element)
93
+ * - When a press sould trigger an action, meaning it activated and deactivated
94
+ * while within the geometry of the element without the lock being stolen.
95
+ *
96
+ * A high quality interaction isn't as simple as you might think. There should
97
+ * be a slight delay before activation. Moving your finger beyond an element's
98
+ * bounds should trigger deactivation, but moving the same finger back within an
99
+ * element's bounds should trigger reactivation.
100
+ *
101
+ * In order to use `PressResponder`, do the following:
102
+ *
103
+ * const pressResponder = new PressResponder(config);
104
+ *
105
+ * 2. Choose the rendered component who should collect the press events. On that
106
+ * element, spread `pressability.getEventHandlers()` into its props.
107
+ *
108
+ * return (
109
+ * <View {...this.state.pressResponder.getEventHandlers()} />
110
+ * );
111
+ *
112
+ * 3. Reset `PressResponder` when your component unmounts.
113
+ *
114
+ * componentWillUnmount() {
115
+ * this.state.pressResponder.reset();
116
+ * }
117
+ *
118
+ * ==================== Implementation Details ====================
119
+ *
120
+ * `PressResponder` only assumes that there exists a `HitRect` node. The `PressRect`
121
+ * is an abstract box that is extended beyond the `HitRect`.
122
+ *
123
+ * # Geometry
124
+ *
125
+ * ┌────────────────────────┐
126
+ * │ ┌──────────────────┐ │ - Presses start anywhere within `HitRect`.
127
+ * │ │ ┌────────────┐ │ │
128
+ * │ │ │ VisualRect │ │ │
129
+ * │ │ └────────────┘ │ │ - When pressed down for sufficient amount of time
130
+ * │ │ HitRect │ │ before letting up, `VisualRect` activates.
131
+ * │ └──────────────────┘ │
132
+ * │ Out Region o │
133
+ * └────────────────────│───┘
134
+ * └────── When the press is released outside the `HitRect`,
135
+ * the responder is NOT eligible for a "press".
136
+ *
137
+ * # State Machine
138
+ *
139
+ * ┌───────────────┐ ◀──── RESPONDER_RELEASE
140
+ * │ NOT_RESPONDER │
141
+ * └───┬───────────┘ ◀──── RESPONDER_TERMINATED
142
+ * │
143
+ * │ RESPONDER_GRANT (HitRect)
144
+ * │
145
+ * ▼
146
+ * ┌─────────────────────┐ ┌───────────────────┐ ┌───────────────────┐
147
+ * │ RESPONDER_INACTIVE_ │ DELAY │ RESPONDER_ACTIVE_ │ T + DELAY │ RESPONDER_ACTIVE_ │
148
+ * │ PRESS_START ├────────▶ │ PRESS_START ├────────────▶ │ LONG_PRESS_START │
149
+ * └─────────────────────┘ └───────────────────┘ └───────────────────┘
150
+ *
151
+ * T + DELAY => LONG_PRESS_DELAY + DELAY
152
+ *
153
+ * Not drawn are the side effects of each transition. The most important side
154
+ * effect is the invocation of `onLongPress`. Only when the browser produces a
155
+ * `click` event is `onPress` invoked.
156
+ */
157
+
158
+ export default class PressResponder {
159
+ _touchActivatePosition = null as any
160
+ _pressDelayTimeout = 0 as any
161
+ _selectionTerminated = false
162
+ _isPointerTouch = false
163
+ _longPressDelayTimeout = 0 as any
164
+ _longPressDispatched = false
165
+ _pressOutDelayTimeout = 0 as any
166
+ _touchState = NOT_RESPONDER
167
+ _config = null as any
168
+ _eventHandlers = null as any
169
+
170
+ constructor(config) {
171
+ this.configure(config)
172
+ }
173
+
174
+ configure(config) {
175
+ this._config = config
176
+ }
177
+ /**
178
+ * Resets any pending timers. This should be called on unmount.
179
+ */
180
+
181
+ reset() {
182
+ this._cancelLongPressDelayTimeout()
183
+
184
+ this._cancelPressDelayTimeout()
185
+
186
+ this._cancelPressOutDelayTimeout()
187
+ }
188
+ /**
189
+ * Returns a set of props to spread into the interactive element.
190
+ */
191
+
192
+ getEventHandlers() {
193
+ if (this._eventHandlers == null) {
194
+ this._eventHandlers = this._createEventHandlers()
195
+ }
196
+
197
+ return this._eventHandlers
198
+ }
199
+
200
+ _createEventHandlers() {
201
+ const start = (event, shouldDelay?: boolean) => {
202
+ event.persist()
203
+
204
+ this._cancelPressOutDelayTimeout()
205
+
206
+ this._longPressDispatched = false
207
+ this._selectionTerminated = false
208
+ this._touchState = NOT_RESPONDER
209
+ this._isPointerTouch = event.nativeEvent.type === 'touchstart'
210
+
211
+ this._receiveSignal(RESPONDER_GRANT, event)
212
+
213
+ const delayPressStart = normalizeDelay(
214
+ this._config.delayPressStart,
215
+ 0,
216
+ DEFAULT_PRESS_DELAY_MS
217
+ )
218
+
219
+ if (shouldDelay !== false && delayPressStart > 0) {
220
+ this._pressDelayTimeout = setTimeout(() => {
221
+ this._receiveSignal(DELAY, event)
222
+ }, delayPressStart)
223
+ } else {
224
+ this._receiveSignal(DELAY, event)
225
+ }
226
+
227
+ const delayLongPress = normalizeDelay(
228
+ this._config.delayLongPress,
229
+ 10,
230
+ DEFAULT_LONG_PRESS_DELAY_MS
231
+ )
232
+ this._longPressDelayTimeout = setTimeout(() => {
233
+ this._handleLongPress(event)
234
+ }, delayLongPress + delayPressStart)
235
+ }
236
+
237
+ const end = (event) => {
238
+ this._receiveSignal(RESPONDER_RELEASE, event)
239
+ }
240
+
241
+ const keyupHandler = (event) => {
242
+ const onPress = this._config.onPress
243
+ const target = event.target
244
+
245
+ if (this._touchState !== NOT_RESPONDER && isValidKeyPress(event)) {
246
+ end(event)
247
+ document.removeEventListener('keyup', keyupHandler)
248
+ const role = target.getAttribute('role')
249
+ const elementType = target.tagName.toLowerCase()
250
+ const isNativeInteractiveElement =
251
+ role === 'link' ||
252
+ elementType === 'a' ||
253
+ elementType === 'button' ||
254
+ elementType === 'input' ||
255
+ elementType === 'select' ||
256
+ elementType === 'textarea'
257
+
258
+ if (onPress != null && !isNativeInteractiveElement) {
259
+ onPress(event)
260
+ }
261
+ }
262
+ }
263
+
264
+ return {
265
+ onStartShouldSetResponder: (event) => {
266
+ const disabled = this._config.disabled
267
+
268
+ if (disabled && isButtonRole(event.currentTarget)) {
269
+ event.stopPropagation()
270
+ }
271
+
272
+ if (disabled == null) {
273
+ return true
274
+ }
275
+
276
+ return !disabled
277
+ },
278
+ onKeyDown: (event) => {
279
+ const disabled = this._config.disabled
280
+ const key = event.key,
281
+ target = event.target
282
+
283
+ if (!disabled && isValidKeyPress(event)) {
284
+ if (this._touchState === NOT_RESPONDER) {
285
+ start(event, false) // Listen to 'keyup' on document to account for situations where
286
+ // focus is moved to another element during 'keydown'.
287
+
288
+ document.addEventListener('keyup', keyupHandler)
289
+ }
290
+
291
+ const role = target.getAttribute('role')
292
+ const isSpacebarKey = key === ' ' || key === 'Spacebar'
293
+
294
+ const _isButtonRole = role === 'button' || role === 'menuitem'
295
+
296
+ if (isSpacebarKey && _isButtonRole) {
297
+ // Prevent spacebar scrolling the window
298
+ event.preventDefault()
299
+ }
300
+
301
+ event.stopPropagation()
302
+ }
303
+ },
304
+ onResponderGrant: (event) => start(event),
305
+ onResponderMove: (event) => {
306
+ if (this._config.onPressMove != null) {
307
+ this._config.onPressMove(event)
308
+ }
309
+
310
+ const touch = getTouchFromResponderEvent(event)
311
+
312
+ if (this._touchActivatePosition != null) {
313
+ const deltaX = this._touchActivatePosition.pageX - touch.pageX
314
+ const deltaY = this._touchActivatePosition.pageY - touch.pageY
315
+
316
+ if (Math.hypot(deltaX, deltaY) > 10) {
317
+ this._cancelLongPressDelayTimeout()
318
+ }
319
+ }
320
+ },
321
+ onResponderRelease: (event) => end(event),
322
+ onResponderTerminate: (event) => {
323
+ if (event.nativeEvent.type === 'selectionchange') {
324
+ this._selectionTerminated = true
325
+ }
326
+
327
+ this._receiveSignal(RESPONDER_TERMINATED, event)
328
+ },
329
+ onResponderTerminationRequest: (event) => {
330
+ const _this$_config = this._config,
331
+ cancelable = _this$_config.cancelable,
332
+ disabled = _this$_config.disabled,
333
+ onLongPress = _this$_config.onLongPress // If `onLongPress` is provided, don't terminate on `contextmenu` as default
334
+ // behavior will be prevented for non-mouse pointers.
335
+
336
+ if (
337
+ !disabled &&
338
+ onLongPress != null &&
339
+ this._isPointerTouch &&
340
+ event.nativeEvent.type === 'contextmenu'
341
+ ) {
342
+ return false
343
+ }
344
+
345
+ if (cancelable == null) {
346
+ return true
347
+ }
348
+
349
+ return cancelable
350
+ },
351
+ // NOTE: this diverges from react-native in 3 significant ways:
352
+ // * The `onPress` callback is not connected to the responder system (the native
353
+ // `click` event must be used but is dispatched in many scenarios where no pointers
354
+ // are on the screen.) Therefore, it's possible for `onPress` to be called without
355
+ // `onPress{Start,End}` being called first.
356
+ // * The `onPress` callback is only be called on the first ancestor of the native
357
+ // `click` target that is using the PressResponder.
358
+ // * The event's `nativeEvent` is a `MouseEvent` not a `TouchEvent`.
359
+ onClick: (event) => {
360
+ const _this$_config2 = this._config,
361
+ disabled = _this$_config2.disabled,
362
+ onPress = _this$_config2.onPress
363
+
364
+ if (!disabled) {
365
+ // If long press dispatched, cancel default click behavior.
366
+ // If the responder terminated because text was selected during the gesture,
367
+ // cancel the default click behavior.
368
+ event.stopPropagation()
369
+
370
+ if (this._longPressDispatched || this._selectionTerminated) {
371
+ event.preventDefault()
372
+ } else if (onPress != null && event.altKey === false) {
373
+ onPress(event)
374
+ }
375
+ } else {
376
+ if (isButtonRole(event.currentTarget)) {
377
+ event.stopPropagation()
378
+ }
379
+ }
380
+ },
381
+ // If `onLongPress` is provided and a touch pointer is being used, prevent the
382
+ // default context menu from opening.
383
+ onContextMenu: (event) => {
384
+ const _this$_config3 = this._config,
385
+ disabled = _this$_config3.disabled,
386
+ onLongPress = _this$_config3.onLongPress
387
+
388
+ if (!disabled) {
389
+ if (onLongPress != null && this._isPointerTouch && !event.defaultPrevented) {
390
+ event.preventDefault()
391
+ event.stopPropagation()
392
+ }
393
+ } else {
394
+ if (isButtonRole(event.currentTarget)) {
395
+ event.stopPropagation()
396
+ }
397
+ }
398
+ },
399
+ }
400
+ }
401
+ /**
402
+ * Receives a state machine signal, performs side effects of the transition
403
+ * and stores the new state. Validates the transition as well.
404
+ */
405
+
406
+ _receiveSignal(signal, event) {
407
+ const prevState = this._touchState
408
+ let nextState = null
409
+
410
+ if (Transitions[prevState] != null) {
411
+ nextState = Transitions[prevState][signal]
412
+ }
413
+
414
+ if (this._touchState === NOT_RESPONDER && signal === RESPONDER_RELEASE) {
415
+ return
416
+ }
417
+
418
+ if (nextState == null || nextState === ERROR) {
419
+ // eslint-disable-next-line no-console
420
+ console.error(
421
+ 'PressResponder: Invalid signal ' + signal + ' for state ' + prevState + ' on responder'
422
+ )
423
+ } else if (prevState !== nextState) {
424
+ this._performTransitionSideEffects(prevState, nextState, signal, event)
425
+
426
+ this._touchState = nextState
427
+ }
428
+ }
429
+ /**
430
+ * Performs a transition between touchable states and identify any activations
431
+ * or deactivations (and callback invocations).
432
+ */
433
+
434
+ _performTransitionSideEffects(prevState, nextState, signal, event) {
435
+ if (isTerminalSignal(signal)) {
436
+ // Pressable suppression of contextmenu on windows.
437
+ // On Windows, the contextmenu is displayed after pointerup.
438
+ // https://github.com/necolas/react-native-web/issues/2296
439
+ setTimeout(() => {
440
+ this._isPointerTouch = false
441
+ }, 0)
442
+ this._touchActivatePosition = null
443
+
444
+ this._cancelLongPressDelayTimeout()
445
+ }
446
+
447
+ if (isPressStartSignal(prevState) && signal === LONG_PRESS_DETECTED) {
448
+ const onLongPress = this._config.onLongPress // Long press is not supported for keyboards because 'click' can be dispatched
449
+ // immediately (and multiple times) after 'keydown'.
450
+
451
+ if (onLongPress != null && event.nativeEvent.key == null) {
452
+ onLongPress(event)
453
+ this._longPressDispatched = true
454
+ }
455
+ }
456
+
457
+ const isPrevActive = isActiveSignal(prevState)
458
+ const isNextActive = isActiveSignal(nextState)
459
+
460
+ if (!isPrevActive && isNextActive) {
461
+ this._activate(event)
462
+ } else if (isPrevActive && !isNextActive) {
463
+ this._deactivate(event)
464
+ }
465
+
466
+ if (isPressStartSignal(prevState) && signal === RESPONDER_RELEASE) {
467
+ const _this$_config4 = this._config,
468
+ _onLongPress = _this$_config4.onLongPress,
469
+ onPress = _this$_config4.onPress
470
+
471
+ if (onPress != null) {
472
+ const isPressCanceledByLongPress =
473
+ _onLongPress != null && prevState === RESPONDER_ACTIVE_LONG_PRESS_START
474
+
475
+ if (!isPressCanceledByLongPress) {
476
+ // If we never activated (due to delays), activate and deactivate now.
477
+ if (!isNextActive && !isPrevActive) {
478
+ this._activate(event)
479
+
480
+ this._deactivate(event)
481
+ }
482
+ }
483
+ }
484
+ }
485
+
486
+ this._cancelPressDelayTimeout()
487
+ }
488
+
489
+ _activate(event) {
490
+ const _this$_config5 = this._config,
491
+ onPressChange = _this$_config5.onPressChange,
492
+ onPressStart = _this$_config5.onPressStart
493
+ const touch = getTouchFromResponderEvent(event)
494
+ this._touchActivatePosition = {
495
+ pageX: touch.pageX,
496
+ pageY: touch.pageY,
497
+ }
498
+
499
+ if (onPressStart != null) {
500
+ onPressStart(event)
501
+ }
502
+
503
+ if (onPressChange != null) {
504
+ onPressChange(true)
505
+ }
506
+ }
507
+
508
+ _deactivate(event) {
509
+ const _this$_config6 = this._config,
510
+ onPressChange = _this$_config6.onPressChange,
511
+ onPressEnd = _this$_config6.onPressEnd
512
+
513
+ function end() {
514
+ if (onPressEnd != null) {
515
+ onPressEnd(event)
516
+ }
517
+
518
+ if (onPressChange != null) {
519
+ onPressChange(false)
520
+ }
521
+ }
522
+
523
+ const delayPressEnd = normalizeDelay(this._config.delayPressEnd)
524
+
525
+ if (delayPressEnd > 0) {
526
+ this._pressOutDelayTimeout = setTimeout(() => {
527
+ end()
528
+ }, delayPressEnd)
529
+ } else {
530
+ end()
531
+ }
532
+ }
533
+
534
+ _handleLongPress(event) {
535
+ if (
536
+ this._touchState === RESPONDER_ACTIVE_PRESS_START ||
537
+ this._touchState === RESPONDER_ACTIVE_LONG_PRESS_START
538
+ ) {
539
+ this._receiveSignal(LONG_PRESS_DETECTED, event)
540
+ }
541
+ }
542
+
543
+ _cancelLongPressDelayTimeout() {
544
+ if (this._longPressDelayTimeout != null) {
545
+ clearTimeout(this._longPressDelayTimeout)
546
+ this._longPressDelayTimeout = null
547
+ }
548
+ }
549
+
550
+ _cancelPressDelayTimeout() {
551
+ if (this._pressDelayTimeout != null) {
552
+ clearTimeout(this._pressDelayTimeout)
553
+ this._pressDelayTimeout = null
554
+ }
555
+ }
556
+
557
+ _cancelPressOutDelayTimeout() {
558
+ if (this._pressOutDelayTimeout != null) {
559
+ clearTimeout(this._pressOutDelayTimeout)
560
+ this._pressOutDelayTimeout = null
561
+ }
562
+ }
563
+ }
564
+
565
+ function normalizeDelay(delay, min?: number, fallback?: any) {
566
+ if (min === void 0) {
567
+ min = 0
568
+ }
569
+
570
+ if (fallback === void 0) {
571
+ fallback = 0
572
+ }
573
+
574
+ return Math.max(min, delay !== null && delay !== void 0 ? delay : fallback)
575
+ }
576
+
577
+ function getTouchFromResponderEvent(event) {
578
+ const _event$nativeEvent = event.nativeEvent,
579
+ changedTouches = _event$nativeEvent.changedTouches,
580
+ touches = _event$nativeEvent.touches
581
+
582
+ if (touches != null && touches.length > 0) {
583
+ return touches[0]
584
+ }
585
+
586
+ if (changedTouches != null && changedTouches.length > 0) {
587
+ return changedTouches[0]
588
+ }
589
+
590
+ return event.nativeEvent
591
+ }
package/src/index.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ * @format
9
+ */
10
+ 'use strict'
11
+
12
+ import { useDebugValue, useEffect, useRef } from 'react'
13
+
14
+ import PressResponder from './PressResponder'
15
+
16
+ // todo
17
+ export type PressResponderConfig = any
18
+
19
+ export function usePressEvents(config?: any) {
20
+ const pressResponderRef = useRef<any>(null)
21
+
22
+ if (pressResponderRef.current == null) {
23
+ pressResponderRef.current = new PressResponder(config)
24
+ }
25
+
26
+ const pressResponder = pressResponderRef.current // Re-configure to use the current node and configuration.
27
+
28
+ useEffect(() => {
29
+ pressResponder.configure(config)
30
+ }, [config, pressResponder]) // Reset the `pressResponder` when cleanup needs to occur. This is
31
+ // a separate effect because we do not want to rest the responder when `config` changes.
32
+
33
+ useEffect(() => {
34
+ return () => {
35
+ pressResponder.reset()
36
+ }
37
+ }, [pressResponder])
38
+ useDebugValue(config)
39
+ return pressResponder.getEventHandlers()
40
+ }