11import React , {
2+ Ref ,
23 useRef ,
34 useEffect ,
45 RefCallback ,
@@ -25,19 +26,36 @@ interface Props extends HTMLAttributes<HTMLElement> {
2526const eventTypeMapping = {
2627 click : 'onClick' ,
2728 focusin : 'onFocus' ,
28- focusout : 'onFocus ' ,
29+ focusout : 'onBlur ' ,
2930 mousedown : 'onMouseDown' ,
3031 mouseup : 'onMouseUp' ,
3132 touchstart : 'onTouchStart' ,
3233 touchend : 'onTouchEnd'
3334} ;
3435
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+
3552const ClickAwayListener : FunctionComponent < Props > = ( {
3653 children,
3754 onClickAway,
3855 focusEvent = 'focusin' ,
3956 mouseEvent = 'click' ,
40- touchEvent = 'touchend'
57+ touchEvent = 'touchend' ,
58+ ...rest
4159} ) => {
4260 const node = useRef < HTMLElement | null > ( null ) ;
4361 const bubbledEventTarget = useRef < EventTarget | null > ( null ) ;
@@ -69,19 +87,17 @@ const ClickAwayListener: FunctionComponent<Props> = ({
6987 }
7088 } ;
7189
72- const handleChildRef = ( childRef : HTMLElement ) => {
73- node . current = childRef ;
90+ let childRef : React . Ref < any > | null = null ;
7491
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+ }
7898
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 ] ) ;
85101
86102 useEffect ( ( ) => {
87103 const nodeDocument = node . current ?. ownerDocument ?? document ;
@@ -117,10 +133,11 @@ const ClickAwayListener: FunctionComponent<Props> = ({
117133
118134 return React . Children . only (
119135 cloneElement ( children as ReactElement < any > , {
120- ref : handleChildRef ,
136+ ref : combinedRef ,
121137 [ mappedFocusEvent ] : handleBubbledEvents ( mappedFocusEvent ) ,
122138 [ mappedMouseEvent ] : handleBubbledEvents ( mappedMouseEvent ) ,
123- [ mappedTouchEvent ] : handleBubbledEvents ( mappedTouchEvent )
139+ [ mappedTouchEvent ] : handleBubbledEvents ( mappedTouchEvent ) ,
140+ ...rest
124141 } )
125142 ) ;
126143} ;
0 commit comments