@react-aria/interactions 3.22.5 → 3.24.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 (90) hide show
  1. package/dist/Pressable.main.js +28 -4
  2. package/dist/Pressable.main.js.map +1 -1
  3. package/dist/Pressable.mjs +30 -6
  4. package/dist/Pressable.module.js +30 -6
  5. package/dist/Pressable.module.js.map +1 -1
  6. package/dist/createEventHandler.main.js +5 -1
  7. package/dist/createEventHandler.main.js.map +1 -1
  8. package/dist/createEventHandler.mjs +5 -1
  9. package/dist/createEventHandler.module.js +5 -1
  10. package/dist/createEventHandler.module.js.map +1 -1
  11. package/dist/focusSafely.main.js +40 -0
  12. package/dist/focusSafely.main.js.map +1 -0
  13. package/dist/focusSafely.mjs +35 -0
  14. package/dist/focusSafely.module.js +35 -0
  15. package/dist/focusSafely.module.js.map +1 -0
  16. package/dist/import.mjs +5 -1
  17. package/dist/main.js +9 -0
  18. package/dist/main.js.map +1 -1
  19. package/dist/module.js +5 -1
  20. package/dist/module.js.map +1 -1
  21. package/dist/textSelection.main.js +5 -3
  22. package/dist/textSelection.main.js.map +1 -1
  23. package/dist/textSelection.mjs +5 -3
  24. package/dist/textSelection.module.js +5 -3
  25. package/dist/textSelection.module.js.map +1 -1
  26. package/dist/types.d.ts +59 -25
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/useFocus.main.js +2 -1
  29. package/dist/useFocus.main.js.map +1 -1
  30. package/dist/useFocus.mjs +3 -2
  31. package/dist/useFocus.module.js +3 -2
  32. package/dist/useFocus.module.js.map +1 -1
  33. package/dist/useFocusVisible.main.js +9 -3
  34. package/dist/useFocusVisible.main.js.map +1 -1
  35. package/dist/useFocusVisible.mjs +9 -3
  36. package/dist/useFocusVisible.module.js +9 -3
  37. package/dist/useFocusVisible.module.js.map +1 -1
  38. package/dist/useFocusWithin.main.js +33 -4
  39. package/dist/useFocusWithin.main.js.map +1 -1
  40. package/dist/useFocusWithin.mjs +34 -5
  41. package/dist/useFocusWithin.module.js +34 -5
  42. package/dist/useFocusWithin.module.js.map +1 -1
  43. package/dist/useFocusable.main.js +112 -0
  44. package/dist/useFocusable.main.js.map +1 -0
  45. package/dist/useFocusable.mjs +100 -0
  46. package/dist/useFocusable.module.js +100 -0
  47. package/dist/useFocusable.module.js.map +1 -0
  48. package/dist/useHover.main.js +18 -3
  49. package/dist/useHover.main.js.map +1 -1
  50. package/dist/useHover.mjs +18 -3
  51. package/dist/useHover.module.js +18 -3
  52. package/dist/useHover.module.js.map +1 -1
  53. package/dist/useInteractOutside.main.js +6 -1
  54. package/dist/useInteractOutside.main.js.map +1 -1
  55. package/dist/useInteractOutside.mjs +6 -1
  56. package/dist/useInteractOutside.module.js +6 -1
  57. package/dist/useInteractOutside.module.js.map +1 -1
  58. package/dist/useLongPress.main.js +2 -0
  59. package/dist/useLongPress.main.js.map +1 -1
  60. package/dist/useLongPress.mjs +3 -1
  61. package/dist/useLongPress.module.js +3 -1
  62. package/dist/useLongPress.module.js.map +1 -1
  63. package/dist/usePress.main.js +97 -94
  64. package/dist/usePress.main.js.map +1 -1
  65. package/dist/usePress.mjs +98 -95
  66. package/dist/usePress.module.js +98 -95
  67. package/dist/usePress.module.js.map +1 -1
  68. package/dist/utils.main.js +57 -1
  69. package/dist/utils.main.js.map +1 -1
  70. package/dist/utils.mjs +55 -2
  71. package/dist/utils.module.js +55 -2
  72. package/dist/utils.module.js.map +1 -1
  73. package/package.json +7 -5
  74. package/src/Pressable.tsx +66 -6
  75. package/src/createEventHandler.ts +8 -1
  76. package/src/focusSafely.ts +45 -0
  77. package/src/index.ts +3 -0
  78. package/src/textSelection.ts +6 -4
  79. package/src/useFocus.ts +3 -3
  80. package/src/useFocusVisible.ts +14 -4
  81. package/src/useFocusWithin.ts +34 -5
  82. package/src/useFocusable.tsx +183 -0
  83. package/src/useHover.ts +17 -3
  84. package/src/useInteractOutside.ts +9 -3
  85. package/src/useLongPress.ts +8 -2
  86. package/src/usePress.ts +132 -131
  87. package/src/utils.ts +80 -1
  88. package/src/DOMPropsContext.ts +0 -39
  89. package/src/DOMPropsResponder.tsx +0 -47
  90. package/src/useDOMPropsResponder.ts +0 -27
