@@ -8,8 +8,9 @@ import { Portal } from 'react-native-teleport';
88import { useStableCallback } from '../../hooks' ;
99import {
1010 clearClosingPortalLayout ,
11+ createClosingPortalLayoutRegistrationId ,
1112 setClosingPortalLayout ,
12- useOverlayController ,
13+ useShouldTeleportToClosingPortal ,
1314} from '../../state-store' ;
1415
1516type PortalWhileClosingViewProps = {
@@ -39,15 +40,16 @@ type PortalWhileClosingViewProps = {
3940 * This wrapper moves that UI into the overlay host layer for the closing phase, so stacking stays correct.
4041 *
4142 * To use it, simply wrap any view that should remain on top while the overlay is closing, and pass a `portalHostName`
42- * and a `portalName`. Registration within the host layer will happen automatically, as will calculating layout .
43+ * and a `portalName`. Once the wrapped view has a valid measured layout, it can participate in the closing host layer .
4344 *
4445 * Behavior:
4546 * - renders children in place during normal operation
46- * - registers absolute layout for `portalHostName`
47+ * - registers absolute layout for `portalHostName` once a valid measurement exists
4748 * - while overlay state is `closing`, teleports children to the matching closing host
4849 * - renders a local placeholder while closing to preserve original layout space
4950 *
50- * Host registration is done once per key; subsequent layout updates are pushed via shared values.
51+ * Stack presence only starts after first valid measurement. That prevents unmeasured entries from taking over a host
52+ * slot and rendering with incomplete geometry.
5153 *
5254 * Note: As the `PortalWhileClosingView` relies heavily on being able to calculate the layout and positioning
5355 * properties of its children automatically, make sure that you do not wrap absolutely positioned views with
@@ -65,11 +67,18 @@ export const PortalWhileClosingView = ({
6567 portalHostName,
6668 portalName,
6769} : PortalWhileClosingViewProps ) => {
68- const { closing } = useOverlayController ( ) ;
6970 const containerRef = useRef < View | null > ( null ) ;
71+ const registrationIdRef = useRef < string | null > ( null ) ;
7072 const placeholderLayout = useSharedValue ( { h : 0 , w : 0 } ) ;
7173 const insets = useSafeAreaInsets ( ) ;
7274
75+ if ( ! registrationIdRef . current ) {
76+ registrationIdRef . current = createClosingPortalLayoutRegistrationId ( ) ;
77+ }
78+
79+ const registrationId = registrationIdRef . current ;
80+ const shouldTeleport = useShouldTeleportToClosingPortal ( portalHostName , registrationId ) ;
81+
7382 const syncPortalLayout = useStableCallback ( ( ) => {
7483 containerRef . current ?. measureInWindow ( ( x , y , width , height ) => {
7584 const absolute = {
@@ -83,14 +92,20 @@ export const PortalWhileClosingView = ({
8392
8493 placeholderLayout . value = { h : height , w : width } ;
8594
86- setClosingPortalLayout ( portalHostName , {
95+ setClosingPortalLayout ( portalHostName , registrationId , {
8796 ...absolute ,
8897 h : height ,
8998 w : width ,
9099 } ) ;
91100 } ) ;
92101 } ) ;
93102
103+ useEffect ( ( ) => {
104+ return ( ) => {
105+ clearClosingPortalLayout ( portalHostName , registrationId ) ;
106+ } ;
107+ } , [ portalHostName , registrationId ] ) ;
108+
94109 useEffect ( ( ) => {
95110 // Measure once after mount and layout settle.
96111 requestAnimationFrame ( ( ) => {
@@ -100,27 +115,19 @@ export const PortalWhileClosingView = ({
100115 } ) ;
101116 } , [ insets . top , portalHostName , syncPortalLayout ] ) ;
102117
103- const unregisterPortalHost = useStableCallback ( ( ) => clearClosingPortalLayout ( portalHostName ) ) ;
104-
105- useEffect ( ( ) => {
106- return ( ) => {
107- unregisterPortalHost ( ) ;
108- } ;
109- } , [ unregisterPortalHost ] ) ;
110-
111118 const placeholderStyle = useAnimatedStyle ( ( ) => ( {
112119 height : placeholderLayout . value . h ,
113120 width : placeholderLayout . value . w > 0 ? placeholderLayout . value . w : '100%' ,
114121 } ) ) ;
115122
116123 return (
117124 < >
118- < Portal hostName = { closing ? portalHostName : undefined } name = { portalName } >
125+ < Portal hostName = { shouldTeleport ? portalHostName : undefined } name = { portalName } >
119126 < View collapsable = { false } ref = { containerRef } onLayout = { syncPortalLayout } >
120127 { children }
121128 </ View >
122129 </ Portal >
123- { closing ? < Animated . View pointerEvents = 'none' style = { placeholderStyle } /> : null }
130+ { shouldTeleport ? < Animated . View pointerEvents = 'none' style = { placeholderStyle } /> : null }
124131 </ >
125132 ) ;
126133} ;
0 commit comments