@@ -41,14 +41,13 @@ export interface DisclosureAria {
4141 * @param state - State for the disclosure, as returned by `useDisclosureState`.
4242 * @param ref - A ref for the disclosure panel.
4343 */
44- export function useDisclosure ( props : AriaDisclosureProps , state : DisclosureState , ref : RefObject < Element | null > ) : DisclosureAria {
44+ export function useDisclosure ( props : AriaDisclosureProps , state : DisclosureState , ref : RefObject < HTMLElement | null > ) : DisclosureAria {
4545 let {
4646 isDisabled
4747 } = props ;
4848 let triggerId = useId ( ) ;
4949 let panelId = useId ( ) ;
5050 let isSSR = useIsSSR ( ) ;
51- let supportsBeforeMatch = ! isSSR && 'onbeforematch' in document . body ;
5251
5352 let raf = useRef < number | null > ( null ) ;
5453
@@ -66,22 +65,64 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
6665 } , [ ref , state ] ) ;
6766
6867 // @ts -ignore https://github.com/facebook/react/pull/24741
69- useEvent ( ref , 'beforematch' , supportsBeforeMatch ? handleBeforeMatch : null ) ;
68+ useEvent ( ref , 'beforematch' , handleBeforeMatch ) ;
7069
70+ let isExpandedRef = useRef < boolean | null > ( null ) ;
7171 useLayoutEffect ( ( ) => {
7272 // Cancel any pending RAF to prevent stale updates
7373 if ( raf . current ) {
7474 cancelAnimationFrame ( raf . current ) ;
7575 }
76- // Until React supports hidden="until-found": https://github.com/facebook/react/pull/24741
77- if ( supportsBeforeMatch && ref . current && ! isDisabled ) {
78- if ( state . isExpanded ) {
79- ref . current . removeAttribute ( 'hidden' ) ;
80- } else {
81- ref . current . setAttribute ( 'hidden' , 'until-found' ) ;
76+ if ( ref . current && ! isDisabled && ! isSSR ) {
77+ let panel = ref . current ;
78+
79+ if ( isExpandedRef . current == null || typeof panel . getAnimations !== 'function' ) {
80+ // On initial render (and in tests), set attributes without animation.
81+ if ( state . isExpanded ) {
82+ panel . removeAttribute ( 'hidden' ) ;
83+ panel . style . setProperty ( '--disclosure-panel-width' , 'auto' ) ;
84+ panel . style . setProperty ( '--disclosure-panel-height' , 'auto' ) ;
85+ } else {
86+ panel . setAttribute ( 'hidden' , 'until-found' ) ;
87+ panel . style . setProperty ( '--disclosure-panel-width' , '0px' ) ;
88+ panel . style . setProperty ( '--disclosure-panel-height' , '0px' ) ;
89+ }
90+ } else if ( state . isExpanded !== isExpandedRef . current ) {
91+ if ( state . isExpanded ) {
92+ panel . removeAttribute ( 'hidden' ) ;
93+
94+ // Set the width and height as pixels so they can be animated.
95+ panel . style . setProperty ( '--disclosure-panel-width' , panel . scrollWidth + 'px' ) ;
96+ panel . style . setProperty ( '--disclosure-panel-height' , panel . scrollHeight + 'px' ) ;
97+
98+ Promise . all ( panel . getAnimations ( ) . map ( a => a . finished ) )
99+ . then ( ( ) => {
100+ // After the animations complete, switch back to auto so the content can resize.
101+ panel . style . setProperty ( '--disclosure-panel-width' , 'auto' ) ;
102+ panel . style . setProperty ( '--disclosure-panel-height' , 'auto' ) ;
103+ } )
104+ . catch ( ( ) => { } ) ;
105+ } else {
106+ panel . style . setProperty ( '--disclosure-panel-width' , panel . scrollWidth + 'px' ) ;
107+ panel . style . setProperty ( '--disclosure-panel-height' , panel . scrollHeight + 'px' ) ;
108+
109+ // Force style re-calculation to trigger animations.
110+ window . getComputedStyle ( panel ) . height ;
111+
112+ // Animate to zero size.
113+ panel . style . setProperty ( '--disclosure-panel-width' , '0px' ) ;
114+ panel . style . setProperty ( '--disclosure-panel-height' , '0px' ) ;
115+
116+ // Wait for animations to apply the hidden attribute.
117+ Promise . all ( panel . getAnimations ( ) . map ( a => a . finished ) )
118+ . then ( ( ) => panel . setAttribute ( 'hidden' , 'until-found' ) )
119+ . catch ( ( ) => { } ) ;
120+ }
82121 }
122+
123+ isExpandedRef . current = state . isExpanded ;
83124 }
84- } , [ isDisabled , ref , state . isExpanded , supportsBeforeMatch ] ) ;
125+ } , [ isDisabled , ref , state . isExpanded , isSSR ] ) ;
85126
86127 useEffect ( ( ) => {
87128 return ( ) => {
@@ -114,7 +155,7 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
114155 role : 'group' ,
115156 'aria-labelledby' : triggerId ,
116157 'aria-hidden' : ! state . isExpanded ,
117- hidden : supportsBeforeMatch ? true : ! state . isExpanded
158+ hidden : isSSR ? ! state . isExpanded : undefined
118159 }
119160 } ;
120161}
0 commit comments