@react-aria/interactions 3.27.0 → 3.28.0

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.
Files changed (118) hide show
  1. package/dist/import.mjs +21 -15
  2. package/dist/main.js +45 -39
  3. package/dist/main.js.map +1 -1
  4. package/dist/module.js +21 -15
  5. package/dist/module.js.map +1 -1
  6. package/dist/types/src/index.d.ts +31 -0
  7. package/package.json +16 -15
  8. package/src/index.ts +31 -35
  9. package/dist/PressResponder.main.js +0 -61
  10. package/dist/PressResponder.main.js.map +0 -1
  11. package/dist/PressResponder.mjs +0 -51
  12. package/dist/PressResponder.module.js +0 -51
  13. package/dist/PressResponder.module.js.map +0 -1
  14. package/dist/Pressable.main.js +0 -69
  15. package/dist/Pressable.main.js.map +0 -1
  16. package/dist/Pressable.mjs +0 -60
  17. package/dist/Pressable.module.js +0 -60
  18. package/dist/Pressable.module.js.map +0 -1
  19. package/dist/context.main.js +0 -30
  20. package/dist/context.main.js.map +0 -1
  21. package/dist/context.mjs +0 -21
  22. package/dist/context.module.js +0 -21
  23. package/dist/context.module.js.map +0 -1
  24. package/dist/createEventHandler.main.js +0 -46
  25. package/dist/createEventHandler.main.js.map +0 -1
  26. package/dist/createEventHandler.mjs +0 -41
  27. package/dist/createEventHandler.module.js +0 -41
  28. package/dist/createEventHandler.module.js.map +0 -1
  29. package/dist/focusSafely.main.js +0 -40
  30. package/dist/focusSafely.main.js.map +0 -1
  31. package/dist/focusSafely.mjs +0 -35
  32. package/dist/focusSafely.module.js +0 -35
  33. package/dist/focusSafely.module.js.map +0 -1
  34. package/dist/textSelection.main.js +0 -77
  35. package/dist/textSelection.main.js.map +0 -1
  36. package/dist/textSelection.mjs +0 -71
  37. package/dist/textSelection.module.js +0 -71
  38. package/dist/textSelection.module.js.map +0 -1
  39. package/dist/types.d.ts +0 -255
  40. package/dist/types.d.ts.map +0 -1
  41. package/dist/useFocus.main.js +0 -65
  42. package/dist/useFocus.main.js.map +0 -1
  43. package/dist/useFocus.mjs +0 -60
  44. package/dist/useFocus.module.js +0 -60
  45. package/dist/useFocus.module.js.map +0 -1
  46. package/dist/useFocusVisible.main.js +0 -265
  47. package/dist/useFocusVisible.main.js.map +0 -1
  48. package/dist/useFocusVisible.mjs +0 -253
  49. package/dist/useFocusVisible.module.js +0 -253
  50. package/dist/useFocusVisible.module.js.map +0 -1
  51. package/dist/useFocusWithin.main.js +0 -105
  52. package/dist/useFocusWithin.main.js.map +0 -1
  53. package/dist/useFocusWithin.mjs +0 -100
  54. package/dist/useFocusWithin.module.js +0 -100
  55. package/dist/useFocusWithin.module.js.map +0 -1
  56. package/dist/useFocusable.main.js +0 -113
  57. package/dist/useFocusable.main.js.map +0 -1
  58. package/dist/useFocusable.mjs +0 -101
  59. package/dist/useFocusable.module.js +0 -101
  60. package/dist/useFocusable.module.js.map +0 -1
  61. package/dist/useHover.main.js +0 -159
  62. package/dist/useHover.main.js.map +0 -1
  63. package/dist/useHover.mjs +0 -154
  64. package/dist/useHover.module.js +0 -154
  65. package/dist/useHover.module.js.map +0 -1
  66. package/dist/useInteractOutside.main.js +0 -106
  67. package/dist/useInteractOutside.main.js.map +0 -1
  68. package/dist/useInteractOutside.mjs +0 -101
  69. package/dist/useInteractOutside.module.js +0 -101
  70. package/dist/useInteractOutside.module.js.map +0 -1
  71. package/dist/useKeyboard.main.js +0 -30
  72. package/dist/useKeyboard.main.js.map +0 -1
  73. package/dist/useKeyboard.mjs +0 -25
  74. package/dist/useKeyboard.module.js +0 -25
  75. package/dist/useKeyboard.module.js.map +0 -1
  76. package/dist/useLongPress.main.js +0 -86
  77. package/dist/useLongPress.main.js.map +0 -1
  78. package/dist/useLongPress.mjs +0 -81
  79. package/dist/useLongPress.module.js +0 -81
  80. package/dist/useLongPress.module.js.map +0 -1
  81. package/dist/useMove.main.js +0 -272
  82. package/dist/useMove.main.js.map +0 -1
  83. package/dist/useMove.mjs +0 -267
  84. package/dist/useMove.module.js +0 -267
  85. package/dist/useMove.module.js.map +0 -1
  86. package/dist/usePress.main.js +0 -763
  87. package/dist/usePress.main.js.map +0 -1
  88. package/dist/usePress.mjs +0 -758
  89. package/dist/usePress.module.js +0 -758
  90. package/dist/usePress.module.js.map +0 -1
  91. package/dist/useScrollWheel.main.js +0 -41
  92. package/dist/useScrollWheel.main.js.map +0 -1
  93. package/dist/useScrollWheel.mjs +0 -36
  94. package/dist/useScrollWheel.module.js +0 -36
  95. package/dist/useScrollWheel.module.js.map +0 -1
  96. package/dist/utils.main.js +0 -163
  97. package/dist/utils.main.js.map +0 -1
  98. package/dist/utils.mjs +0 -154
  99. package/dist/utils.module.js +0 -154
  100. package/dist/utils.module.js.map +0 -1
  101. package/src/PressResponder.tsx +0 -67
  102. package/src/Pressable.tsx +0 -97
  103. package/src/context.ts +0 -23
  104. package/src/createEventHandler.ts +0 -55
  105. package/src/focusSafely.ts +0 -45
  106. package/src/textSelection.ts +0 -101
  107. package/src/useFocus.ts +0 -87
  108. package/src/useFocusVisible.ts +0 -357
  109. package/src/useFocusWithin.ts +0 -132
  110. package/src/useFocusable.tsx +0 -191
  111. package/src/useHover.ts +0 -222
  112. package/src/useInteractOutside.ts +0 -142
  113. package/src/useKeyboard.ts +0 -36
  114. package/src/useLongPress.ts +0 -135
  115. package/src/useMove.ts +0 -259
  116. package/src/usePress.ts +0 -1093
  117. package/src/useScrollWheel.ts +0 -41
  118. package/src/utils.ts +0 -174
