@@ -41,14 +41,13 @@ export interface DisclosureAria {
41
41
* @param state - State for the disclosure, as returned by `useDisclosureState`.
42
42
* @param ref - A ref for the disclosure panel.
43
43
*/
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 {
45
45
let {
46
46
isDisabled
47
47
} = props ;
48
48
let triggerId = useId ( ) ;
49
49
let panelId = useId ( ) ;
50
50
let isSSR = useIsSSR ( ) ;
51
- let supportsBeforeMatch = ! isSSR && 'onbeforematch' in document . body ;
52
51
53
52
let raf = useRef < number | null > ( null ) ;
54
53
@@ -66,22 +65,64 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
66
65
} , [ ref , state ] ) ;
67
66
68
67
// @ts -ignore https://github.com/facebook/react/pull/24741
69
- useEvent ( ref , 'beforematch' , supportsBeforeMatch ? handleBeforeMatch : null ) ;
68
+ useEvent ( ref , 'beforematch' , handleBeforeMatch ) ;
70
69
70
+ let isExpandedRef = useRef < boolean | null > ( null ) ;
71
71
useLayoutEffect ( ( ) => {
72
72
// Cancel any pending RAF to prevent stale updates
73
73
if ( raf . current ) {
74
74
cancelAnimationFrame ( raf . current ) ;
75
75
}
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
+ }
82
121
}
122
+
123
+ isExpandedRef . current = state . isExpanded ;
83
124
}
84
- } , [ isDisabled , ref , state . isExpanded , supportsBeforeMatch ] ) ;
125
+ } , [ isDisabled , ref , state . isExpanded , isSSR ] ) ;
85
126
86
127
useEffect ( ( ) => {
87
128
return ( ) => {
@@ -114,7 +155,7 @@ export function useDisclosure(props: AriaDisclosureProps, state: DisclosureState
114
155
role : 'group' ,
115
156
'aria-labelledby' : triggerId ,
116
157
'aria-hidden' : ! state . isExpanded ,
117
- hidden : supportsBeforeMatch ? true : ! state . isExpanded
158
+ hidden : isSSR ? ! state . isExpanded : undefined
118
159
}
119
160
} ;
120
161
}
0 commit comments