@@ -84,7 +84,7 @@ export interface UseDismissProps {
8484 * ```
8585 * @default true
8686 */
87- outsidePress ?: boolean | ( ( event : MouseEvent ) => boolean ) ;
87+ outsidePress ?: boolean | ( ( event : MouseEvent | TouchEvent ) => boolean ) ;
8888 /**
8989 * The type of event to use to determine an outside "press".
9090 * - `intentional` requires the user to click outside intentionally, firing on `pointerup` for mouse, and requiring minimal `touchmove`s for touch.
@@ -155,7 +155,7 @@ export function useDismiss(
155155 startTime : number ;
156156 startX : number ;
157157 startY : number ;
158- dismissOnPointerUp : boolean ;
158+ dismissOnTouchEnd : boolean ;
159159 dismissOnMouseDown : boolean ;
160160 } | null > ( null ) ;
161161 const cancelDismissOnEndTimeout = useTimeout ( ) ;
@@ -242,7 +242,7 @@ export function useDismiss(
242242 } ) ;
243243
244244 const closeOnPressOutside = useEventCallback (
245- ( event : MouseEvent , endedOrStartedInside = false ) => {
245+ ( event : MouseEvent | PointerEvent | TouchEvent , endedOrStartedInside = false ) => {
246246 if ( shouldIgnoreEvent ( event ) ) {
247247 return ;
248248 }
@@ -299,7 +299,8 @@ export function useDismiss(
299299 }
300300
301301 // Check if the click occurred on the scrollbar
302- if ( isHTMLElement ( target ) ) {
302+ // Skip for touch events: scrollbars don't receive touch events on most platforms
303+ if ( isHTMLElement ( target ) && ! ( 'touches' in event ) ) {
303304 const lastTraversableNode = isLastTraversableNode ( target ) ;
304305 const style = getComputedStyle ( target ) ;
305306 const scrollRe = / a u t o | s c r o l l / ;
@@ -369,6 +370,7 @@ export function useDismiss(
369370 const handlePointerDown = useEventCallback ( ( event : PointerEvent ) => {
370371 if (
371372 getOutsidePressEvent ( ) !== 'sloppy' ||
373+ event . pointerType === 'touch' ||
372374 ! open ||
373375 ! enabled ||
374376 isEventTargetWithin ( event , elements . floating ) ||
@@ -377,25 +379,46 @@ export function useDismiss(
377379 return ;
378380 }
379381
380- if ( event . pointerType === 'touch' ) {
382+ closeOnPressOutside ( event ) ;
383+ } ) ;
384+
385+ const handleTouchStart = useEventCallback ( ( event : TouchEvent ) => {
386+ if (
387+ getOutsidePressEvent ( ) !== 'sloppy' ||
388+ ! open ||
389+ ! enabled ||
390+ isEventTargetWithin ( event , elements . floating ) ||
391+ isEventTargetWithin ( event , elements . domReference )
392+ ) {
393+ return ;
394+ }
395+
396+ const touch = event . touches [ 0 ] ;
397+ if ( touch ) {
381398 touchStateRef . current = {
382399 startTime : Date . now ( ) ,
383- startX : event . clientX ,
384- startY : event . clientY ,
385- dismissOnPointerUp : false ,
400+ startX : touch . clientX ,
401+ startY : touch . clientY ,
402+ dismissOnTouchEnd : false ,
386403 dismissOnMouseDown : true ,
387404 } ;
388405
389406 cancelDismissOnEndTimeout . start ( 1000 , ( ) => {
390407 if ( touchStateRef . current ) {
391- touchStateRef . current . dismissOnPointerUp = false ;
408+ touchStateRef . current . dismissOnTouchEnd = false ;
392409 touchStateRef . current . dismissOnMouseDown = false ;
393410 }
394411 } ) ;
395- return ;
396412 }
413+ } ) ;
397414
398- closeOnPressOutside ( event ) ;
415+ const handleTouchStartCapture = useEventCallback ( ( event : TouchEvent ) => {
416+ const target = getTarget ( event ) ;
417+ function callback ( ) {
418+ handleTouchStart ( event ) ;
419+ target ?. removeEventListener ( event . type , callback ) ;
420+ }
421+ target ?. addEventListener ( event . type , callback ) ;
399422 } ) ;
400423
401424 const closeOnPressOutsideCapture = useEventCallback ( ( event : PointerEvent | MouseEvent ) => {
@@ -433,23 +456,27 @@ export function useDismiss(
433456 target ?. addEventListener ( event . type , callback ) ;
434457 } ) ;
435458
436- const handlePointerMove = useEventCallback ( ( event : PointerEvent ) => {
459+ const handleTouchMove = useEventCallback ( ( event : TouchEvent ) => {
437460 if (
438461 getOutsidePressEvent ( ) !== 'sloppy' ||
439- event . pointerType !== 'touch' ||
440462 ! touchStateRef . current ||
441463 isEventTargetWithin ( event , elements . floating ) ||
442464 isEventTargetWithin ( event , elements . domReference )
443465 ) {
444466 return ;
445467 }
446468
447- const deltaX = Math . abs ( event . clientX - touchStateRef . current . startX ) ;
448- const deltaY = Math . abs ( event . clientY - touchStateRef . current . startY ) ;
469+ const touch = event . touches [ 0 ] ;
470+ if ( ! touch ) {
471+ return ;
472+ }
473+
474+ const deltaX = Math . abs ( touch . clientX - touchStateRef . current . startX ) ;
475+ const deltaY = Math . abs ( touch . clientY - touchStateRef . current . startY ) ;
449476 const distance = Math . sqrt ( deltaX * deltaX + deltaY * deltaY ) ;
450477
451478 if ( distance > 5 ) {
452- touchStateRef . current . dismissOnPointerUp = true ;
479+ touchStateRef . current . dismissOnTouchEnd = true ;
453480 }
454481
455482 if ( distance > 10 ) {
@@ -459,38 +486,37 @@ export function useDismiss(
459486 }
460487 } ) ;
461488
462- const handlePointerMoveCapture = useEventCallback ( ( event : PointerEvent ) => {
489+ const handleTouchMoveCapture = useEventCallback ( ( event : TouchEvent ) => {
463490 const target = getTarget ( event ) ;
464491 function callback ( ) {
465- handlePointerMove ( event ) ;
492+ handleTouchMove ( event ) ;
466493 target ?. removeEventListener ( event . type , callback ) ;
467494 }
468495 target ?. addEventListener ( event . type , callback ) ;
469496 } ) ;
470497
471- const handlePointerUp = useEventCallback ( ( event : PointerEvent ) => {
498+ const handleTouchEnd = useEventCallback ( ( event : TouchEvent ) => {
472499 if (
473500 getOutsidePressEvent ( ) !== 'sloppy' ||
474- event . pointerType !== 'touch' ||
475501 ! touchStateRef . current ||
476502 isEventTargetWithin ( event , elements . floating ) ||
477503 isEventTargetWithin ( event , elements . domReference )
478504 ) {
479505 return ;
480506 }
481507
482- if ( touchStateRef . current . dismissOnPointerUp ) {
508+ if ( touchStateRef . current . dismissOnTouchEnd ) {
483509 closeOnPressOutside ( event ) ;
484510 }
485511
486512 cancelDismissOnEndTimeout . clear ( ) ;
487513 touchStateRef . current = null ;
488514 } ) ;
489515
490- const handlePointerUpCapture = useEventCallback ( ( event : PointerEvent ) => {
516+ const handleTouchEndCapture = useEventCallback ( ( event : TouchEvent ) => {
491517 const target = getTarget ( event ) ;
492518 function callback ( ) {
493- handlePointerUp ( event ) ;
519+ handleTouchEnd ( event ) ;
494520 target ?. removeEventListener ( event . type , callback ) ;
495521 }
496522 target ?. addEventListener ( event . type , callback ) ;
@@ -554,8 +580,9 @@ export function useDismiss(
554580 outsidePressCapture ? closeOnPressOutsideCapture : closeOnPressOutside ,
555581 outsidePressCapture ,
556582 ) ;
557- doc . addEventListener ( 'pointermove' , handlePointerMoveCapture , outsidePressCapture ) ;
558- doc . addEventListener ( 'pointerup' , handlePointerUpCapture , outsidePressCapture ) ;
583+ doc . addEventListener ( 'touchstart' , handleTouchStartCapture , outsidePressCapture ) ;
584+ doc . addEventListener ( 'touchmove' , handleTouchMoveCapture , outsidePressCapture ) ;
585+ doc . addEventListener ( 'touchend' , handleTouchEndCapture , outsidePressCapture ) ;
559586 doc . addEventListener ( 'mousedown' , closeOnPressOutsideCapture , outsidePressCapture ) ;
560587 }
561588
@@ -610,8 +637,9 @@ export function useDismiss(
610637 outsidePressCapture ? closeOnPressOutsideCapture : closeOnPressOutside ,
611638 outsidePressCapture ,
612639 ) ;
613- doc . removeEventListener ( 'pointermove' , handlePointerMoveCapture , outsidePressCapture ) ;
614- doc . removeEventListener ( 'pointerup' , handlePointerUpCapture , outsidePressCapture ) ;
640+ doc . removeEventListener ( 'touchstart' , handleTouchStartCapture , outsidePressCapture ) ;
641+ doc . removeEventListener ( 'touchmove' , handleTouchMoveCapture , outsidePressCapture ) ;
642+ doc . removeEventListener ( 'touchend' , handleTouchEndCapture , outsidePressCapture ) ;
615643 doc . removeEventListener ( 'mousedown' , closeOnPressOutsideCapture , outsidePressCapture ) ;
616644 }
617645
@@ -639,8 +667,9 @@ export function useDismiss(
639667 outsidePressCapture ,
640668 closeOnPressOutsideCapture ,
641669 handlePointerDown ,
642- handlePointerMoveCapture ,
643- handlePointerUpCapture ,
670+ handleTouchStartCapture ,
671+ handleTouchMoveCapture ,
672+ handleTouchEndCapture ,
644673 trackPointerType ,
645674 ] ) ;
646675
@@ -689,7 +718,8 @@ export function useDismiss(
689718 onMouseDownCapture : handleCaptureInside ,
690719 onClickCapture : handleCaptureInside ,
691720 onMouseUpCapture : handleCaptureInside ,
692- onPointerMoveCapture : handleCaptureInside ,
721+ onTouchMoveCapture : handleCaptureInside ,
722+ onTouchEndCapture : handleCaptureInside ,
693723 } ) ,
694724 [ closeOnEscapeKeyDown , handlePressedInside , handleCaptureInside ] ,
695725 ) ;
0 commit comments