package/src/useHover.ts DELETED
@@ -1,222 +0,0 @@
1
- /*
2
- * Copyright 2020 Adobe. All rights reserved.
3
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License. You may obtain a copy
5
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- *
7
- * Unless required by applicable law or agreed to in writing, software distributed under
8
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- * OF ANY KIND, either express or implied. See the License for the specific language
10
- * governing permissions and limitations under the License.
11
- */
12
-
13
- // Portions of the code in this file are based on code from react.
14
- // Original licensing for the following can be found in the
15
- // NOTICE file in the root directory of this source tree.
16
- // See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
17
-
18
- import {DOMAttributes, HoverEvents} from '@react-types/shared';
19
- import {getOwnerDocument, nodeContains, useGlobalListeners} from '@react-aria/utils';
20
- import {useEffect, useMemo, useRef, useState} from 'react';
21
-
22
- export interface HoverProps extends HoverEvents {
23
- /** Whether the hover events should be disabled. */
24
- isDisabled?: boolean
25
- }
26
-
27
- export interface HoverResult {
28
- /** Props to spread on the target element. */
29
- hoverProps: DOMAttributes,
30
- isHovered: boolean
31
- }
32
-
33
- // iOS fires onPointerEnter twice: once with pointerType="touch" and again with pointerType="mouse".
34
- // We want to ignore these emulated events so they do not trigger hover behavior.
35
- // See https://bugs.webkit.org/show_bug.cgi?id=214609.
36
- let globalIgnoreEmulatedMouseEvents = false;
37
- let hoverCount = 0;
38
-
39
- function setGlobalIgnoreEmulatedMouseEvents() {
40
- globalIgnoreEmulatedMouseEvents = true;
41
-
42
- // Clear globalIgnoreEmulatedMouseEvents after a short timeout. iOS fires onPointerEnter
43
- // with pointerType="mouse" immediately after onPointerUp and before onFocus. On other
44
- // devices that don't have this quirk, we don't want to ignore a mouse hover sometime in
45
- // the distant future because a user previously touched the element.
46
- setTimeout(() => {
47
- globalIgnoreEmulatedMouseEvents = false;
48
- }, 50);
49
- }
50
-
51
- function handleGlobalPointerEvent(e: PointerEvent) {
52
- if (e.pointerType === 'touch') {
53
- setGlobalIgnoreEmulatedMouseEvents();
54
- }
55
- }
56
-
57
- function setupGlobalTouchEvents() {
58
- if (typeof document === 'undefined') {
59
- return;
60
- }
61
-
62
- if (hoverCount === 0) {
63
- if (typeof PointerEvent !== 'undefined') {
64
- document.addEventListener('pointerup', handleGlobalPointerEvent);
65
- } else if (process.env.NODE_ENV === 'test') {
66
- document.addEventListener('touchend', setGlobalIgnoreEmulatedMouseEvents);
67
- }
68
- }
69
-
70
- hoverCount++;
71
- return () => {
72
- hoverCount--;
73
- if (hoverCount > 0) {
74
- return;
75
- }
76
-
77
- if (typeof PointerEvent !== 'undefined') {
78
- document.removeEventListener('pointerup', handleGlobalPointerEvent);
79
- } else if (process.env.NODE_ENV === 'test') {
80
- document.removeEventListener('touchend', setGlobalIgnoreEmulatedMouseEvents);
81
- }
82
- };
83
- }
84
-
85
- /**
86
- * Handles pointer hover interactions for an element. Normalizes behavior
87
- * across browsers and platforms, and ignores emulated mouse events on touch devices.
88
- */
89
- export function useHover(props: HoverProps): HoverResult {
90
- let {
91
- onHoverStart,
92
- onHoverChange,
93
- onHoverEnd,
94
- isDisabled
95
- } = props;
96
-
97
- let [isHovered, setHovered] = useState(false);
98
- let state = useRef({
99
- isHovered: false,
100
- ignoreEmulatedMouseEvents: false,
101
- pointerType: '',
102
- target: null
103
- }).current;
104
-
105
- useEffect(setupGlobalTouchEvents, []);
106
- let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();
107
-
108
- let {hoverProps, triggerHoverEnd} = useMemo(() => {
109
- let triggerHoverStart = (event, pointerType) => {
110
- state.pointerType = pointerType;
111
- if (isDisabled || pointerType === 'touch' || state.isHovered || !nodeContains(event.currentTarget, event.target)) {
112
- return;
113
- }
114
-
115
- state.isHovered = true;
116
- let target = event.currentTarget;
117
- state.target = target;
118
-
119
- // When an element that is hovered over is removed, no pointerleave event is fired by the browser,
120
- // even though the originally hovered target may have shrunk in size so it is no longer hovered.
121
- // However, a pointerover event will be fired on the new target the mouse is over.
122
- // In Chrome this happens immediately. In Safari and Firefox, it happens upon moving the mouse one pixel.
123
- addGlobalListener(getOwnerDocument(event.target), 'pointerover', e => {
124
- if (state.isHovered && state.target && !nodeContains(state.target, e.target as Element)) {
125
- triggerHoverEnd(e, e.pointerType);
126
- }
127
- }, {capture: true});
128
-
129
- if (onHoverStart) {
130
- onHoverStart({
131
- type: 'hoverstart',
132
- target,
133
- pointerType
134
- });
135
- }
136
-
137
- if (onHoverChange) {
138
- onHoverChange(true);
139
- }
140
-
141
- setHovered(true);
142
- };
143
-
144
- let triggerHoverEnd = (event, pointerType) => {
145
- let target = state.target;
146
- state.pointerType = '';
147
- state.target = null;
148
-
149
- if (pointerType === 'touch' || !state.isHovered || !target) {
150
- return;
151
- }
152
-
153
- state.isHovered = false;
154
- removeAllGlobalListeners();
155
-
156
- if (onHoverEnd) {
157
- onHoverEnd({
158
- type: 'hoverend',
159
- target,
160
- pointerType
161
- });
162
- }
163
-
164
- if (onHoverChange) {
165
- onHoverChange(false);
166
- }
167
-
168
- setHovered(false);
169
- };
170
-
171
- let hoverProps: DOMAttributes = {};
172
-
173
- if (typeof PointerEvent !== 'undefined') {
174
- hoverProps.onPointerEnter = (e) => {
175
- if (globalIgnoreEmulatedMouseEvents && e.pointerType === 'mouse') {
176
- return;
177
- }
178
-
179
- triggerHoverStart(e, e.pointerType);
180
- };
181
-
182
- hoverProps.onPointerLeave = (e) => {
183
- if (!isDisabled && nodeContains(e.currentTarget, e.target as Element)) {
184
- triggerHoverEnd(e, e.pointerType);
185
- }
186
- };
187
- } else if (process.env.NODE_ENV === 'test') {
188
- hoverProps.onTouchStart = () => {
189
- state.ignoreEmulatedMouseEvents = true;
190
- };
191
-
192
- hoverProps.onMouseEnter = (e) => {
193
- if (!state.ignoreEmulatedMouseEvents && !globalIgnoreEmulatedMouseEvents) {
194
- triggerHoverStart(e, 'mouse');
195
- }
196
-
197
- state.ignoreEmulatedMouseEvents = false;
198
- };
199
-
200
- hoverProps.onMouseLeave = (e) => {
201
- if (!isDisabled && nodeContains(e.currentTarget, e.target as Element)) {
202
- triggerHoverEnd(e, 'mouse');
203
- }
204
- };
205
- }
206
- return {hoverProps, triggerHoverEnd};
207
- }, [onHoverStart, onHoverChange, onHoverEnd, isDisabled, state, addGlobalListener, removeAllGlobalListeners]);
208
-
209
- useEffect(() => {
210
- // Call the triggerHoverEnd as soon as isDisabled changes to true
211
- // Safe to call triggerHoverEnd, it will early return if we aren't currently hovering
212
- if (isDisabled) {
213
- triggerHoverEnd({currentTarget: state.target}, state.pointerType);
214
- }
215
- // eslint-disable-next-line react-hooks/exhaustive-deps
216
- }, [isDisabled]);
217
-
218
- return {
219
- hoverProps,
220
- isHovered
221
- };
222
- }
@@ -1,142 +0,0 @@
1
- /*
2
- * Copyright 2020 Adobe. All rights reserved.
3
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License. You may obtain a copy
5
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- *
7
- * Unless required by applicable law or agreed to in writing, software distributed under
8
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- * OF ANY KIND, either express or implied. See the License for the specific language
10
- * governing permissions and limitations under the License.
11
- */
12
-
13
- // Portions of the code in this file are based on code from react.
14
- // Original licensing for the following can be found in the
15
- // NOTICE file in the root directory of this source tree.
16
- // See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
17
-
18
- import {getOwnerDocument, nodeContains, useEffectEvent} from '@react-aria/utils';
19
- import {RefObject} from '@react-types/shared';
20
- import {useEffect, useRef} from 'react';
21
-
22
- export interface InteractOutsideProps {
23
- ref: RefObject<Element | null>,
24
- onInteractOutside?: (e: PointerEvent) => void,
25
- onInteractOutsideStart?: (e: PointerEvent) => void,
26
- /** Whether the interact outside events should be disabled. */
27
- isDisabled?: boolean
28
- }
29
-
30
- /**
31
- * Example, used in components like Dialogs and Popovers so they can close
32
- * when a user clicks outside them.
33
- */
34
- export function useInteractOutside(props: InteractOutsideProps): void {
35
- let {ref, onInteractOutside, isDisabled, onInteractOutsideStart} = props;
36
- let stateRef = useRef({
37
- isPointerDown: false,
38
- ignoreEmulatedMouseEvents: false
39
- });
40
-
41
- let onPointerDown = useEffectEvent((e) => {
42
- if (onInteractOutside && isValidEvent(e, ref)) {
43
- if (onInteractOutsideStart) {
44
- onInteractOutsideStart(e);
45
- }
46
- stateRef.current.isPointerDown = true;
47
- }
48
- });
49
-
50
- let triggerInteractOutside = useEffectEvent((e: PointerEvent) => {
51
- if (onInteractOutside) {
52
- onInteractOutside(e);
53
- }
54
- });
55
-
56
- useEffect(() => {
57
- let state = stateRef.current;
58
- if (isDisabled) {
59
- return;
60
- }
61
-
62
- const element = ref.current;
63
- const documentObject = getOwnerDocument(element);
64
-
65
- // Use pointer events if available. Otherwise, fall back to mouse and touch events.
66
- if (typeof PointerEvent !== 'undefined') {
67
- let onClick = (e) => {
68
- if (state.isPointerDown && isValidEvent(e, ref)) {
69
- triggerInteractOutside(e);
70
- }
71
- state.isPointerDown = false;
72
- };
73
-
74
- // changing these to capture phase fixed combobox
75
- // Use click instead of pointerup to avoid Android Chrome issue
76
- // https://issues.chromium.org/issues/40732224
77
- documentObject.addEventListener('pointerdown', onPointerDown, true);
78
- documentObject.addEventListener('click', onClick, true);
79
-
80
- return () => {
81
- documentObject.removeEventListener('pointerdown', onPointerDown, true);
82
- documentObject.removeEventListener('click', onClick, true);
83
- };
84
- } else if (process.env.NODE_ENV === 'test') {
85
- let onMouseUp = (e) => {
86
- if (state.ignoreEmulatedMouseEvents) {
87
- state.ignoreEmulatedMouseEvents = false;
88
- } else if (state.isPointerDown && isValidEvent(e, ref)) {
89
- triggerInteractOutside(e);
90
- }
91
- state.isPointerDown = false;
92
- };
93
-
94
- let onTouchEnd = (e) => {
95
- state.ignoreEmulatedMouseEvents = true;
96
- if (state.isPointerDown && isValidEvent(e, ref)) {
97
- triggerInteractOutside(e);
98
- }
99
- state.isPointerDown = false;
100
- };
101
-
102
- documentObject.addEventListener('mousedown', onPointerDown, true);
103
- documentObject.addEventListener('mouseup', onMouseUp, true);
104
- documentObject.addEventListener('touchstart', onPointerDown, true);
105
- documentObject.addEventListener('touchend', onTouchEnd, true);
106
-
107
- return () => {
108
- documentObject.removeEventListener('mousedown', onPointerDown, true);
109
- documentObject.removeEventListener('mouseup', onMouseUp, true);
110
- documentObject.removeEventListener('touchstart', onPointerDown, true);
111
- documentObject.removeEventListener('touchend', onTouchEnd, true);
112
- };
113
- }
114
- }, [ref, isDisabled]);
115
- }
116
-
117
- function isValidEvent(event, ref) {
118
- if (event.button > 0) {
119
- return false;
120
- }
121
- if (event.target) {
122
- // if the event target is no longer in the document, ignore
123
- const ownerDocument = event.target.ownerDocument;
124
- if (!ownerDocument || !nodeContains(ownerDocument.documentElement, event.target)) {
125
- return false;
126
- }
127
- // If the target is within a top layer element (e.g. toasts), ignore.
128
- if (event.target.closest('[data-react-aria-top-layer]')) {
129
- return false;
130
- }
131
- }
132
-
133
- if (!ref.current) {
134
- return false;
135
- }
136
-
137
- // When the event source is inside a Shadow DOM, event.target is just the shadow root.
138
- // Using event.composedPath instead means we can get the actual element inside the shadow root.
139
- // This only works if the shadow root is open, there is no way to detect if it is closed.
140
- // If the event composed path contains the ref, interaction is inside.
141
- return !event.composedPath().includes(ref.current);
142
- }
@@ -1,36 +0,0 @@
1
- /*
2
- * Copyright 2020 Adobe. All rights reserved.
3
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License. You may obtain a copy
5
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- *
7
- * Unless required by applicable law or agreed to in writing, software distributed under
8
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- * OF ANY KIND, either express or implied. See the License for the specific language
10
- * governing permissions and limitations under the License.
11
- */
12
-
13
- import {createEventHandler} from './createEventHandler';
14
- import {DOMAttributes, KeyboardEvents} from '@react-types/shared';
15
-
16
- export interface KeyboardProps extends KeyboardEvents {
17
- /** Whether the keyboard events should be disabled. */
18
- isDisabled?: boolean
19
- }
20
-
21
- export interface KeyboardResult {
22
- /** Props to spread onto the target element. */
23
- keyboardProps: DOMAttributes
24
- }
25
-
26
- /**
27
- * Handles keyboard interactions for a focusable element.
28
- */
29
- export function useKeyboard(props: KeyboardProps): KeyboardResult {
30
- return {
31
- keyboardProps: props.isDisabled ? {} : {
32
- onKeyDown: createEventHandler(props.onKeyDown),
33
- onKeyUp: createEventHandler(props.onKeyUp)
34
- }
35
- };
36
- }
@@ -1,135 +0,0 @@
1
- /*
2
- * Copyright 2020 Adobe. All rights reserved.
3
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License. You may obtain a copy
5
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- *
7
- * Unless required by applicable law or agreed to in writing, software distributed under
8
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- * OF ANY KIND, either express or implied. See the License for the specific language
10
- * governing permissions and limitations under the License.
11
- */
12
-
13
- import {DOMAttributes, FocusableElement, LongPressEvent} from '@react-types/shared';
14
- import {focusWithoutScrolling, getOwnerDocument, mergeProps, useDescription, useGlobalListeners} from '@react-aria/utils';
15
- import {usePress} from './usePress';
16
- import {useRef} from 'react';
17
-
18
- export interface LongPressProps {
19
- /** Whether long press events should be disabled. */
20
- isDisabled?: boolean,
21
- /** Handler that is called when a long press interaction starts. */
22
- onLongPressStart?: (e: LongPressEvent) => void,
23
- /**
24
- * Handler that is called when a long press interaction ends, either
25
- * over the target or when the pointer leaves the target.
26
- */
27
- onLongPressEnd?: (e: LongPressEvent) => void,
28
- /**
29
- * Handler that is called when the threshold time is met while
30
- * the press is over the target.
31
- */
32
- onLongPress?: (e: LongPressEvent) => void,
33
- /**
34
- * The amount of time in milliseconds to wait before triggering a long press.
35
- * @default 500ms
36
- */
37
- threshold?: number,
38
- /**
39
- * A description for assistive techology users indicating that a long press
40
- * action is available, e.g. "Long press to open menu".
41
- */
42
- accessibilityDescription?: string
43
- }
44
-
45
- export interface LongPressResult {
46
- /** Props to spread on the target element. */
47
- longPressProps: DOMAttributes
48
- }
49
-
50
- const DEFAULT_THRESHOLD = 500;
51
-
52
- /**
53
- * Handles long press interactions across mouse and touch devices. Supports a customizable time threshold,
54
- * accessibility description, and normalizes behavior across browsers and devices.
55
- */
56
- export function useLongPress(props: LongPressProps): LongPressResult {
57
- let {
58
- isDisabled,
59
- onLongPressStart,
60
- onLongPressEnd,
61
- onLongPress,
62
- threshold = DEFAULT_THRESHOLD,
63
- accessibilityDescription
64
- } = props;
65
-
66
- const timeRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
67
- let {addGlobalListener, removeGlobalListener} = useGlobalListeners();
68
-
69
- let {pressProps} = usePress({
70
- isDisabled,
71
- onPressStart(e) {
72
- e.continuePropagation();
73
- if (e.pointerType === 'mouse' || e.pointerType === 'touch') {
74
- if (onLongPressStart) {
75
- onLongPressStart({
76
- ...e,
77
- type: 'longpressstart'
78
- });
79
- }
80
-
81
- timeRef.current = setTimeout(() => {
82
- // Prevent other usePress handlers from also handling this event.
83
- e.target.dispatchEvent(new PointerEvent('pointercancel', {bubbles: true}));
84
-
85
- // Ensure target is focused. On touch devices, browsers typically focus on pointer up.
86
- if (getOwnerDocument(e.target).activeElement !== e.target) {
87
- focusWithoutScrolling(e.target as FocusableElement);
88
- }
89
-
90
- if (onLongPress) {
91
- onLongPress({
92
- ...e,
93
- type: 'longpress'
94
- });
95
- }
96
- timeRef.current = undefined;
97
- }, threshold);
98
-
99
- // Prevent context menu, which may be opened on long press on touch devices
100
- if (e.pointerType === 'touch') {
101
- let onContextMenu = e => {
102
- e.preventDefault();
103
- };
104
-
105
- addGlobalListener(e.target, 'contextmenu', onContextMenu, {once: true});
106
- addGlobalListener(window, 'pointerup', () => {
107
- // If no contextmenu event is fired quickly after pointerup, remove the handler
108
- // so future context menu events outside a long press are not prevented.
109
- setTimeout(() => {
110
- removeGlobalListener(e.target, 'contextmenu', onContextMenu);
111
- }, 30);
112
- }, {once: true});
113
- }
114
- }
115
- },
116
- onPressEnd(e) {
117
- if (timeRef.current) {
118
- clearTimeout(timeRef.current);
119
- }
120
-
121
- if (onLongPressEnd && (e.pointerType === 'mouse' || e.pointerType === 'touch')) {
122
- onLongPressEnd({
123
- ...e,
124
- type: 'longpressend'
125
- });
126
- }
127
- }
128
- });
129
-
130
- let descriptionProps = useDescription(onLongPress && !isDisabled ? accessibilityDescription : undefined);
131
-
132
- return {
133
- longPressProps: mergeProps(pressProps, descriptionProps)
134
- };
135
- }