@@ -118,7 +118,7 @@ function Collapsible(props: {
118
118
* Explicitly set `display` to ensure the panel is actually rendered before
119
119
* measuring anything. `!important` is to needed to override a conflicting
120
120
* Tailwind v4 default that sets `display: none !important` on `[hidden]`:
121
- * https://github.com/tailwindlabs/tailwindcss/blame/main /packages/tailwindcss/preflight.css#L382
121
+ * https://github.com/tailwindlabs/tailwindcss/blob/cd154a4f471e7a63cc27cad15dada650de89d52b /packages/tailwindcss/preflight.css#L320-L326
122
122
*/
123
123
element . style . setProperty ( 'display' , 'block' , 'important' ) ; // TODO: maybe this can be set more conditionally
124
124
@@ -134,16 +134,11 @@ function Collapsible(props: {
134
134
element . setAttribute ( 'data-starting-style' , '' ) ;
135
135
}
136
136
137
- if ( ! shouldCancelInitialOpenTransitionRef . current && hiddenUntilFoundProp ) {
138
- element . setAttribute ( 'data-starting-style' , '' ) ;
139
- }
140
-
141
137
setHeight ( element . scrollHeight ) ;
142
138
element . style . removeProperty ( 'display' ) ;
143
139
144
140
if ( shouldCancelInitialOpenTransitionRef . current ) {
145
141
element . style . setProperty ( 'transition-duration' , '0s' ) ;
146
- // element.style.setProperty('display', 'none');
147
142
}
148
143
}
149
144
@@ -173,7 +168,8 @@ function Collapsible(props: {
173
168
const nextOpen = ! open ;
174
169
175
170
const panel = panelRef . current ;
176
- if ( panel && animationTypeRef . current === 'css-animation' ) {
171
+
172
+ if ( animationTypeRef . current === 'css-animation' && panel != null ) {
177
173
panel . style . removeProperty ( 'animation-name' ) ;
178
174
}
179
175
@@ -195,23 +191,26 @@ function Collapsible(props: {
195
191
}
196
192
setOpen ( nextOpen ) ;
197
193
194
+ /**
195
+ * When `keepMounted={false}` and when opening, the element isn't inserted
196
+ * in the DOM at this point so bail out here and resume in an effect.
197
+ */
198
198
if ( ! panel || animationTypeRef . current !== 'css-transition' ) {
199
199
return ;
200
200
}
201
201
202
202
panel . style . setProperty ( 'display' , 'block' , 'important' ) ;
203
203
204
204
if ( nextOpen ) {
205
+ /* opening */
205
206
if ( abortControllerRef . current != null ) {
206
207
abortControllerRef . current . abort ( ) ;
207
208
abortControllerRef . current = null ;
208
209
}
209
210
210
211
panel . style . removeProperty ( 'display' ) ;
211
212
panel . style . removeProperty ( 'content-visibility' ) ;
212
-
213
- /* opening */
214
- panel . style . height = '0px' ;
213
+ panel . style . setProperty ( 'height' , '0px' ) ;
215
214
216
215
requestAnimationFrame ( ( ) => {
217
216
panel . style . removeProperty ( 'height' ) ;
@@ -229,20 +228,19 @@ function Collapsible(props: {
229
228
abortControllerRef . current = new AbortController ( ) ;
230
229
231
230
runOnceAnimationsFinish ( ( ) => {
232
- // TODO: !important may be needed
233
- panel . style . setProperty ( 'display' , 'none' ) ;
234
- // panel.style.removeProperty('content-visibility');
231
+ panel . style . removeProperty ( 'display' ) ;
232
+ panel . style . removeProperty ( 'content-visibility' ) ;
235
233
abortControllerRef . current = null ;
236
234
} , abortControllerRef . current . signal ) ;
237
235
}
238
236
} ) ;
239
237
240
238
/**
241
- * This only handles `keepMounted={false}` as the state changes can't be done
242
- * in the event handler
239
+ * This only handles CSS transitions when `keepMounted={false}` as we may not
240
+ * have access to the panel element in the DOM in the trigger event handler.
243
241
*/
244
242
useEnhancedEffect ( ( ) => {
245
- if ( animationTypeRef . current !== 'css-transition' ) {
243
+ if ( animationTypeRef . current !== 'css-transition' || keepMounted ) {
246
244
return ;
247
245
}
248
246
@@ -253,10 +251,6 @@ function Collapsible(props: {
253
251
}
254
252
255
253
if ( open ) {
256
- if ( hiddenUntilFoundProp ) {
257
- return ;
258
- }
259
- console . log ( 'open here' ) ;
260
254
if ( abortControllerRef . current != null ) {
261
255
abortControllerRef . current . abort ( ) ;
262
256
abortControllerRef . current = null ;
@@ -273,7 +267,6 @@ function Collapsible(props: {
273
267
panel . style . removeProperty ( 'display' ) ;
274
268
275
269
panel . style . removeProperty ( 'height' ) ;
276
-
277
270
setHeight ( panel . scrollHeight ) ;
278
271
} ) ;
279
272
} else {
@@ -363,9 +356,24 @@ function Collapsible(props: {
363
356
useEnhancedEffect ( ( ) => {
364
357
const panel = panelRef . current ;
365
358
366
- if ( panel && hiddenUntilFoundProp && isHidden ) {
367
- // @ts -ignore
359
+ if (
360
+ panel &&
361
+ hiddenUntilFoundProp &&
362
+ animationTypeRef . current === 'css-transition' &&
363
+ isHidden
364
+ ) {
365
+ /**
366
+ * React only supports a boolean for the `hidden` attribute and forces
367
+ * legit string values to booleans so we have to force it back in the DOM
368
+ * when necessary: https://github.com/facebook/react/issues/24740
369
+ */
368
370
panel . setAttribute ( 'hidden' , 'until-found' ) ;
371
+ /**
372
+ * Set data-starting-style here to persist the closed styles, this is to
373
+ * prevent transitions from starting when the `hidden` attribute changes
374
+ * to `'until-found'` as they could have different `display` properties:
375
+ * https://github.com/tailwindlabs/tailwindcss/pull/14625
376
+ */
369
377
panel . setAttribute ( 'data-starting-style' , '' ) ;
370
378
}
371
379
} , [ hiddenUntilFoundProp , isHidden ] ) ;
@@ -378,11 +386,10 @@ function Collapsible(props: {
378
386
}
379
387
380
388
function handleBeforeMatch ( event : Event ) {
389
+ // TODO: probably remove this because beforematch isn't cancellable anyway
381
390
event . preventDefault ( ) ;
382
391
383
392
isBeforeMatchRef . current = true ;
384
-
385
- // beforematch only fires if the matching content is initially hidden
386
393
setOpen ( true ) ;
387
394
}
388
395
0 commit comments