1
1
import React , {
2
+ Ref ,
2
3
useRef ,
3
4
useEffect ,
4
5
RefCallback ,
@@ -25,19 +26,36 @@ interface Props extends HTMLAttributes<HTMLElement> {
25
26
const eventTypeMapping = {
26
27
click : 'onClick' ,
27
28
focusin : 'onFocus' ,
28
- focusout : 'onFocus ' ,
29
+ focusout : 'onBlur ' ,
29
30
mousedown : 'onMouseDown' ,
30
31
mouseup : 'onMouseUp' ,
31
32
touchstart : 'onTouchStart' ,
32
33
touchend : 'onTouchEnd'
33
34
} ;
34
35
36
+ const reactMajorVersion = parseInt ( React . version . split ( '.' ) [ 0 ] , 10 ) ;
37
+
38
+ const mergeRefs = < T extends any > (
39
+ refs : Array < Ref < T > | undefined | null >
40
+ ) : RefCallback < T > => {
41
+ return ( value ) => {
42
+ refs . forEach ( ( ref ) => {
43
+ if ( typeof ref === 'function' ) {
44
+ ref ( value ) ;
45
+ } else if ( ref != null ) {
46
+ ( ref as MutableRefObject < T | null > ) . current = value ;
47
+ }
48
+ } ) ;
49
+ } ;
50
+ } ;
51
+
35
52
const ClickAwayListener : FunctionComponent < Props > = ( {
36
53
children,
37
54
onClickAway,
38
55
focusEvent = 'focusin' ,
39
56
mouseEvent = 'click' ,
40
- touchEvent = 'touchend'
57
+ touchEvent = 'touchend' ,
58
+ ...rest
41
59
} ) => {
42
60
const node = useRef < HTMLElement | null > ( null ) ;
43
61
const bubbledEventTarget = useRef < EventTarget | null > ( null ) ;
@@ -69,19 +87,17 @@ const ClickAwayListener: FunctionComponent<Props> = ({
69
87
}
70
88
} ;
71
89
72
- const handleChildRef = ( childRef : HTMLElement ) => {
73
- node . current = childRef ;
90
+ let childRef : React . Ref < any > | null = null ;
74
91
75
- let { ref } = children as typeof children & {
76
- ref : RefCallback < HTMLElement > | MutableRefObject < HTMLElement > ;
77
- } ;
92
+ // For React 19+, we get the ref via props.ref
93
+ if ( reactMajorVersion >= 19 ) {
94
+ childRef = children . props ?. ref || null ;
95
+ } else if ( 'ref' in children ) {
96
+ childRef = ( children as any ) . ref ;
97
+ }
78
98
79
- if ( typeof ref === 'function' ) {
80
- ref ( childRef ) ;
81
- } else if ( ref ) {
82
- ref . current = childRef ;
83
- }
84
- } ;
99
+ // Create a combined ref handler
100
+ const combinedRef = mergeRefs ( [ node , childRef ] ) ;
85
101
86
102
useEffect ( ( ) => {
87
103
const nodeDocument = node . current ?. ownerDocument ?? document ;
@@ -117,10 +133,11 @@ const ClickAwayListener: FunctionComponent<Props> = ({
117
133
118
134
return React . Children . only (
119
135
cloneElement ( children as ReactElement < any > , {
120
- ref : handleChildRef ,
136
+ ref : combinedRef ,
121
137
[ mappedFocusEvent ] : handleBubbledEvents ( mappedFocusEvent ) ,
122
138
[ mappedMouseEvent ] : handleBubbledEvents ( mappedMouseEvent ) ,
123
- [ mappedTouchEvent ] : handleBubbledEvents ( mappedTouchEvent )
139
+ [ mappedTouchEvent ] : handleBubbledEvents ( mappedTouchEvent ) ,
140
+ ...rest
124
141
} )
125
142
) ;
126
143
} ;
0 commit comments