@@ -126,36 +126,34 @@ export const BottomSheet = React.forwardRef<
126126 headerRef,
127127 } )
128128
129+ // Setup refs that are used in cases where full control is needed over when a side effect is executed
130+ const maxHeightRef = useRef ( maxHeight )
131+ const minSnapRef = useRef ( minSnap )
132+ const maxSnapRef = useRef ( maxSnap )
133+ const findSnapRef = useRef ( findSnap )
134+ // Sync the refs with current state, giving the spring full control over when to respond to changes
135+ useEffect ( ( ) => {
136+ maxHeightRef . current = maxHeight
137+ maxSnapRef . current = maxSnap
138+ minSnapRef . current = minSnap
139+ findSnapRef . current = findSnap
140+ } , [ findSnap , maxHeight , maxSnap , minSnap ] )
141+
129142 const defaultSnapRef = useRef ( 0 )
130143 useEffect ( ( ) => {
131144 // Wait with selectin default snap until element dimensions are measured
132145 if ( ! ready ) return
133146 console . count ( 'selecting default snap' )
134147
135- defaultSnapRef . current = findSnap ( getDefaultSnap )
136- } , [ findSnap , getDefaultSnap , ready ] )
148+ defaultSnapRef . current = findSnapRef . current ( getDefaultSnap )
149+ } , [ getDefaultSnap , ready ] )
150+
151+ // @TODO can be renamed or deleted
137152 // Wether to interpolate refs or states, useful when needing to transition between changed snapshot bounds
138153 const shouldInterpolateRefs = useRef ( false )
139- const minSnapRef = useRef ( minSnap )
140- const maxSnapRef = useRef ( maxSnap )
154+
141155 // Adjust the height whenever the snap points are changed due to resize events
142156 useEffect ( ( ) => {
143- // If we're not gonna interpolate the refs we'll just quietly update them
144- if ( ! shouldInterpolateRefs . current ) {
145- maxSnapRef . current = maxSnap
146- minSnapRef . current = minSnap
147-
148- console . log (
149- 'Resizing due to' ,
150- 'maxSnap:' ,
151- maxSnapRef . current !== maxSnap ,
152- 'minSnap:' ,
153- minSnapRef . current !== minSnap
154- )
155-
156- return
157- }
158-
159157 if ( shouldInterpolateRefs . current ) {
160158 set ( {
161159 // @ts -expect-error
@@ -164,7 +162,7 @@ export const BottomSheet = React.forwardRef<
164162
165163 await onSpringStartRef . current ?.( { type : 'RESIZE' } )
166164
167- const snap = findSnap ( heightRef . current )
165+ const snap = findSnapRef . current ( heightRef . current )
168166
169167 console . log ( 'animate resize' )
170168
@@ -177,8 +175,8 @@ export const BottomSheet = React.forwardRef<
177175 minSnapRef . current !== minSnap
178176 )
179177 // Adjust bounds to have enough room for the transition
180- maxSnapRef . current = Math . max ( snap , heightRef . current )
181- minSnapRef . current = Math . min ( snap , heightRef . current )
178+ // maxSnapRef.current = Math.max(snap, heightRef.current)
179+ // minSnapRef.current = Math.min(snap, heightRef.current)
182180 console . log ( 'new maxSnapRef' , maxSnapRef . current )
183181
184182 heightRef . current = snap
@@ -187,19 +185,22 @@ export const BottomSheet = React.forwardRef<
187185 await next ( {
188186 y : snap ,
189187 backdrop : 1 ,
188+ maxHeight,
189+ maxSnap,
190+ minSnap,
190191 immediate : prefersReducedMotion . current ,
191192 } )
192193
193- maxSnapRef . current = maxSnap
194- minSnapRef . current = minSnap
194+ // maxSnapRef.current = maxSnap
195+ // minSnapRef.current = minSnap
195196
196197 onSpringEndRef . current ?.( { type : 'RESIZE' } )
197198
198199 console . groupEnd ( )
199200 } ,
200201 } )
201202 }
202- } , [ findSnap , lastSnapRef , maxSnap , minSnap , prefersReducedMotion , set ] )
203+ } , [ lastSnapRef , maxHeight , maxSnap , minSnap , prefersReducedMotion , set ] )
203204 useImperativeHandle (
204205 forwardRef ,
205206 ( ) => ( {
@@ -261,7 +262,11 @@ export const BottomSheet = React.forwardRef<
261262 await next ( {
262263 y : defaultSnapRef . current ,
263264 backdrop : 1 ,
264- opacity : 1 ,
265+ ready : 1 ,
266+ maxHeight : maxHeightRef . current ,
267+ maxSnap : maxSnapRef . current ,
268+ // Using defaultSnapRef instead of minSnapRef to avoid animating `height` on open
269+ minSnap : defaultSnapRef . current ,
265270 immediate : true ,
266271 } )
267272
@@ -281,7 +286,11 @@ export const BottomSheet = React.forwardRef<
281286 await next ( {
282287 y : defaultSnapRef . current ,
283288 backdrop : 0 ,
284- opacity : 0 ,
289+ ready : 0 ,
290+ maxHeight : maxHeightRef . current ,
291+ maxSnap : maxSnapRef . current ,
292+ // Using defaultSnapRef instead of minSnapRef to avoid animating `height` on open
293+ minSnap : defaultSnapRef . current ,
285294 immediate : true ,
286295 } )
287296
@@ -298,7 +307,11 @@ export const BottomSheet = React.forwardRef<
298307 await next ( {
299308 y : 0 ,
300309 backdrop : 0 ,
301- opacity : 1 ,
310+ ready : 1 ,
311+ maxHeight : maxHeightRef . current ,
312+ maxSnap : maxSnapRef . current ,
313+ // Using defaultSnapRef instead of minSnapRef to avoid animating `height` on open
314+ minSnap : defaultSnapRef . current ,
302315 immediate : true ,
303316 } )
304317
@@ -309,7 +322,11 @@ export const BottomSheet = React.forwardRef<
309322 await next ( {
310323 y : defaultSnapRef . current ,
311324 backdrop : 1 ,
312- opacity : 1 ,
325+ ready : 1 ,
326+ maxHeight : maxHeightRef . current ,
327+ maxSnap : maxSnapRef . current ,
328+ // Using defaultSnapRef instead of minSnapRef to avoid animating `height` on open
329+ minSnap : defaultSnapRef . current ,
313330 immediate : prefersReducedMotion . current ,
314331 } )
315332 }
@@ -368,18 +385,30 @@ export const BottomSheet = React.forwardRef<
368385
369386 if ( maybeCancel ( ) ) return
370387
371- heightRef . current = 0
388+ // Edge case for already closed
389+ if ( heightRef . current === 0 ) {
390+ onSpringEndRef . current ?.( { type : 'CLOSE' } )
391+ return
392+ }
372393
373- console . log ( 'animate close' )
394+ // Avoid animating the height property on close and stay within FLIP bounds by upping the minSnap
395+ next ( {
396+ minSnap : heightRef . current ,
397+ immediate : true ,
398+ } )
399+
400+ heightRef . current = 0
374401
375402 await next ( {
376403 y : 0 ,
377404 backdrop : 0 ,
405+ maxHeight : maxHeightRef . current ,
406+ maxSnap : maxSnapRef . current ,
378407 immediate : prefersReducedMotion . current ,
379408 } )
380409 if ( maybeCancel ( ) ) return
381410
382- await next ( { opacity : 0 , immediate : true } )
411+ await next ( { ready : 0 , immediate : true } )
383412
384413 if ( maybeCancel ( ) ) return
385414
@@ -496,7 +525,10 @@ export const BottomSheet = React.forwardRef<
496525 set ( {
497526 y : newY ,
498527 backdrop : clamp ( newY / minSnapRef . current , 0 , 1 ) ,
499- opacity : 1 ,
528+ ready : 1 ,
529+ maxHeight : maxHeightRef . current ,
530+ maxSnap : maxSnapRef . current ,
531+ minSnap : minSnapRef . current ,
500532 immediate : prefersReducedMotion . current || down ,
501533 config : {
502534 mass : relativeVelocity ,
@@ -535,17 +567,7 @@ export const BottomSheet = React.forwardRef<
535567 throw new TypeError ( 'minSnapRef is NaN!!' )
536568 }
537569
538- const interpolations = useSpringInterpolations ( {
539- spring,
540- maxHeight,
541- // Select which values to use in the interpolation based on wether it's safe to trust the height
542- maxSnapRef : shouldInterpolateRefs . current
543- ? maxSnapRef
544- : { current : maxSnap } ,
545- minSnapRef : shouldInterpolateRefs . current
546- ? minSnapRef
547- : { current : minSnap } ,
548- } )
570+ const interpolations = useSpringInterpolations ( { spring } )
549571
550572 return (
551573 < animated . div
@@ -563,7 +585,9 @@ export const BottomSheet = React.forwardRef<
563585 // but allow overriding them/disabling them
564586 ...style ,
565587 // Not overridable as the "focus lock with opacity 0" trick rely on it
566- opacity : spring . opacity ,
588+ // @TODO the line below only fails on TS <4
589+ // @ts -ignore
590+ opacity : spring . ready ,
567591 // Allows interactions on the rest of the page before the close transition is finished
568592 pointerEvents : ! ready || off ? 'none' : undefined ,
569593 } }
0 commit comments