88 type LayoutRectangle ,
99 Dimensions ,
1010 Platform ,
11+ type LayoutChangeEvent ,
12+ InteractionManager ,
1113} from 'react-native' ;
1214
1315type TooltipPosition =
@@ -43,38 +45,81 @@ export const HighlightToolTip: React.FC<HighlightOverlayProps> = ({
4345 androidOffsetY = 0 ,
4446} ) => {
4547 const [ hole , setHole ] = useState < LayoutRectangle | null > ( null ) ;
48+ const [ tooltipLayout , setTooltipLayout ] = useState < {
49+ width : number ;
50+ height : number ;
51+ } | null > ( null ) ;
4652 const { width : screenWidth , height : screenHeight } = Dimensions . get ( 'window' ) ;
4753
4854 useEffect ( ( ) => {
49- if ( ! targetRef . current ) return ;
50- const handle = findNodeHandle ( targetRef . current ) ;
51- UIManager . measureInWindow (
52- handle ! ,
53- ( x : number , y : number , width : number , height : number ) => {
54- const isAndroid = Platform . OS === 'android' ;
55- setHole ( {
56- x,
57- y : isAndroid ? y + androidOffsetY : y ,
58- width,
59- height,
55+ const measure = ( retryCount = 0 ) => {
56+ if ( ! targetRef . current ) {
57+ if ( retryCount < 5 ) {
58+ setTimeout ( ( ) => measure ( retryCount + 1 ) , 100 ) ;
59+ }
60+ return ;
61+ }
62+ const handle = findNodeHandle ( targetRef . current ) ;
63+ if ( handle ) {
64+ UIManager . measureInWindow ( handle , ( x , y , width , height ) => {
65+ if (
66+ [ x , y , width , height ] . some (
67+ ( val ) => typeof val !== 'number' || isNaN ( val )
68+ ) ||
69+ ( width === 0 && height === 0 )
70+ ) {
71+ if ( retryCount < 5 ) {
72+ setTimeout ( ( ) => measure ( retryCount + 1 ) , 100 ) ;
73+ } else {
74+ console . warn (
75+ 'HighlightToolTip: Failed to measure target component after multiple retries.'
76+ ) ;
77+ onRequestClose ( ) ;
78+ }
79+ return ;
80+ }
81+
82+ const isAndroid = Platform . OS === 'android' ;
83+ setHole ( {
84+ x,
85+ y : isAndroid ? y + androidOffsetY : y ,
86+ width,
87+ height,
88+ } ) ;
6089 } ) ;
90+ } else if ( retryCount < 5 ) {
91+ setTimeout ( ( ) => measure ( retryCount + 1 ) , 100 ) ;
92+ } else {
93+ console . warn (
94+ 'HighlightToolTip: Could not find node handle for targetRef after multiple retries.'
95+ ) ;
96+ onRequestClose ( ) ;
6197 }
62- ) ;
63- } , [ targetRef , androidOffsetY ] ) ;
98+ } ;
99+
100+ const interactionHandle = InteractionManager . runAfterInteractions ( ( ) => {
101+ measure ( ) ;
102+ } ) ;
103+
104+ return ( ) => interactionHandle . cancel ( ) ;
105+ } , [ targetRef , androidOffsetY , onRequestClose ] ) ;
106+
107+ const onTooltipLayout = ( event : LayoutChangeEvent ) => {
108+ const { width, height } = event . nativeEvent . layout ;
109+ if ( tooltipLayout ?. width !== width || tooltipLayout ?. height !== height ) {
110+ setTooltipLayout ( { width, height } ) ;
111+ }
112+ } ;
64113
65114 const getTooltipPosition = ( ) => {
66- if ( ! hole ) return { top : 0 , left : 0 } ;
115+ if ( ! hole || ! tooltipLayout ) {
116+ return { top : 0 , left : 0 , opacity : 0 } ;
117+ }
67118
68119 const { x : offsetX = 0 , y : offsetY = 0 } = offset ;
69- // Adjust margin based on allowOverlap - use larger margin to prevent overlap
70- const margin = allowOverlap ? - 4 : 24 ; // Negative margin when overlap is allowed, otherwise larger margin
71-
72- // Calculate tooltip size dynamically based on screen size
73- const maxTooltipWidth = Math . min ( screenWidth * 0.8 , 320 ) ; // Max 80% of screen width or 320px
74- const tooltipWidth = maxTooltipWidth ;
75- const tooltipHeight = 120 ; // Adjust to actual height + extra space
120+ const margin = allowOverlap ? - 4 : 24 ;
121+ const { width : tooltipWidth , height : tooltipHeight } = tooltipLayout ;
76122
77- // Calculate initial position
78123 let calculatedPosition = { top : 0 , left : 0 } ;
79124
80125 switch ( tooltipPosition ) {
@@ -151,39 +196,26 @@ export const HighlightToolTip: React.FC<HighlightOverlayProps> = ({
151196 } ;
152197 }
153198
154- // Check screen boundaries and auto-adjust
155199 let { top, left } = calculatedPosition ;
156-
157- // Fixed margin for screen boundary adjustment (always positive)
158200 const boundaryMargin = 16 ;
159201
160- // Left boundary check
161202 if ( left < boundaryMargin ) {
162203 left = boundaryMargin ;
163204 }
164205
165- // Right boundary check - ensure tooltip doesn't extend beyond screen
166206 if ( left + tooltipWidth > screenWidth - boundaryMargin ) {
167207 left = screenWidth - tooltipWidth - boundaryMargin ;
168- // If still not enough space, adjust tooltip width
169- if ( left < boundaryMargin ) {
170- left = boundaryMargin ;
171- }
172208 }
173209
174- // Top boundary check
175210 if ( top < boundaryMargin ) {
176- // If it goes above, move to bottom - maintain allowOverlap setting
177211 if ( tooltipPosition . includes ( 'top' ) ) {
178212 top = hole . y + hole . height + ( allowOverlap ? - 4 : 24 ) ;
179213 } else {
180214 top = boundaryMargin ;
181215 }
182216 }
183217
184- // Bottom boundary check
185218 if ( top + tooltipHeight > screenHeight - boundaryMargin ) {
186- // If it goes below, move to top - maintain allowOverlap setting
187219 if ( tooltipPosition . includes ( 'bottom' ) ) {
188220 top = hole . y - tooltipHeight - ( allowOverlap ? - 4 : 24 ) ;
189221 } else {
@@ -194,11 +226,17 @@ export const HighlightToolTip: React.FC<HighlightOverlayProps> = ({
194226 return {
195227 top : top + offsetY ,
196228 left : left + offsetX ,
197- maxWidth : tooltipWidth ,
229+ opacity : 1 ,
230+ maxWidth : screenWidth * 0.9 ,
198231 } ;
199232 } ;
200233
201- if ( ! hole ) return null ;
234+ const isHoleLayoutInvalid =
235+ ! hole || [ hole . x , hole . y , hole . width , hole . height ] . some ( isNaN ) ;
236+
237+ if ( isHoleLayoutInvalid ) {
238+ return null ;
239+ }
202240
203241 const tooltipStyle = getTooltipPosition ( ) ;
204242
@@ -256,10 +294,12 @@ export const HighlightToolTip: React.FC<HighlightOverlayProps> = ({
256294
257295 { /* Tooltip */ }
258296 < View
297+ onLayout = { onTooltipLayout }
259298 style = { {
260299 position : 'absolute' ,
261300 top : tooltipStyle . top ,
262301 left : tooltipStyle . left ,
302+ opacity : tooltipStyle . opacity ,
263303 maxWidth : tooltipStyle . maxWidth ,
264304 } }
265305 >
0 commit comments