@react-aria/interactions 3.25.6 → 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 +186 -114
  29. package/dist/usePress.main.js.map +1 -1
  30. package/dist/usePress.mjs +188 -116
  31. package/dist/usePress.module.js +188 -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 +3 -3
  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 +183 -147
  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,23 +293,24 @@ 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]);
299
304
 
300
- let triggerClick = useEffectEvent((e: RMouseEvent<FocusableElement>) => {
305
+ let triggerClick = useCallback((e: RMouseEvent<FocusableElement>) => {
301
306
  if (isDisabled) {
302
307
  return;
303
308
  }
304
309
 
305
310
  onClick?.(e);
306
- });
311
+ }, [isDisabled, onClick]);
307
312
 
308
- let triggerSyntheticClick = useEffectEvent((e: KeyboardEvent | TouchEvent, target: FocusableElement) => {
313
+ let triggerSyntheticClick = useCallback((e: KeyboardEvent | TouchEvent, target: FocusableElement) => {
309
314
  if (isDisabled) {
310
315
  return;
311
316
  }
@@ -320,7 +325,164 @@ export function usePress(props: PressHookProps): PressResult {
320
325
  setEventTarget(event, target);
321
326
  onClick(createSyntheticEvent(event));
322
327
  }
323
- });
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]);
324
486
 