package/dist/usePress.mjs CHANGED
@@ -1,9 +1,11 @@
1
1
  import {disableTextSelection as $14c0b72509d70225$export$16a4697467175487, restoreTextSelection as $14c0b72509d70225$export$b0d6fa1ab32e3295} from "./textSelection.mjs";
2
2
  import {PressResponderContext as $ae1eeba8b9eafd08$export$5165eccb35aaadb5} from "./context.mjs";
3
+ import {preventFocus as $8a9cb279dc87e130$export$cabe61c495ee3649} from "./utils.mjs";
3
4
  import {_ as $7mdmh$_} from "@swc/helpers/_/_class_private_field_get";
4
5
  import {_ as $7mdmh$_1} from "@swc/helpers/_/_class_private_field_init";
5
6
  import {_ as $7mdmh$_2} from "@swc/helpers/_/_class_private_field_set";
6
- import {mergeProps as $7mdmh$mergeProps, useSyncRef as $7mdmh$useSyncRef, useGlobalListeners as $7mdmh$useGlobalListeners, useEffectEvent as $7mdmh$useEffectEvent, getOwnerDocument as $7mdmh$getOwnerDocument, chain as $7mdmh$chain, isMac as $7mdmh$isMac, openLink as $7mdmh$openLink, isVirtualClick as $7mdmh$isVirtualClick, focusWithoutScrolling as $7mdmh$focusWithoutScrolling, isVirtualPointerEvent as $7mdmh$isVirtualPointerEvent, getOwnerWindow as $7mdmh$getOwnerWindow} from "@react-aria/utils";
7
+ import {mergeProps as $7mdmh$mergeProps, useSyncRef as $7mdmh$useSyncRef, useGlobalListeners as $7mdmh$useGlobalListeners, useEffectEvent as $7mdmh$useEffectEvent, nodeContains as $7mdmh$nodeContains, getEventTarget as $7mdmh$getEventTarget, getOwnerDocument as $7mdmh$getOwnerDocument, chain as $7mdmh$chain, isMac as $7mdmh$isMac, openLink as $7mdmh$openLink, isVirtualClick as $7mdmh$isVirtualClick, isVirtualPointerEvent as $7mdmh$isVirtualPointerEvent, focusWithoutScrolling as $7mdmh$focusWithoutScrolling, getOwnerWindow as $7mdmh$getOwnerWindow} from "@react-aria/utils";
8
+ import {flushSync as $7mdmh$flushSync} from "react-dom";
7
9
  import {useContext as $7mdmh$useContext, useState as $7mdmh$useState, useRef as $7mdmh$useRef, useMemo as $7mdmh$useMemo, useEffect as $7mdmh$useEffect} from "react";
8
10
 
