1- import contains from 'dom-helpers/contains' ;
21import listen from 'dom-helpers/listen' ;
32import ownerDocument from 'dom-helpers/ownerDocument' ;
4- import { useCallback , useEffect , useRef } from 'react' ;
3+ import { useEffect } from 'react' ;
54
65import useEventCallback from '@restart/hooks/useEventCallback' ;
7- import warning from 'warning' ;
6+ import useClickOutside , {
7+ ClickOutsideOptions ,
8+ getRefTarget ,
9+ } from './useClickOutside' ;
810
911const escapeKeyCode = 27 ;
1012const noop = ( ) => { } ;
1113
12- export type MouseEvents = {
13- [ K in keyof GlobalEventHandlersEventMap ] : GlobalEventHandlersEventMap [ K ] extends MouseEvent
14- ? K
15- : never ;
16- } [ keyof GlobalEventHandlersEventMap ] ;
17-
18- function isLeftClickEvent ( event : MouseEvent ) {
19- return event . button === 0 ;
20- }
21-
22- function isModifiedEvent ( event : MouseEvent ) {
23- return ! ! ( event . metaKey || event . altKey || event . ctrlKey || event . shiftKey ) ;
24- }
25-
26- const getRefTarget = (
27- ref : React . RefObject < Element > | Element | null | undefined ,
28- ) => ref && ( 'current' in ref ? ref . current : ref ) ;
29-
30- export interface RootCloseOptions {
14+ export interface RootCloseOptions extends ClickOutsideOptions {
3115 disabled ?: boolean ;
32- clickTrigger ?: MouseEvents ;
3316}
17+
3418/**
3519 * The `useRootClose` hook registers your callback on the document
3620 * when rendered. Powers the `<Overlay/>` component. This is used achieve modal
@@ -46,35 +30,11 @@ export interface RootCloseOptions {
4630function useRootClose (
4731 ref : React . RefObject < Element > | Element | null | undefined ,
4832 onRootClose : ( e : Event ) => void ,
49- { disabled, clickTrigger = 'click' } : RootCloseOptions = { } ,
33+ { disabled, clickTrigger } : RootCloseOptions = { } ,
5034) {
51- const preventMouseRootCloseRef = useRef ( false ) ;
5235 const onClose = onRootClose || noop ;
5336
54- const handleMouseCapture = useCallback (
55- ( e ) => {
56- const currentTarget = getRefTarget ( ref ) ;
57-
58- warning (
59- ! ! currentTarget ,
60- 'RootClose captured a close event but does not have a ref to compare it to. ' +
61- 'useRootClose(), should be passed a ref that resolves to a DOM node' ,
62- ) ;
63-
64- preventMouseRootCloseRef . current =
65- ! currentTarget ||
66- isModifiedEvent ( e ) ||
67- ! isLeftClickEvent ( e ) ||
68- ! ! contains ( currentTarget , e . target ) ;
69- } ,
70- [ ref ] ,
71- ) ;
72-
73- const handleMouse = useEventCallback ( ( e : MouseEvent ) => {
74- if ( ! preventMouseRootCloseRef . current ) {
75- onClose ( e ) ;
76- }
77- } ) ;
37+ useClickOutside ( ref , onClose , { disabled, clickTrigger } ) ;
7838
7939 const handleKeyUp = useEventCallback ( ( e : KeyboardEvent ) => {
8040 if ( e . keyCode === escapeKeyCode ) {
@@ -91,25 +51,6 @@ function useRootClose(
9151 // https://github.com/facebook/react/issues/20074
9252 let currentEvent = ( doc . defaultView || window ) . event ;
9353
94- // Use capture for this listener so it fires before React's listener, to
95- // avoid false positives in the contains() check below if the target DOM
96- // element is removed in the React mouse callback.
97- const removeMouseCaptureListener = listen (
98- doc as any ,
99- clickTrigger ,
100- handleMouseCapture ,
101- true ,
102- ) ;
103-
104- const removeMouseListener = listen ( doc as any , clickTrigger , ( e ) => {
105- // skip if this event is the same as the one running when we added the handlers
106- if ( e === currentEvent ) {
107- currentEvent = undefined ;
108- return ;
109- }
110- handleMouse ( e ) ;
111- } ) ;
112-
11354 const removeKeyupListener = listen ( doc as any , 'keyup' , ( e ) => {
11455 // skip if this event is the same as the one running when we added the handlers
11556 if ( e === currentEvent ) {
@@ -119,27 +60,10 @@ function useRootClose(
11960 handleKeyUp ( e ) ;
12061 } ) ;
12162
122- let mobileSafariHackListeners = [ ] as Array < ( ) => void > ;
123- if ( 'ontouchstart' in doc . documentElement ) {
124- mobileSafariHackListeners = [ ] . slice
125- . call ( doc . body . children )
126- . map ( ( el ) => listen ( el , 'mousemove' , noop ) ) ;
127- }
128-
12963 return ( ) => {
130- removeMouseCaptureListener ( ) ;
131- removeMouseListener ( ) ;
13264 removeKeyupListener ( ) ;
133- mobileSafariHackListeners . forEach ( ( remove ) => remove ( ) ) ;
13465 } ;
135- } , [
136- ref ,
137- disabled ,
138- clickTrigger ,
139- handleMouseCapture ,
140- handleMouse ,
141- handleKeyUp ,
142- ] ) ;
66+ } , [ ref , disabled , handleKeyUp ] ) ;
14367}
14468
14569export default useRootClose ;
0 commit comments