@react-aria/interactions 3.25.5 → 3.26.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 (45) hide show
  1. package/dist/focusSafely.main.js +4 -4
  2. package/dist/focusSafely.main.js.map +1 -1
  3. package/dist/focusSafely.mjs +4 -4
  4. package/dist/focusSafely.module.js +4 -4
  5. package/dist/focusSafely.module.js.map +1 -1
  6. package/dist/import.mjs +2 -2
  7. package/dist/main.js +1 -0
  8. package/dist/main.js.map +1 -1
  9. package/dist/module.js +2 -2
  10. package/dist/module.js.map +1 -1
  11. package/dist/types.d.ts +3 -1
  12. package/dist/types.d.ts.map +1 -1
  13. package/dist/useFocusVisible.main.js +12 -2
  14. package/dist/useFocusVisible.main.js.map +1 -1
  15. package/dist/useFocusVisible.mjs +13 -4
  16. package/dist/useFocusVisible.module.js +13 -4
  17. package/dist/useFocusVisible.module.js.map +1 -1
  18. package/dist/useInteractOutside.main.js +1 -3
  19. package/dist/useInteractOutside.main.js.map +1 -1
  20. package/dist/useInteractOutside.mjs +1 -3
  21. package/dist/useInteractOutside.module.js +1 -3
  22. package/dist/useInteractOutside.module.js.map +1 -1
  23. package/dist/useMove.main.js +110 -74
  24. package/dist/useMove.main.js.map +1 -1
  25. package/dist/useMove.mjs +112 -76
  26. package/dist/useMove.module.js +112 -76
  27. package/dist/useMove.module.js.map +1 -1
  28. package/dist/usePress.main.js +188 -114
  29. package/dist/usePress.main.js.map +1 -1
  30. package/dist/usePress.mjs +190 -116
  31. package/dist/usePress.module.js +190 -116
  32. package/dist/usePress.module.js.map +1 -1
  33. package/dist/utils.main.js +2 -5
  34. package/dist/utils.main.js.map +1 -1
  35. package/dist/utils.mjs +3 -6
  36. package/dist/utils.module.js +3 -6
  37. package/dist/utils.module.js.map +1 -1
  38. package/package.json +4 -4
  39. package/src/focusSafely.ts +4 -4
  40. package/src/index.ts +1 -0
  41. package/src/useFocusVisible.ts +15 -3
  42. package/src/useInteractOutside.ts +1 -1
  43. package/src/useMove.ts +85 -57
  44. package/src/usePress.ts +192 -148
  45. package/src/utils.ts +3 -7
package/src/usePress.ts CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  openLink,
30
30
  useEffectEvent,
31
31
  useGlobalListeners,
32
+ useLayoutEffect,
32
33
  useSyncRef
33
34
  } from '@react-aria/utils';
34
35
  import {createSyntheticEvent, preventFocus, setEventTarget} from './utils';
@@ -36,7 +37,7 @@ import {disableTextSelection, restoreTextSelection} from './textSelection';
36
37
  import {DOMAttributes, FocusableElement, PressEvent as IPressEvent, PointerType, PressEvents, RefObject} from '@react-types/shared';
37
38
  import {flushSync} from 'react-dom';
38
39
  import {PressResponderContext} from './context';