9
11
  /*
@@ -27,6 +29,8 @@ import {useContext as $7mdmh$useContext, useState as $7mdmh$useState, useRef as
27
29
 
28
30
 
29
31
 
32
+
33
+
30
34
  function $f6c31cce2adf654f$var$usePressResponderContext(props) {
31
35
  // Consume context from <PressResponder> and merge with props.
32
36
  let context = (0, $7mdmh$useContext)((0, $ae1eeba8b9eafd08$export$5165eccb35aaadb5));
@@ -89,13 +93,13 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
89
93
  let ref = (0, $7mdmh$useRef)({
90
94
  isPressed: false,
91
95
  ignoreEmulatedMouseEvents: false,
92
- ignoreClickAfterPress: false,
93
96
  didFirePressStart: false,
94
97
  isTriggeringEvent: false,
95
98
  activePointerId: null,
96
99
  target: null,
97
100
  isOverTarget: false,
98
- pointerType: null
101
+ pointerType: null,
102
+ disposables: []
99
103
  });
100
104
  let { addGlobalListener: addGlobalListener, removeAllGlobalListeners: removeAllGlobalListeners } = (0, $7mdmh$useGlobalListeners)();
101
105
  let triggerPressStart = (0, $7mdmh$useEffectEvent)((originalEvent, pointerType)=>{
@@ -117,7 +121,6 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
117
121
  let triggerPressEnd = (0, $7mdmh$useEffectEvent)((originalEvent, pointerType, wasPressed = true)=>{
118
122
  let state = ref.current;
119
123
  if (!state.didFirePressStart) return false;
120
- state.ignoreClickAfterPress = true;
121
124
  state.didFirePressStart = false;
122
125
  state.isTriggeringEvent = true;
123
126
  let shouldStopPropagation = true;
@@ -151,13 +154,15 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
151
154
  let cancel = (0, $7mdmh$useEffectEvent)((e)=>{
152
155
  let state = ref.current;
153
156
  if (state.isPressed && state.target) {
154
- if (state.isOverTarget && state.pointerType != null) triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType, false);
157
+ if (state.didFirePressStart && state.pointerType != null) triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType, false);
155
158
  state.isPressed = false;
156
159
  state.isOverTarget = false;
157
160
  state.activePointerId = null;
158
161
  state.pointerType = null;
159
162
  removeAllGlobalListeners();
160
163
  if (!allowTextSelectionOnPress) (0, $14c0b72509d70225$export$b0d6fa1ab32e3295)(state.target);
164
+ for (let dispose of state.disposables)dispose();
165
+ state.disposables = [];
161
166
  }
162
167
  });
163
168
  let cancelOnPointerExit = (0, $7mdmh$useEffectEvent)((e)=>{
@@ -167,9 +172,9 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
167
172
  let state = ref.current;
168
173
  let pressProps = {
169
174
  onKeyDown (e) {
170
- if ($f6c31cce2adf654f$var$isValidKeyboardEvent(e.nativeEvent, e.currentTarget) && e.currentTarget.contains(e.target)) {
175
+ if ($f6c31cce2adf654f$var$isValidKeyboardEvent(e.nativeEvent, e.currentTarget) && (0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) {
171
176
  var _state_metaKeyEvents;
172
- if ($f6c31cce2adf654f$var$shouldPreventDefaultKeyboard(e.target, e.key)) e.preventDefault();
177
+ if ($f6c31cce2adf654f$var$shouldPreventDefaultKeyboard((0, $7mdmh$getEventTarget)(e.nativeEvent), e.key)) e.preventDefault();
173
178
  // If the event is repeating, it may have started on a different element
174
179
  // after which focus moved to the current element. Ignore these events and
175
180
  // only handle the first key down event.
@@ -177,13 +182,14 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
177
182
  if (!state.isPressed && !e.repeat) {
178
183
  state.target = e.currentTarget;
179
184
  state.isPressed = true;
185
+ state.pointerType = 'keyboard';
180
186
  shouldStopPropagation = triggerPressStart(e, 'keyboard');
181
187
  // Focus may move before the key up event, so register the event on the document
182
188
  // instead of the same element where the key down event occurred. Make it capturing so that it will trigger
183
189
  // before stopPropagation from useKeyboard on a child element may happen and thus we can still call triggerPress for the parent element.
184
190
  let originalTarget = e.currentTarget;
185
191
  let pressUp = (e)=>{
186
- if ($f6c31cce2adf654f$var$isValidKeyboardEvent(e, originalTarget) && !e.repeat && originalTarget.contains(e.target) && state.target) triggerPressUp($f6c31cce2adf654f$var$createEvent(state.target, e), 'keyboard');
192
+ if ($f6c31cce2adf654f$var$isValidKeyboardEvent(e, originalTarget) && !e.repeat && (0, $7mdmh$nodeContains)(originalTarget, (0, $7mdmh$getEventTarget)(e)) && state.target) triggerPressUp($f6c31cce2adf654f$var$createEvent(state.target, e), 'keyboard');
187
193
  };
188
194
  addGlobalListener((0, $7mdmh$getOwnerDocument)(e.currentTarget), 'keyup', (0, $7mdmh$chain)(pressUp, onKeyUp), true);
189
195
  }
@@ -199,22 +205,24 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
199
205
  } else if (e.key === 'Meta') state.metaKeyEvents = new Map();
200
206
  },
201
207
  onClick (e) {
202
- if (e && !e.currentTarget.contains(e.target)) return;
208
+ if (e && !(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
203
209
  if (e && e.button === 0 && !state.isTriggeringEvent && !(0, $7mdmh$openLink).isOpening) {
204
210
  let shouldStopPropagation = true;
205
211
  if (isDisabled) e.preventDefault();
206
212
  // If triggered from a screen reader or by using element.click(),
207
213
  // trigger as if it were a keyboard click.
208
- if (!state.ignoreClickAfterPress && !state.ignoreEmulatedMouseEvents && !state.isPressed && (state.pointerType === 'virtual' || (0, $7mdmh$isVirtualClick)(e.nativeEvent))) {
209
- // Ensure the element receives focus (VoiceOver on iOS does not do this)
210
- if (!isDisabled && !preventFocusOnPress) (0, $7mdmh$focusWithoutScrolling)(e.currentTarget);
214
+ if (!state.ignoreEmulatedMouseEvents && !state.isPressed && (state.pointerType === 'virtual' || (0, $7mdmh$isVirtualClick)(e.nativeEvent))) {
211
215
  let stopPressStart = triggerPressStart(e, 'virtual');
212
216
  let stopPressUp = triggerPressUp(e, 'virtual');
213
217
  let stopPressEnd = triggerPressEnd(e, 'virtual');
214
218
  shouldStopPropagation = stopPressStart && stopPressUp && stopPressEnd;
219
+ } else if (state.isPressed && state.pointerType !== 'keyboard') {
220
+ let pointerType = state.pointerType || e.nativeEvent.pointerType || 'virtual';
221
+ shouldStopPropagation = triggerPressEnd($f6c31cce2adf654f$var$createEvent(e.currentTarget, e), pointerType, true);
222
+ state.isOverTarget = false;
223
+ cancel(e);
215
224
  }
216
225
  state.ignoreEmulatedMouseEvents = false;
217
- state.ignoreClickAfterPress = false;
218
226
  if (shouldStopPropagation) e.stopPropagation();
219
227
  }
220
228
  }
@@ -223,14 +231,14 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
223
231
  var _state_metaKeyEvents;
224
232
  if (state.isPressed && state.target && $f6c31cce2adf654f$var$isValidKeyboardEvent(e, state.target)) {
225
233
  var _state_metaKeyEvents1;
226
- if ($f6c31cce2adf654f$var$shouldPreventDefaultKeyboard(e.target, e.key)) e.preventDefault();
227
- let target = e.target;
228
- triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), 'keyboard', state.target.contains(target));
234
+ if ($f6c31cce2adf654f$var$shouldPreventDefaultKeyboard((0, $7mdmh$getEventTarget)(e), e.key)) e.preventDefault();
235
+ let target = (0, $7mdmh$getEventTarget)(e);
236
+ triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), 'keyboard', (0, $7mdmh$nodeContains)(state.target, (0, $7mdmh$getEventTarget)(e)));
229
237
  removeAllGlobalListeners();
230
238
  // If a link was triggered with a key other than Enter, open the URL ourselves.
231
239
  // This means the link has a role override, and the default browser behavior
232
240
  // only applies when using the Enter key.
233
- if (e.key !== 'Enter' && $f6c31cce2adf654f$var$isHTMLAnchorLink(state.target) && state.target.contains(target) && !e[$f6c31cce2adf654f$var$LINK_CLICKED]) {
241
+ if (e.key !== 'Enter' && $f6c31cce2adf654f$var$isHTMLAnchorLink(state.target) && (0, $7mdmh$nodeContains)(state.target, target) && !e[$f6c31cce2adf654f$var$LINK_CLICKED]) {
234
242
  // Store a hidden property on the event so we only trigger link click once,
235
243
  // even if there are multiple usePress instances attached to the element.
236
244
  e[$f6c31cce2adf654f$var$LINK_CLICKED] = true;
@@ -251,7 +259,7 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
251
259
  if (typeof PointerEvent !== 'undefined') {
252
260
  pressProps.onPointerDown = (e)=>{
253
261
  // Only handle left clicks, and ignore events that bubbled through portals.
254
- if (e.button !== 0 || !e.currentTarget.contains(e.target)) return;
262
+ if (e.button !== 0 || !(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
255
263
  // iOS safari fires pointer events from VoiceOver with incorrect coordinates/target.
256
264
  // Ignore and let the onClick handler take care of it instead.
257
265
  // https://bugs.webkit.org/show_bug.cgi?id=222627
@@ -260,9 +268,6 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
260
268
  state.pointerType = 'virtual';
261
269
  return;
262
270
  }
263
- // Due to browser inconsistencies, especially on mobile browsers, we prevent
264
- // default on pointer down and handle focusing the pressable element ourselves.
265
- if ($f6c31cce2adf654f$var$shouldPreventDefaultDown(e.currentTarget)) e.preventDefault();
266
271
  state.pointerType = e.pointerType;
267
272
  let shouldStopPropagation = true;
268
273
  if (!state.isPressed) {
@@ -270,44 +275,41 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
270
275
  state.isOverTarget = true;
271
276
  state.activePointerId = e.pointerId;
272
277
  state.target = e.currentTarget;
273
- if (!isDisabled && !preventFocusOnPress) (0, $7mdmh$focusWithoutScrolling)(e.currentTarget);
274
278
  if (!allowTextSelectionOnPress) (0, $14c0b72509d70225$export$16a4697467175487)(state.target);
275
279
  shouldStopPropagation = triggerPressStart(e, state.pointerType);
276
- addGlobalListener((0, $7mdmh$getOwnerDocument)(e.currentTarget), 'pointermove', onPointerMove, false);
280
+ // Release pointer capture so that touch interactions can leave the original target.
281
+ // This enables onPointerLeave and onPointerEnter to fire.
282
+ let target = (0, $7mdmh$getEventTarget)(e.nativeEvent);
283
+ if ('releasePointerCapture' in target) target.releasePointerCapture(e.pointerId);
277
284
  addGlobalListener((0, $7mdmh$getOwnerDocument)(e.currentTarget), 'pointerup', onPointerUp, false);
278
285
  addGlobalListener((0, $7mdmh$getOwnerDocument)(e.currentTarget), 'pointercancel', onPointerCancel, false);
279
286
  }
280
287
  if (shouldStopPropagation) e.stopPropagation();
281
288
  };
282
289
  pressProps.onMouseDown = (e)=>{
283
- if (!e.currentTarget.contains(e.target)) return;
290
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
284
291
  if (e.button === 0) {
285
- // Chrome and Firefox on touch Windows devices require mouse down events
286
- // to be canceled in addition to pointer events, or an extra asynchronous
287
- // focus event will be fired.
288
- if ($f6c31cce2adf654f$var$shouldPreventDefaultDown(e.currentTarget)) e.preventDefault();
292
+ if (preventFocusOnPress) {
293
+ let dispose = (0, $8a9cb279dc87e130$export$cabe61c495ee3649)(e.target);
294
+ if (dispose) state.disposables.push(dispose);
295
+ }
289
296
  e.stopPropagation();
290
297
  }
291
298
  };
292
299
  pressProps.onPointerUp = (e)=>{
293
300
  // iOS fires pointerup with zero width and height, so check the pointerType recorded during pointerdown.
294
- if (!e.currentTarget.contains(e.target) || state.pointerType === 'virtual') return;
301
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent)) || state.pointerType === 'virtual') return;
295
302
  // Only handle left clicks
296
- // Safari on iOS sometimes fires pointerup events, even
297
- // when the touch isn't over the target, so double check.
298
- if (e.button === 0 && $f6c31cce2adf654f$var$isOverTarget(e, e.currentTarget)) triggerPressUp(e, state.pointerType || e.pointerType);
303
+ if (e.button === 0) triggerPressUp(e, state.pointerType || e.pointerType);
299
304
  };
300
- // Safari on iOS < 13.2 does not implement pointerenter/pointerleave events correctly.
301
- // Use pointer move events instead to implement our own hit testing.
302
- // See https://bugs.webkit.org/show_bug.cgi?id=199803
303
- let onPointerMove = (e)=>{
304
- if (e.pointerId !== state.activePointerId) return;
305
- if (state.target && $f6c31cce2adf654f$var$isOverTarget(e, state.target)) {
306
- if (!state.isOverTarget && state.pointerType != null) {
307
- state.isOverTarget = true;
308
- triggerPressStart($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType);
309
- }
310
- } else if (state.target && state.isOverTarget && state.pointerType != null) {
305
+ pressProps.onPointerEnter = (e)=>{
306
+ if (e.pointerId === state.activePointerId && state.target && !state.isOverTarget && state.pointerType != null) {
307
+ state.isOverTarget = true;
308
+ triggerPressStart($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType);
309
+ }
310
+ };
311
+ pressProps.onPointerLeave = (e)=>{
312
+ if (e.pointerId === state.activePointerId && state.target && state.isOverTarget && state.pointerType != null) {
311
313
  state.isOverTarget = false;
312
314
  triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType, false);
313
315
  cancelOnPointerExit(e);
@@ -315,46 +317,50 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
315
317
  };
316
318
  let onPointerUp = (e)=>{
317
319
  if (e.pointerId === state.activePointerId && state.isPressed && e.button === 0 && state.target) {
318
- if ($f6c31cce2adf654f$var$isOverTarget(e, state.target) && state.pointerType != null) triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType);
319
- else if (state.isOverTarget && state.pointerType != null) triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType, false);
320
- state.isPressed = false;
320
+ if ((0, $7mdmh$nodeContains)(state.target, (0, $7mdmh$getEventTarget)(e)) && state.pointerType != null) {
321
+ // Wait for onClick to fire onPress. This avoids browser issues when the DOM
322
+ // is mutated between onPointerUp and onClick, and is more compatible with third party libraries.
323
+ // https://github.com/adobe/react-spectrum/issues/1513
324
+ // https://issues.chromium.org/issues/40732224
325
+ // However, iOS and Android do not focus or fire onClick after a long press.
326
+ // We work around this by triggering a click ourselves after a timeout.
327
+ // This timeout is canceled during the click event in case the real one fires first.
328
+ // The timeout must be at least 32ms, because Safari on iOS delays the click event on
329
+ // non-form elements without certain ARIA roles (for hover emulation).
330
+ // https://github.com/WebKit/WebKit/blob/dccfae42bb29bd4bdef052e469f604a9387241c0/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm#L875-L892
331
+ let clicked = false;
332
+ let timeout = setTimeout(()=>{
333
+ if (state.isPressed && state.target instanceof HTMLElement) {
334
+ if (clicked) cancel(e);
335
+ else {
336
+ (0, $7mdmh$focusWithoutScrolling)(state.target);
337
+ state.target.click();
338
+ }
339
+ }
340
+ }, 80);
341
+ // Use a capturing listener to track if a click occurred.
342
+ // If stopPropagation is called it may never reach our handler.
343
+ addGlobalListener(e.currentTarget, 'click', ()=>clicked = true, true);
344
+ state.disposables.push(()=>clearTimeout(timeout));
345
+ } else cancel(e);
346
+ // Ignore subsequent onPointerLeave event before onClick on touch devices.
321
347
  state.isOverTarget = false;
322
- state.activePointerId = null;
323
- state.pointerType = null;
324
- removeAllGlobalListeners();
325
- if (!allowTextSelectionOnPress) (0, $14c0b72509d70225$export$b0d6fa1ab32e3295)(state.target);
326
- // Prevent subsequent touchend event from triggering onClick on unrelated elements on Android. See below.
327
- // Both 'touch' and 'pen' pointerTypes trigger onTouchEnd, but 'mouse' does not.
328
- if ('ontouchend' in state.target && e.pointerType !== 'mouse') addGlobalListener(state.target, 'touchend', onTouchEnd, {
329
- once: true
330
- });
331
348
  }
332
349
  };
333
- // This is a workaround for an Android Chrome/Firefox issue where click events are fired on an incorrect element
334
- // if the original target is removed during onPointerUp (before onClick).
335
- // https://github.com/adobe/react-spectrum/issues/1513
336
- // https://issues.chromium.org/issues/40732224
337
- // Note: this event must be registered directly on the element, not via React props in order to work.
338
- // https://github.com/facebook/react/issues/9809
339
- let onTouchEnd = (e)=>{
340
- // Don't preventDefault if we actually want the default (e.g. submit/link click).
341
- if ($f6c31cce2adf654f$var$shouldPreventDefaultUp(e.currentTarget)) e.preventDefault();
342
- };
343
350
  let onPointerCancel = (e)=>{
344
351
  cancel(e);
345
352
  };
346
353
  pressProps.onDragStart = (e)=>{
347
- if (!e.currentTarget.contains(e.target)) return;
354
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
348
355
  // Safari does not call onPointerCancel when a drag starts, whereas Chrome and Firefox do.
349
356
  cancel(e);
350
357
  };
351
358
  } else {
359
+ // NOTE: this fallback branch is almost entirely used by unit tests.
360
+ // All browsers now support pointer events, but JSDOM still does not.
352
361
  pressProps.onMouseDown = (e)=>{
353
362
  // Only handle left clicks
354
- if (e.button !== 0 || !e.currentTarget.contains(e.target)) return;
355
- // Due to browser inconsistencies, especially on mobile browsers, we prevent
356
- // default on mouse down and handle focusing the pressable element ourselves.
357
- if ($f6c31cce2adf654f$var$shouldPreventDefaultDown(e.currentTarget)) e.preventDefault();
363
+ if (e.button !== 0 || !(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
358
364
  if (state.ignoreEmulatedMouseEvents) {
359
365
  e.stopPropagation();
360
366
  return;
@@ -363,13 +369,17 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
363
369
  state.isOverTarget = true;
364
370
  state.target = e.currentTarget;
365
371
  state.pointerType = (0, $7mdmh$isVirtualClick)(e.nativeEvent) ? 'virtual' : 'mouse';
366
- if (!isDisabled && !preventFocusOnPress) (0, $7mdmh$focusWithoutScrolling)(e.currentTarget);
367
- let shouldStopPropagation = triggerPressStart(e, state.pointerType);
372
+ // Flush sync so that focus moved during react re-renders occurs before we yield back to the browser.
373
+ let shouldStopPropagation = (0, $7mdmh$flushSync)(()=>triggerPressStart(e, state.pointerType));
368
374
  if (shouldStopPropagation) e.stopPropagation();
375
+ if (preventFocusOnPress) {
376
+ let dispose = (0, $8a9cb279dc87e130$export$cabe61c495ee3649)(e.target);
377
+ if (dispose) state.disposables.push(dispose);
378
+ }
369
379
  addGlobalListener((0, $7mdmh$getOwnerDocument)(e.currentTarget), 'mouseup', onMouseUp, false);
370
380
  };
371
381
  pressProps.onMouseEnter = (e)=>{
372
- if (!e.currentTarget.contains(e.target)) return;
382
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
373
383
  let shouldStopPropagation = true;
374
384
  if (state.isPressed && !state.ignoreEmulatedMouseEvents && state.pointerType != null) {
375
385
  state.isOverTarget = true;
@@ -378,7 +388,7 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
378
388
  if (shouldStopPropagation) e.stopPropagation();
379
389
  };
380
390
  pressProps.onMouseLeave = (e)=>{
381
- if (!e.currentTarget.contains(e.target)) return;
391
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
382
392
  let shouldStopPropagation = true;
383
393
  if (state.isPressed && !state.ignoreEmulatedMouseEvents && state.pointerType != null) {
384
394
  state.isOverTarget = false;
@@ -388,24 +398,22 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
388
398
  if (shouldStopPropagation) e.stopPropagation();
389
399
  };
390
400
  pressProps.onMouseUp = (e)=>{
391
- if (!e.currentTarget.contains(e.target)) return;
401
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
392
402
  if (!state.ignoreEmulatedMouseEvents && e.button === 0) triggerPressUp(e, state.pointerType || 'mouse');
393
403
  };
394
404
  let onMouseUp = (e)=>{
395
405
  // Only handle left clicks
396
406
  if (e.button !== 0) return;
397
- state.isPressed = false;
398
- removeAllGlobalListeners();
399
407
  if (state.ignoreEmulatedMouseEvents) {
400
408
  state.ignoreEmulatedMouseEvents = false;
401
409
  return;
402
410
  }
403
- if (state.target && $f6c31cce2adf654f$var$isOverTarget(e, state.target) && state.pointerType != null) triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType);
404
- else if (state.target && state.isOverTarget && state.pointerType != null) triggerPressEnd($f6c31cce2adf654f$var$createEvent(state.target, e), state.pointerType, false);
411
+ if (state.target && state.target.contains(e.target) && state.pointerType != null) ;
412
+ else cancel(e);
405
413
  state.isOverTarget = false;
406
414
  };
407
415
  pressProps.onTouchStart = (e)=>{
408
- if (!e.currentTarget.contains(e.target)) return;
416
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
409
417
  let touch = $f6c31cce2adf654f$var$getTouchFromEvent(e.nativeEvent);
410
418
  if (!touch) return;
411
419
  state.activePointerId = touch.identifier;
@@ -414,16 +422,13 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
414
422
  state.isPressed = true;
415
423
  state.target = e.currentTarget;
416
424
  state.pointerType = 'touch';
417
- // Due to browser inconsistencies, especially on mobile browsers, we prevent default
418
- // on the emulated mouse event and handle focusing the pressable element ourselves.
419
- if (!isDisabled && !preventFocusOnPress) (0, $7mdmh$focusWithoutScrolling)(e.currentTarget);
420
425
  if (!allowTextSelectionOnPress) (0, $14c0b72509d70225$export$16a4697467175487)(state.target);
421
426
  let shouldStopPropagation = triggerPressStart($f6c31cce2adf654f$var$createTouchEvent(state.target, e), state.pointerType);
422
427
  if (shouldStopPropagation) e.stopPropagation();
423
428
  addGlobalListener((0, $7mdmh$getOwnerWindow)(e.currentTarget), 'scroll', onScroll, true);
424
429
  };
425
430
  pressProps.onTouchMove = (e)=>{
426
- if (!e.currentTarget.contains(e.target)) return;
431
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
427
432
  if (!state.isPressed) {
428
433
  e.stopPropagation();
429
434
  return;
@@ -443,7 +448,7 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
443
448
  if (shouldStopPropagation) e.stopPropagation();
444
449
  };
445
450
  pressProps.onTouchEnd = (e)=>{
446
- if (!e.currentTarget.contains(e.target)) return;
451
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
447
452
  if (!state.isPressed) {
448
453
  e.stopPropagation();
449
454
  return;
@@ -463,12 +468,12 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
463
468
  removeAllGlobalListeners();
464
469
  };
465
470
  pressProps.onTouchCancel = (e)=>{
466
- if (!e.currentTarget.contains(e.target)) return;
471
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
467
472
  e.stopPropagation();
468
473
  if (state.isPressed) cancel($f6c31cce2adf654f$var$createTouchEvent(state.target, e));
469
474
  };
470
475
  let onScroll = (e)=>{
471
- if (state.isPressed && e.target.contains(state.target)) cancel({
476
+ if (state.isPressed && (0, $7mdmh$nodeContains)((0, $7mdmh$getEventTarget)(e), state.target)) cancel({
472
477
  currentTarget: state.target,
473
478
  shiftKey: false,
474
479
  ctrlKey: false,
@@ -477,7 +482,7 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
477
482
  });
478
483
  };
479
484
  pressProps.onDragStart = (e)=>{
480
- if (!e.currentTarget.contains(e.target)) return;
485
+ if (!(0, $7mdmh$nodeContains)(e.currentTarget, (0, $7mdmh$getEventTarget)(e.nativeEvent))) return;
481
486
  cancel(e);
482
487
  };
483
488
  }
@@ -496,10 +501,12 @@ function $f6c31cce2adf654f$export$45712eceda6fad21(props) {
496
501
  ]);
497
502
  // Remove user-select: none in case component unmounts immediately after pressStart
498
503
  (0, $7mdmh$useEffect)(()=>{
504
+ let state = ref.current;
499
505
  return ()=>{
500
- var _ref_current_target;
501
- if (!allowTextSelectionOnPress) // eslint-disable-next-line react-hooks/exhaustive-deps
502
- (0, $14c0b72509d70225$export$b0d6fa1ab32e3295)((_ref_current_target = ref.current.target) !== null && _ref_current_target !== void 0 ? _ref_current_target : undefined);
506
+ var _state_target;
507
+ if (!allowTextSelectionOnPress) (0, $14c0b72509d70225$export$b0d6fa1ab32e3295)((_state_target = state.target) !== null && _state_target !== void 0 ? _state_target : undefined);
508
+ for (let dispose of state.disposables)dispose();
509
+ state.disposables = [];
503
510
  };
504
511
  }, [
505
512
  allowTextSelectionOnPress
@@ -590,10 +597,6 @@ function $f6c31cce2adf654f$var$isOverTarget(point, target) {
590
597
  let pointRect = $f6c31cce2adf654f$var$getPointClientRect(point);
591
598
  return $f6c31cce2adf654f$var$areRectanglesOverlapping(rect, pointRect);
592
599
  }
593
- function $f6c31cce2adf654f$var$shouldPreventDefaultDown(target) {
594
- // We cannot prevent default if the target is a draggable element.
595
- return !(target instanceof HTMLElement) || !target.hasAttribute('draggable');
596
- }
597
600
  function $f6c31cce2adf654f$var$shouldPreventDefaultUp(target) {
598
601
  if (target instanceof HTMLInputElement) return false;
599
602
  if (target instanceof HTMLButtonElement) return target.type !== 'submit' && target.type !== 'reset';