@@ -19,11 +19,12 @@ export const NotificationWrapper = (props: {
1919
2020 const { notification, swipeThreshold} = props ;
2121 const mobile = useMobile ( ) ;
22- const [ wrapperMaxHeight , setWrapperMaxHeight ] = React . useState < number | undefined > ( undefined ) ;
2322 const [ isRemoved , setIsRemoved ] = React . useState ( false ) ;
2423
2524 React . useEffect ( ( ) => {
26- if ( ! ref . current ) {
25+ const element = ref . current ;
26+
27+ if ( ! element ) {
2728 if ( ! notification . archived && isRemoved ) {
2829 setIsRemoved ( false ) ;
2930 }
@@ -34,36 +35,34 @@ export const NotificationWrapper = (props: {
3435 const listener = ( event : TransitionEvent ) => {
3536 if ( event . propertyName === 'max-height' ) {
3637 setIsRemoved ( true ) ;
37- ref . current ? .removeEventListener ( 'transitionend' , listener ) ;
38+ element . removeEventListener ( 'transitionend' , listener ) ;
3839 }
3940 } ;
4041
41- ref . current . addEventListener ( 'transitionend' , listener ) ;
42+ element . addEventListener ( 'transitionend' , listener ) ;
43+
44+ element . style . maxHeight = `${ element . scrollHeight } px` ;
45+ element . style . transition = 'max-height 0.3s' ;
4246
43- ref . current . style . transition = 'max-height 0.3s' ;
44- setWrapperMaxHeight ( 0 ) ;
47+ // Firefox batches style changes made within a single frame, so setting maxHeight
48+ // to scrollHeight and then to 0px in the same frame skips the transition entirely.
49+ // Two nested requestAnimationFrame calls guarantee the browser commits the initial
50+ // maxHeight in one frame before applying 0px in the next, so the animation runs.
51+ requestAnimationFrame ( ( ) => {
52+ requestAnimationFrame ( ( ) => {
53+ element . style . maxHeight = '0px' ;
54+ } ) ;
55+ } ) ;
4556
4657 return ( ) => {
47- ref . current ? .removeEventListener ( 'transitionend' , listener ) ;
58+ element . removeEventListener ( 'transitionend' , listener ) ;
4859 } ;
49- } else {
50- setIsRemoved ( false ) ;
51-
52- setTimeout ( ( ) => {
53- if ( ! ref . current ) return ;
54-
55- ref . current . style . transition = 'none' ;
56- ref . current . style . maxHeight = 'none' ;
57-
58- const maxHeight = ref . current ?. getBoundingClientRect ( ) . height ?? 0 ;
59- setWrapperMaxHeight ( maxHeight ) ;
60- } , 0 ) ;
61-
62- return ( ) => { } ;
6360 }
64- } , [ ref , notification . archived , isRemoved ] ) ;
6561
66- const style = wrapperMaxHeight === undefined ? { } : { maxHeight : `${ wrapperMaxHeight } px` } ;
62+ setIsRemoved ( false ) ;
63+
64+ return ( ) => { } ;
65+ } , [ notification . archived , isRemoved ] ) ;
6766
6867 if ( isRemoved ) {
6968 return null ;
@@ -78,15 +77,15 @@ export const NotificationWrapper = (props: {
7877 active : Boolean ( notification . onClick ) ,
7978 } ) }
8079 ref = { ref }
81- style = { style }
8280 >
8381 { mobile && notification . swipeActions ? (
8482 < NotificationWithSwipe
8583 notification = { notification }
8684 swipeThreshold = { swipeThreshold }
85+ wrapperRef = { ref }
8786 />
8887 ) : (
89- < Notification notification = { notification } />
88+ < Notification notification = { notification } wrapperRef = { ref } />
9089 ) }
9190 </ div >
9291 </ li >
0 commit comments