39
- import {MouseEvent as RMouseEvent, TouchEvent as RTouchEvent, useContext, useEffect, useMemo, useRef, useState} from 'react';
40
+ import {MouseEvent as RMouseEvent, TouchEvent as RTouchEvent, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
40
41
 
41
42
  export interface PressProps extends PressEvents {
42
43
  /** Whether the target is in a controlled press state (e.g. an overlay it triggers is open). */
@@ -195,9 +196,9 @@ export function usePress(props: PressHookProps): PressResult {
195
196
  disposables: []
196
197
  });
197
198
 
198
- let {addGlobalListener, removeAllGlobalListeners} = useGlobalListeners();
199
+ let {addGlobalListener, removeAllGlobalListeners, removeGlobalListener} = useGlobalListeners();
199
200
 
200
- let triggerPressStart = useEffectEvent((originalEvent: EventBase, pointerType: PointerType) => {
201
+ let triggerPressStart = useCallback((originalEvent: EventBase, pointerType: PointerType) => {
201
202
  let state = ref.current;
202
203
  if (isDisabled || state.didFirePressStart) {
203
204
  return false;
@@ -219,9 +220,9 @@ export function usePress(props: PressHookProps): PressResult {
219
220
  state.didFirePressStart = true;
220
221
  setPressed(true);
221
222
  return shouldStopPropagation;
222
- });
223
+ }, [isDisabled, onPressStart, onPressChange]);
223
224
 
224
- let triggerPressEnd = useEffectEvent((originalEvent: EventBase, pointerType: PointerType, wasPressed = true) => {
225
+ let triggerPressEnd = useCallback((originalEvent: EventBase, pointerType: PointerType, wasPressed = true) => {
225
226
  let state = ref.current;
226
227
  if (!state.didFirePressStart) {
227
228
  return false;
@@ -251,9 +252,10 @@ export function usePress(props: PressHookProps): PressResult {
251
252
 
252
253
  state.isTriggeringEvent = false;
253
254
  return shouldStopPropagation;
254
- });
255
+ }, [isDisabled, onPressEnd, onPressChange, onPress]);
256
+ let triggerPressEndEvent = useEffectEvent(triggerPressEnd);
255
257
 
256
- let triggerPressUp = useEffectEvent((originalEvent: EventBase, pointerType: PointerType) => {
258
+ let triggerPressUp = useCallback((originalEvent: EventBase, pointerType: PointerType) => {
257
259
  let state = ref.current;
258
260
  if (isDisabled) {
259
261
  return false;
@@ -268,15 +270,17 @@ export function usePress(props: PressHookProps): PressResult {
268
270
  }
269
271
 
270
272
  return true;
271
- });
273
+ }, [isDisabled, onPressUp]);
274
+ let triggerPressUpEvent = useEffectEvent(triggerPressUp);
272
275
 
273
- let cancel = useEffectEvent((e: EventBase) => {
276
+ let cancel = useCallback((e: EventBase) => {
274
277
  let state = ref.current;
275
278
  if (state.isPressed && state.target) {
276
279
  if (state.didFirePressStart && state.pointerType != null) {
277
280
  triggerPressEnd(createEvent(state.target, e), state.pointerType, false);
278
281
  }
279
282
  state.isPressed = false;
283
+ setIsPointerPressed(null);
280
284
  state.isOverTarget = false;
281
285
  state.activePointerId = null;
282
286
  state.pointerType = null;
@@ -289,19 +293,28 @@ export function usePress(props: PressHookProps): PressResult {
289
293
  }
290
294
  state.disposables = [];
291
295
  }
292
- });
296
+ }, [allowTextSelectionOnPress, removeAllGlobalListeners, triggerPressEnd]);
297
+ let cancelEvent = useEffectEvent(cancel);
293
298
 
294
- let cancelOnPointerExit = useEffectEvent((e: EventBase) => {
299
+ let cancelOnPointerExit = useCallback((e: EventBase) => {
295
300
  if (shouldCancelOnPointerExit) {
296
301
  cancel(e);
297
302
  }
298
- });
303
+ }, [shouldCancelOnPointerExit, cancel]);
304
+
305
+ let triggerClick = useCallback((e: RMouseEvent<FocusableElement>) => {
306
+ if (isDisabled) {
307
+ return;
308
+ }
299
309
 
300
- let triggerClick = useEffectEvent((e: RMouseEvent<FocusableElement>) => {
301
310
  onClick?.(e);
302
- });
311
+ }, [isDisabled, onClick]);
312
+
313
+ let triggerSyntheticClick = useCallback((e: KeyboardEvent | TouchEvent, target: FocusableElement) => {
314
+ if (isDisabled) {
315
+ return;
316
+ }
303
317
 
304
- let triggerSyntheticClick = useEffectEvent((e: KeyboardEvent | TouchEvent, target: FocusableElement) => {
305
318
  // Some third-party libraries pass in onClick instead of onPress.
306
319
  // Create a fake mouse event and trigger onClick as well.
307
320
  // This matches the browser's native activation behavior for certain elements (e.g. button).
@@ -312,7 +325,164 @@ export function usePress(props: PressHookProps): PressResult {
312
325
  setEventTarget(event, target);
313
326
  onClick(createSyntheticEvent(event));
314
327
  }
315
- });
328
+ }, [isDisabled, onClick]);
329
+ let triggerSyntheticClickEvent = useEffectEvent(triggerSyntheticClick);
330
+
331
+ let [isElemKeyPressed, setIsElemKeyPressed] = useState<boolean>(false);
332
+ useLayoutEffect(() => {
333
+ let state = ref.current;
334
+ if (isElemKeyPressed) {
335
+ let onKeyUp = (e: KeyboardEvent) => {
336
+ if (state.isPressed && state.target && isValidKeyboardEvent(e, state.target)) {
337
+ if (shouldPreventDefaultKeyboard(getEventTarget(e), e.key)) {
338
+ e.preventDefault();
339
+ }
340
+
341
+ let target = getEventTarget(e);
342
+ let wasPressed = nodeContains(state.target, getEventTarget(e));
343
+ triggerPressEndEvent(createEvent(state.target, e), 'keyboard', wasPressed);
344
+ if (wasPressed) {
345
+ triggerSyntheticClickEvent(e, state.target);
346
+ }
347
+ removeAllGlobalListeners();
348
+
349
+ // If a link was triggered with a key other than Enter, open the URL ourselves.
350
+ // This means the link has a role override, and the default browser behavior
351
+ // only applies when using the Enter key.
352
+ if (e.key !== 'Enter' && isHTMLAnchorLink(state.target) && nodeContains(state.target, target) && !e[LINK_CLICKED]) {
353
+ // Store a hidden property on the event so we only trigger link click once,
354
+ // even if there are multiple usePress instances attached to the element.
355
+ e[LINK_CLICKED] = true;
356
+ openLink(state.target, e, false);
357
+ }
358
+
359
+ state.isPressed = false;
360
+ setIsElemKeyPressed(false);
361
+ state.metaKeyEvents?.delete(e.key);
362
+ } else if (e.key === 'Meta' && state.metaKeyEvents?.size) {
363
+ // If we recorded keydown events that occurred while the Meta key was pressed,
364
+ // and those haven't received keyup events already, fire keyup events ourselves.
365
+ // See comment above for more info about the macOS bug causing this.
366
+ let events = state.metaKeyEvents;
367
+ state.metaKeyEvents = undefined;
368
+ for (let event of events.values()) {
369
+ state.target?.dispatchEvent(new KeyboardEvent('keyup', event));
370
+ }
371
+ }
372
+ };
373
+ // Focus may move before the key up event, so register the event on the document
374
+ // instead of the same element where the key down event occurred. Make it capturing so that it will trigger
375
+ // before stopPropagation from useKeyboard on a child element may happen and thus we can still call triggerPress for the parent element.
376
+ let originalTarget = state.target;
377
+ let pressUp = (e) => {
378
+ if (originalTarget && isValidKeyboardEvent(e, originalTarget) && !e.repeat && nodeContains(originalTarget, getEventTarget(e)) && state.target) {
379
+ triggerPressUpEvent(createEvent(state.target, e), 'keyboard');
380
+ }
381
+ };
382
+ let listener = chain(pressUp, onKeyUp);
383
+ addGlobalListener(getOwnerDocument(state.target), 'keyup', listener, true);
384
+ return () => {
385
+ removeGlobalListener(getOwnerDocument(state.target), 'keyup', listener, true);
386
+ };
387
+ }
388
+ }, [isElemKeyPressed, addGlobalListener, removeAllGlobalListeners, removeGlobalListener]);
389
+
390
+ let [isPointerPressed, setIsPointerPressed] = useState<'pointer' | 'mouse' | 'touch' | null>(null);
391
+ useLayoutEffect(() => {
392
+ let state = ref.current;
393
+ if (isPointerPressed === 'pointer') {
394
+ let onPointerUp = (e: PointerEvent) => {
395
+ if (e.pointerId === state.activePointerId && state.isPressed && e.button === 0 && state.target) {
396
+ if (nodeContains(state.target, getEventTarget(e)) && state.pointerType != null) {
397
+ // Wait for onClick to fire onPress. This avoids browser issues when the DOM
398
+ // is mutated between onPointerUp and onClick, and is more compatible with third party libraries.
399
+ // https://github.com/adobe/react-spectrum/issues/1513
400
+ // https://issues.chromium.org/issues/40732224
401
+ // However, iOS and Android do not focus or fire onClick after a long press.
402
+ // We work around this by triggering a click ourselves after a timeout.
403
+ // This timeout is canceled during the click event in case the real one fires first.
404
+ // The timeout must be at least 32ms, because Safari on iOS delays the click event on
405
+ // non-form elements without certain ARIA roles (for hover emulation).
406
+ // https://github.com/WebKit/WebKit/blob/dccfae42bb29bd4bdef052e469f604a9387241c0/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm#L875-L892
407
+ let clicked = false;
408
+ let timeout = setTimeout(() => {
409
+ if (state.isPressed && state.target instanceof HTMLElement) {
410
+ if (clicked) {
411
+ cancelEvent(e);
412
+ } else {
413
+ focusWithoutScrolling(state.target);
414
+ state.target.click();
415
+ }
416
+ }
417
+ }, 80);
418
+ // Use a capturing listener to track if a click occurred.
419
+ // If stopPropagation is called it may never reach our handler.
420
+ addGlobalListener(e.currentTarget as Document, 'click', () => clicked = true, true);
421
+ state.disposables.push(() => clearTimeout(timeout));
422
+ } else {
423
+ cancelEvent(e);
424
+ }
425
+
426
+ // Ignore subsequent onPointerLeave event before onClick on touch devices.
427
+ state.isOverTarget = false;
428
+ }
429
+ };
430
+
431
+ let onPointerCancel = (e: PointerEvent) => {
432
+ cancelEvent(e);
433
+ };
434
+
435
+ addGlobalListener(getOwnerDocument(state.target), 'pointerup', onPointerUp, false);
436
+ addGlobalListener(getOwnerDocument(state.target), 'pointercancel', onPointerCancel, false);
437
+ return () => {
438
+ removeGlobalListener(getOwnerDocument(state.target), 'pointerup', onPointerUp, false);
439
+ removeGlobalListener(getOwnerDocument(state.target), 'pointercancel', onPointerCancel, false);
440
+ };
441
+ } else if (isPointerPressed === 'mouse' && process.env.NODE_ENV === 'test') {
442
+ let onMouseUp = (e: MouseEvent) => {
443
+ // Only handle left clicks
444
+ if (e.button !== 0) {
445
+ return;
446
+ }
447
+
448
+ if (state.ignoreEmulatedMouseEvents) {
449
+ state.ignoreEmulatedMouseEvents = false;
450
+ return;
451
+ }
452
+
453
+ if (state.target && state.target.contains(e.target as Element) && state.pointerType != null) {
454
+ // Wait for onClick to fire onPress. This avoids browser issues when the DOM
455
+ // is mutated between onMouseUp and onClick, and is more compatible with third party libraries.
456
+ } else {
457
+ cancelEvent(e);
458
+ }
459
+
460
+ state.isOverTarget = false;
461
+ };
462
+
463
+ addGlobalListener(getOwnerDocument(state.target), 'mouseup', onMouseUp, false);
464
+ return () => {
465
+ removeGlobalListener(getOwnerDocument(state.target), 'mouseup', onMouseUp, false);
466
+ };
467
+ } else if (isPointerPressed === 'touch' && process.env.NODE_ENV === 'test') {
468
+ let onScroll = (e: Event) => {
469
+ if (state.isPressed && nodeContains(getEventTarget(e), state.target)) {
470
+ cancelEvent({
471
+ currentTarget: state.target,
472
+ shiftKey: false,
473
+ ctrlKey: false,
474
+ metaKey: false,
475
+ altKey: false
476
+ });
477
+ }
478
+ };
479
+
480
+ addGlobalListener(getOwnerWindow(state.target), 'scroll', onScroll, true);
481
+ return () => {
482
+ removeGlobalListener(getOwnerWindow(state.target), 'scroll', onScroll, true);
483
+ };
484
+ }
485
+ }, [isPointerPressed, addGlobalListener, removeGlobalListener]);
316
486
 
317
487
  let pressProps = useMemo(() => {
318
488
  let state = ref.current;
@@ -330,20 +500,9 @@ export function usePress(props: PressHookProps): PressResult {
330
500
  if (!state.isPressed && !e.repeat) {
331
501
  state.target = e.currentTarget;
332
502
  state.isPressed = true;
503
+ setIsElemKeyPressed(true);
333
504
  state.pointerType = 'keyboard';
334
505
  shouldStopPropagation = triggerPressStart(e, 'keyboard');
335
-
336
- // Focus may move before the key up event, so register the event on the document
337
- // instead of the same element where the key down event occurred. Make it capturing so that it will trigger
338
- // before stopPropagation from useKeyboard on a child element may happen and thus we can still call triggerPress for the parent element.
339
- let originalTarget = e.currentTarget;
340
- let pressUp = (e) => {
341
- if (isValidKeyboardEvent(e, originalTarget) && !e.repeat && nodeContains(originalTarget, getEventTarget(e)) && state.target) {
342
- triggerPressUp(createEvent(state.target, e), 'keyboard');
343
- }
344
- };
345
-
346
- addGlobalListener(getOwnerDocument(e.currentTarget), 'keyup', chain(pressUp, onKeyUp), true);
347
506
  }
348
507
 
349
508
  if (shouldStopPropagation) {
@@ -374,7 +533,7 @@ export function usePress(props: PressHookProps): PressResult {
374
533
  if (isDisabled) {
375
534
  e.preventDefault();
376
535
  }
377
-
536
+
378
537
  // If triggered from a screen reader or by using element.click(),
379
538
  // trigger as if it were a keyboard click.
380
539
  if (!state.ignoreEmulatedMouseEvents && !state.isPressed && (state.pointerType === 'virtual' || isVirtualClick(e.nativeEvent))) {
@@ -401,44 +560,6 @@ export function usePress(props: PressHookProps): PressResult {
401
560
  }
402
561
  };
403
562
 
404
- let onKeyUp = (e: KeyboardEvent) => {
405
- if (state.isPressed && state.target && isValidKeyboardEvent(e, state.target)) {
406
- if (shouldPreventDefaultKeyboard(getEventTarget(e), e.key)) {
407
- e.preventDefault();
408
- }
409
-
410
- let target = getEventTarget(e);
411
- let wasPressed = nodeContains(state.target, getEventTarget(e));
412
- triggerPressEnd(createEvent(state.target, e), 'keyboard', wasPressed);
413
- if (wasPressed) {
414
- triggerSyntheticClick(e, state.target);
415
- }
416
- removeAllGlobalListeners();
417
-
418
- // If a link was triggered with a key other than Enter, open the URL ourselves.
419
- // This means the link has a role override, and the default browser behavior
420
- // only applies when using the Enter key.
421
- if (e.key !== 'Enter' && isHTMLAnchorLink(state.target) && nodeContains(state.target, target) && !e[LINK_CLICKED]) {
422
- // Store a hidden property on the event so we only trigger link click once,
423
- // even if there are multiple usePress instances attached to the element.
424
- e[LINK_CLICKED] = true;
425
- openLink(state.target, e, false);
426
- }
427
-
428
- state.isPressed = false;
429
- state.metaKeyEvents?.delete(e.key);
430
- } else if (e.key === 'Meta' && state.metaKeyEvents?.size) {
431
- // If we recorded keydown events that occurred while the Meta key was pressed,
432
- // and those haven't received keyup events already, fire keyup events ourselves.
433
- // See comment above for more info about the macOS bug causing this.
434
- let events = state.metaKeyEvents;
435
- state.metaKeyEvents = undefined;
436
- for (let event of events.values()) {
437
- state.target?.dispatchEvent(new KeyboardEvent('keyup', event));
438
- }
439
- }
440
- };
441
-
442
563
  if (typeof PointerEvent !== 'undefined') {
443
564
  pressProps.onPointerDown = (e) => {
444
565
  // Only handle left clicks, and ignore events that bubbled through portals.
@@ -460,6 +581,7 @@ export function usePress(props: PressHookProps): PressResult {
460
581
  let shouldStopPropagation = true;
461
582
  if (!state.isPressed) {
462
583
  state.isPressed = true;
584
+ setIsPointerPressed('pointer');
463
585
  state.isOverTarget = true;
464
586
  state.activePointerId = e.pointerId;
465
587
  state.target = e.currentTarget as FocusableElement;
@@ -476,9 +598,6 @@ export function usePress(props: PressHookProps): PressResult {
476
598
  if ('releasePointerCapture' in target) {
477
599
  target.releasePointerCapture(e.pointerId);
478
600
  }
479
-
480
- addGlobalListener(getOwnerDocument(e.currentTarget), 'pointerup', onPointerUp, false);
481
- addGlobalListener(getOwnerDocument(e.currentTarget), 'pointercancel', onPointerCancel, false);
482
601
  }
483
602
 
484
603
  if (shouldStopPropagation) {
@@ -530,46 +649,6 @@ export function usePress(props: PressHookProps): PressResult {
530
649
  }
531
650
  };
532
651
 
533
- let onPointerUp = (e: PointerEvent) => {
534
- if (e.pointerId === state.activePointerId && state.isPressed && e.button === 0 && state.target) {
535
- if (nodeContains(state.target, getEventTarget(e)) && state.pointerType != null) {
536
- // Wait for onClick to fire onPress. This avoids browser issues when the DOM
537
- // is mutated between onPointerUp and onClick, and is more compatible with third party libraries.
538
- // https://github.com/adobe/react-spectrum/issues/1513
539
- // https://issues.chromium.org/issues/40732224
540
- // However, iOS and Android do not focus or fire onClick after a long press.
541
- // We work around this by triggering a click ourselves after a timeout.
542
- // This timeout is canceled during the click event in case the real one fires first.
543
- // The timeout must be at least 32ms, because Safari on iOS delays the click event on
544
- // non-form elements without certain ARIA roles (for hover emulation).
545
- // https://github.com/WebKit/WebKit/blob/dccfae42bb29bd4bdef052e469f604a9387241c0/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm#L875-L892
546
- let clicked = false;
547
- let timeout = setTimeout(() => {
548
- if (state.isPressed && state.target instanceof HTMLElement) {
549
- if (clicked) {
550
- cancel(e);
551
- } else {
552
- focusWithoutScrolling(state.target);
553
- state.target.click();
554
- }
555
- }
556
- }, 80);
557
- // Use a capturing listener to track if a click occurred.
558
- // If stopPropagation is called it may never reach our handler.
559
- addGlobalListener(e.currentTarget as Document, 'click', () => clicked = true, true);
560
- state.disposables.push(() => clearTimeout(timeout));
561
- } else {
562
- cancel(e);
563
- }
564
-
565
- // Ignore subsequent onPointerLeave event before onClick on touch devices.
566
- state.isOverTarget = false;
567
- }
568
- };
569
-
570
- let onPointerCancel = (e: PointerEvent) => {
571
- cancel(e);
572
- };
573
652
 
574
653
  pressProps.onDragStart = (e) => {
575
654
  if (!nodeContains(e.currentTarget, getEventTarget(e.nativeEvent))) {
@@ -595,6 +674,7 @@ export function usePress(props: PressHookProps): PressResult {
595
674
  }
596
675
 
597
676
  state.isPressed = true;
677
+ setIsPointerPressed('mouse');
598
678
  state.isOverTarget = true;
599
679
  state.target = e.currentTarget;
600
680
  state.pointerType = isVirtualClick(e.nativeEvent) ? 'virtual' : 'mouse';
@@ -611,8 +691,6 @@ export function usePress(props: PressHookProps): PressResult {
611
691
  state.disposables.push(dispose);
612
692
  }
613
693
  }
614
-
615
- addGlobalListener(getOwnerDocument(e.currentTarget), 'mouseup', onMouseUp, false);
616
694
  };
617
695
 
618
696
  pressProps.onMouseEnter = (e) => {
@@ -658,27 +736,6 @@ export function usePress(props: PressHookProps): PressResult {
658
736
  }
659
737
  };
660
738
 
661
- let onMouseUp = (e: MouseEvent) => {
662
- // Only handle left clicks
663
- if (e.button !== 0) {
664
- return;
665
- }
666
-
667
- if (state.ignoreEmulatedMouseEvents) {
668
- state.ignoreEmulatedMouseEvents = false;
669
- return;
670
- }
671
-
672
- if (state.target && state.target.contains(e.target as Element) && state.pointerType != null) {
673
- // Wait for onClick to fire onPress. This avoids browser issues when the DOM
674
- // is mutated between onMouseUp and onClick, and is more compatible with third party libraries.
675
- } else {
676
- cancel(e);
677
- }
678
-
679
- state.isOverTarget = false;
680
- };
681
-
682
739
  pressProps.onTouchStart = (e) => {
683
740
  if (!nodeContains(e.currentTarget, getEventTarget(e.nativeEvent))) {
684
741
  return;
@@ -692,6 +749,7 @@ export function usePress(props: PressHookProps): PressResult {
692
749
  state.ignoreEmulatedMouseEvents = true;
693
750
  state.isOverTarget = true;
694
751
  state.isPressed = true;
752
+ setIsPointerPressed('touch');
695
753
  state.target = e.currentTarget;
696
754
  state.pointerType = 'touch';
697
755
 
@@ -703,8 +761,6 @@ export function usePress(props: PressHookProps): PressResult {
703
761
  if (shouldStopPropagation) {
704
762
  e.stopPropagation();
705
763
  }
706
-
707
- addGlobalListener(getOwnerWindow(e.currentTarget), 'scroll', onScroll, true);
708
764
  };
709
765
 
710
766
  pressProps.onTouchMove = (e) => {
@@ -760,6 +816,7 @@ export function usePress(props: PressHookProps): PressResult {
760
816
  }
761
817
 
762
818
  state.isPressed = false;
819
+ setIsPointerPressed(null);
763
820
  state.activePointerId = null;
764
821
  state.isOverTarget = false;
765
822
  state.ignoreEmulatedMouseEvents = true;
@@ -780,18 +837,6 @@ export function usePress(props: PressHookProps): PressResult {
780
837
  }
781
838
  };
782
839
 
783
- let onScroll = (e: Event) => {
784
- if (state.isPressed && nodeContains(getEventTarget(e), state.target)) {
785
- cancel({
786
- currentTarget: state.target,
787
- shiftKey: false,
788
- ctrlKey: false,
789
- metaKey: false,
790
- altKey: false
791
- });
792
- }
793
- };
794
-
795
840
  pressProps.onDragStart = (e) => {
796
841
  if (!nodeContains(e.currentTarget, getEventTarget(e.nativeEvent))) {
797
842
  return;
@@ -803,7 +848,6 @@ export function usePress(props: PressHookProps): PressResult {
803
848
 
804
849
  return pressProps;
805
850
  }, [
806
- addGlobalListener,
807
851
  isDisabled,
808
852
  preventFocusOnPress,
809
853
  removeAllGlobalListeners,
package/src/utils.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import {FocusableElement} from '@react-types/shared';
14
- import {focusWithoutScrolling, getOwnerWindow, isFocusable, useEffectEvent, useLayoutEffect} from '@react-aria/utils';
14
+ import {focusWithoutScrolling, getOwnerWindow, isFocusable, useLayoutEffect} from '@react-aria/utils';
15
15
  import {FocusEvent as ReactFocusEvent, SyntheticEvent, useCallback, useRef} from 'react';
16
16
 
17
17
  // Turn a native event into a React synthetic event.
@@ -48,10 +48,6 @@ export function useSyntheticBlurEvent<Target extends Element = Element>(onBlur:
48
48
  };
49
49
  }, []);
50
50
 
51
- let dispatchBlur = useEffectEvent((e: ReactFocusEvent<Target>) => {
52
- onBlur?.(e);
53
- });
54
-
55
51
  // This function is called during a React onFocus event.
56
52
  return useCallback((e: ReactFocusEvent<Target>) => {
57
53
  // React does not fire onBlur when an element is disabled. https://github.com/facebook/react/issues/9142
@@ -73,7 +69,7 @@ export function useSyntheticBlurEvent<Target extends Element = Element>(onBlur:
73
69
  if (target.disabled) {
74
70
  // For backward compatibility, dispatch a (fake) React synthetic event.
75
71
  let event = createSyntheticEvent<ReactFocusEvent<Target>>(e);
76
- dispatchBlur(event);
72
+ onBlur?.(event);
77
73
  }
78
74
 
79
75
  // We no longer need the MutationObserver once the target is blurred.
@@ -96,7 +92,7 @@ export function useSyntheticBlurEvent<Target extends Element = Element>(onBlur:
96
92
 
97
93
  stateRef.current.observer.observe(target, {attributes: true, attributeFilter: ['disabled']});
98
94
  }
99
- }, [dispatchBlur]);
95
+ }, [onBlur]);
100
96
  }
101
97
 
102
98
  export let ignoreFocusEvent = false;