325
487
  let pressProps = useMemo(() => {
326
488
  let state = ref.current;
@@ -338,20 +500,9 @@ export function usePress(props: PressHookProps): PressResult {
338
500
  if (!state.isPressed && !e.repeat) {
339
501
  state.target = e.currentTarget;
340
502
  state.isPressed = true;
503
+ setIsElemKeyPressed(true);
341
504
  state.pointerType = 'keyboard';
342
505
  shouldStopPropagation = triggerPressStart(e, 'keyboard');
343
-
344
- // Focus may move before the key up event, so register the event on the document
345
- // instead of the same element where the key down event occurred. Make it capturing so that it will trigger
346
- // before stopPropagation from useKeyboard on a child element may happen and thus we can still call triggerPress for the parent element.
347
- let originalTarget = e.currentTarget;
348
- let pressUp = (e) => {
349
- if (isValidKeyboardEvent(e, originalTarget) && !e.repeat && nodeContains(originalTarget, getEventTarget(e)) && state.target) {
350
- triggerPressUp(createEvent(state.target, e), 'keyboard');
351
- }
352
- };
353
-
354
- addGlobalListener(getOwnerDocument(e.currentTarget), 'keyup', chain(pressUp, onKeyUp), true);
355
506
  }
356
507
 
357
508
  if (shouldStopPropagation) {
@@ -409,44 +560,6 @@ export function usePress(props: PressHookProps): PressResult {
409
560
  }
410
561
  };
411
562
 
412
- let onKeyUp = (e: KeyboardEvent) => {
413
- if (state.isPressed && state.target && isValidKeyboardEvent(e, state.target)) {
414
- if (shouldPreventDefaultKeyboard(getEventTarget(e), e.key)) {
415
- e.preventDefault();
416
- }
417
-
418
- let target = getEventTarget(e);
419
- let wasPressed = nodeContains(state.target, getEventTarget(e));
420
- triggerPressEnd(createEvent(state.target, e), 'keyboard', wasPressed);
421
- if (wasPressed) {
422
- triggerSyntheticClick(e, state.target);
423
- }
424
- removeAllGlobalListeners();
425
-
426
- // If a link was triggered with a key other than Enter, open the URL ourselves.
427
- // This means the link has a role override, and the default browser behavior
428
- // only applies when using the Enter key.
429
- if (e.key !== 'Enter' && isHTMLAnchorLink(state.target) && nodeContains(state.target, target) && !e[LINK_CLICKED]) {
430
- // Store a hidden property on the event so we only trigger link click once,
431
- // even if there are multiple usePress instances attached to the element.
432
- e[LINK_CLICKED] = true;
433
- openLink(state.target, e, false);
434
- }
435
-
436
- state.isPressed = false;
437
- state.metaKeyEvents?.delete(e.key);
438
- } else if (e.key === 'Meta' && state.metaKeyEvents?.size) {
439
- // If we recorded keydown events that occurred while the Meta key was pressed,
440
- // and those haven't received keyup events already, fire keyup events ourselves.
441
- // See comment above for more info about the macOS bug causing this.
442
- let events = state.metaKeyEvents;
443
- state.metaKeyEvents = undefined;
444
- for (let event of events.values()) {
445
- state.target?.dispatchEvent(new KeyboardEvent('keyup', event));
446
- }
447
- }
448
- };
449
-
450
563
  if (typeof PointerEvent !== 'undefined') {
451
564
  pressProps.onPointerDown = (e) => {
452
565
  // Only handle left clicks, and ignore events that bubbled through portals.
@@ -468,6 +581,7 @@ export function usePress(props: PressHookProps): PressResult {
468
581
  let shouldStopPropagation = true;
469
582
  if (!state.isPressed) {
470
583
  state.isPressed = true;
584
+ setIsPointerPressed('pointer');
471
585
  state.isOverTarget = true;
472
586
  state.activePointerId = e.pointerId;
473
587
  state.target = e.currentTarget as FocusableElement;
@@ -484,9 +598,6 @@ export function usePress(props: PressHookProps): PressResult {
484
598
  if ('releasePointerCapture' in target) {
485
599
  target.releasePointerCapture(e.pointerId);
486
600
  }
487
-
488
- addGlobalListener(getOwnerDocument(e.currentTarget), 'pointerup', onPointerUp, false);
489
- addGlobalListener(getOwnerDocument(e.currentTarget), 'pointercancel', onPointerCancel, false);
490
601
  }
491
602
 
492
603
  if (shouldStopPropagation) {
@@ -538,46 +649,6 @@ export function usePress(props: PressHookProps): PressResult {
538
649
  }
539
650
  };
540
651
 
541
- let onPointerUp = (e: PointerEvent) => {
542
- if (e.pointerId === state.activePointerId && state.isPressed && e.button === 0 && state.target) {
543
- if (nodeContains(state.target, getEventTarget(e)) && state.pointerType != null) {
544
- // Wait for onClick to fire onPress. This avoids browser issues when the DOM
545
- // is mutated between onPointerUp and onClick, and is more compatible with third party libraries.
546
- // https://github.com/adobe/react-spectrum/issues/1513
547
- // https://issues.chromium.org/issues/40732224
548
- // However, iOS and Android do not focus or fire onClick after a long press.
549
- // We work around this by triggering a click ourselves after a timeout.
550
- // This timeout is canceled during the click event in case the real one fires first.
551
- // The timeout must be at least 32ms, because Safari on iOS delays the click event on
552
- // non-form elements without certain ARIA roles (for hover emulation).
553
- // https://github.com/WebKit/WebKit/blob/dccfae42bb29bd4bdef052e469f604a9387241c0/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm#L875-L892
554
- let clicked = false;
555
- let timeout = setTimeout(() => {
556
- if (state.isPressed && state.target instanceof HTMLElement) {
557
- if (clicked) {
558
- cancel(e);
559
- } else {
560
- focusWithoutScrolling(state.target);
561
- state.target.click();
562
- }
563
- }
564
- }, 80);
565
- // Use a capturing listener to track if a click occurred.
566
- // If stopPropagation is called it may never reach our handler.
567
- addGlobalListener(e.currentTarget as Document, 'click', () => clicked = true, true);
568
- state.disposables.push(() => clearTimeout(timeout));
569
- } else {
570
- cancel(e);
571
- }
572
-
573
- // Ignore subsequent onPointerLeave event before onClick on touch devices.
574
- state.isOverTarget = false;
575
- }
576
- };
577
-
578
- let onPointerCancel = (e: PointerEvent) => {
579
- cancel(e);
580
- };
581
652
 
582
653
  pressProps.onDragStart = (e) => {
583
654
  if (!nodeContains(e.currentTarget, getEventTarget(e.nativeEvent))) {
@@ -603,6 +674,7 @@ export function usePress(props: PressHookProps): PressResult {
603
674
  }
604
675
 
605
676
  state.isPressed = true;
677
+ setIsPointerPressed('mouse');
606
678
  state.isOverTarget = true;
607
679
  state.target = e.currentTarget;
608
680
  state.pointerType = isVirtualClick(e.nativeEvent) ? 'virtual' : 'mouse';
@@ -619,8 +691,6 @@ export function usePress(props: PressHookProps): PressResult {
619
691
  state.disposables.push(dispose);
620
692
  }
621
693
  }
622
-
623
- addGlobalListener(getOwnerDocument(e.currentTarget), 'mouseup', onMouseUp, false);
624
694
  };
625
695
 
626
696
  pressProps.onMouseEnter = (e) => {
@@ -666,27 +736,6 @@ export function usePress(props: PressHookProps): PressResult {
666
736
  }
667
737
  };
668
738
 
669
- let onMouseUp = (e: MouseEvent) => {
670
- // Only handle left clicks
671
- if (e.button !== 0) {
672
- return;
673
- }
674
-
675
- if (state.ignoreEmulatedMouseEvents) {
676
- state.ignoreEmulatedMouseEvents = false;
677
- return;
678
- }
679
-
680
- if (state.target && state.target.contains(e.target as Element) && state.pointerType != null) {
681
- // Wait for onClick to fire onPress. This avoids browser issues when the DOM
682
- // is mutated between onMouseUp and onClick, and is more compatible with third party libraries.
683
- } else {
684
- cancel(e);
685
- }
686
-
687
- state.isOverTarget = false;
688
- };
689
-
690
739
  pressProps.onTouchStart = (e) => {
691
740
  if (!nodeContains(e.currentTarget, getEventTarget(e.nativeEvent))) {
692
741
  return;
@@ -700,6 +749,7 @@ export function usePress(props: PressHookProps): PressResult {
700
749
  state.ignoreEmulatedMouseEvents = true;
701
750
  state.isOverTarget = true;
702
751
  state.isPressed = true;
752
+ setIsPointerPressed('touch');
703
753
  state.target = e.currentTarget;
704
754
  state.pointerType = 'touch';
705
755
 
@@ -711,8 +761,6 @@ export function usePress(props: PressHookProps): PressResult {
711
761
  if (shouldStopPropagation) {
712
762
  e.stopPropagation();
713
763
  }
714
-
715
- addGlobalListener(getOwnerWindow(e.currentTarget), 'scroll', onScroll, true);
716
764
  };
717
765
 
718
766
  pressProps.onTouchMove = (e) => {
@@ -768,6 +816,7 @@ export function usePress(props: PressHookProps): PressResult {
768
816
  }
769
817
 
770
818
  state.isPressed = false;
819
+ setIsPointerPressed(null);
771
820
  state.activePointerId = null;
772
821
  state.isOverTarget = false;
773
822
  state.ignoreEmulatedMouseEvents = true;
@@ -788,18 +837,6 @@ export function usePress(props: PressHookProps): PressResult {
788
837
  }
789
838
  };
790
839
 
791
- let onScroll = (e: Event) => {
792
- if (state.isPressed && nodeContains(getEventTarget(e), state.target)) {
793
- cancel({
794
- currentTarget: state.target,
795
- shiftKey: false,
796
- ctrlKey: false,
797
- metaKey: false,
798
- altKey: false
799
- });
800
- }
801
- };
802
-
803
840
  pressProps.onDragStart = (e) => {
804
841
  if (!nodeContains(e.currentTarget, getEventTarget(e.nativeEvent))) {
805
842
  return;
@@ -811,7 +848,6 @@ export function usePress(props: PressHookProps): PressResult {
811
848
 
812
849
  return pressProps;
813
850
  }, [
814
- addGlobalListener,
815
851
  isDisabled,
816
852
  preventFocusOnPress,
817
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;