@@ -5,17 +5,61 @@ import classes from './plain.module.css';
5
5
6
6
import { useAnimationsFinished } from '../../../../../../packages/react/src/utils/useAnimationsFinished' ;
7
7
import { useEventCallback } from '../../../../../../packages/react/src/utils/useEventCallback' ;
8
+ import { useForkRef } from '../../../../../../packages/react/src/utils/useForkRef' ;
8
9
9
- function PlainCollapsible ( props : { keepMounted ?: boolean } ) {
10
- const { keepMounted = true } = props ;
10
+ const DEFAULT_OPEN = true ;
11
11
12
- const [ open , setOpen ] = React . useState ( false ) ;
12
+ function PlainCollapsible ( props : { defaultOpen ?: boolean ; keepMounted ?: boolean } ) {
13
+ const { keepMounted = true , defaultOpen = false } = props ;
14
+
15
+ const [ open , setOpen ] = React . useState ( defaultOpen ) ;
13
16
14
17
const [ mounted , setMounted ] = React . useState ( open ) ;
15
18
19
+ const [ height , setHeight ] = React . useState < number | undefined > ( undefined ) ;
20
+
21
+ const isInitiallyOpen = React . useRef ( open ) ;
22
+
23
+ const isHidden = React . useMemo ( ( ) => {
24
+ if ( keepMounted ) {
25
+ return ! open ;
26
+ }
27
+
28
+ return ! open && ! mounted ;
29
+ } , [ keepMounted , open , mounted ] ) ;
30
+
16
31
const panelRef : React . RefObject < HTMLElement | null > = React . useRef ( null ) ;
17
32
18
- const [ height , setHeight ] = React . useState ( 0 ) ;
33
+ const handlePanelRef = useEventCallback ( ( element : HTMLElement ) => {
34
+ if ( ! element ) {
35
+ return ;
36
+ }
37
+
38
+ element . style . setProperty ( 'display' , 'block' , 'important' ) ;
39
+
40
+ if ( height === undefined ) {
41
+ setHeight ( element . scrollHeight ) ;
42
+
43
+ if ( isInitiallyOpen . current ) {
44
+ element . style . transitionDuration = '0s' ;
45
+
46
+ requestAnimationFrame ( ( ) => {
47
+ setTimeout ( ( ) => {
48
+ element . style . transitionDuration = '' ;
49
+ if ( ! keepMounted ) {
50
+ isInitiallyOpen . current = false ;
51
+ }
52
+ } ) ;
53
+ } ) ;
54
+ }
55
+ }
56
+ } ) ;
57
+
58
+ const mergedRef = useForkRef ( panelRef , handlePanelRef ) ;
59
+
60
+ const abortControllerRef = React . useRef < AbortController | null > ( null ) ;
61
+
62
+ const runOnceAnimationsFinish = useAnimationsFinished ( panelRef , false ) ;
19
63
20
64
const handleTrigger = useEventCallback ( ( ) => {
21
65
const nextOpen = ! open ;
@@ -33,11 +77,15 @@ function PlainCollapsible(props: { keepMounted?: boolean }) {
33
77
return ;
34
78
}
35
79
36
- // panel.style.display = 'block';
37
-
38
80
// const targetHeight = panel.clientHeight;
81
+ panel . style . setProperty ( 'display' , 'block' , 'important' ) ;
39
82
40
83
if ( nextOpen ) {
84
+ if ( abortControllerRef . current != null ) {
85
+ abortControllerRef . current . abort ( ) ;
86
+ abortControllerRef . current = null ;
87
+ }
88
+
41
89
/* opening */
42
90
panel . style . opacity = '0' ;
43
91
panel . style . height = '0px' ;
@@ -50,28 +98,37 @@ function PlainCollapsible(props: { keepMounted?: boolean }) {
50
98
} else {
51
99
/* closing */
52
100
requestAnimationFrame ( ( ) => {
53
- // console.log('closing, scrollHeight', panel.scrollHeight);
54
101
panel . style . opacity = '0' ;
55
102
setHeight ( 0 ) ;
56
103
} ) ;
104
+
105
+ abortControllerRef . current = new AbortController ( ) ;
106
+
107
+ runOnceAnimationsFinish ( ( ) => {
108
+ panel . style . setProperty ( 'display' , 'none' ) ;
109
+ } , abortControllerRef . current . signal ) ;
57
110
}
58
111
} ) ;
59
112
60
- const runOnceAnimationsFinish = useAnimationsFinished ( panelRef ) ;
61
-
62
113
useEnhancedEffect ( ( ) => {
63
114
// This only matters when `keepMounted={false}`
64
115
if ( keepMounted ) {
65
116
return ;
66
117
}
67
- // console.log('useEnhancedEffect open', open, 'mounted', mounted);
68
118
69
119
const panel = panelRef . current ;
70
120
if ( ! panel ) {
71
121
return ;
72
122
}
73
123
74
124
if ( open ) {
125
+ // console.log('mounted?', mounted);
126
+
127
+ if ( abortControllerRef . current != null ) {
128
+ abortControllerRef . current . abort ( ) ;
129
+ abortControllerRef . current = null ;
130
+ }
131
+
75
132
/* opening */
76
133
panel . style . opacity = '0' ;
77
134
panel . style . height = '0px' ;
@@ -81,34 +138,30 @@ function PlainCollapsible(props: { keepMounted?: boolean }) {
81
138
panel . style . height = '' ;
82
139
setHeight ( panel . scrollHeight ) ;
83
140
} ) ;
141
+
142
+ setMounted ( true ) ;
84
143
} else {
85
144
/* closing */
86
145
requestAnimationFrame ( ( ) => {
87
- console . log ( 'closing, scrollHeight' , panel . scrollHeight ) ;
88
146
panel . style . opacity = '0' ;
89
147
setHeight ( 0 ) ;
90
148
} ) ;
91
149
150
+ abortControllerRef . current = new AbortController ( ) ;
151
+
92
152
runOnceAnimationsFinish ( ( ) => {
93
153
setMounted ( false ) ;
94
- } ) ;
154
+ } , abortControllerRef . current . signal ) ;
95
155
}
96
156
} , [ keepMounted , open , mounted , setMounted , runOnceAnimationsFinish ] ) ;
97
157
98
- const isHidden = React . useMemo ( ( ) => {
99
- if ( keepMounted ) {
100
- return ! open ;
101
- }
102
-
103
- return ! open && ! mounted ;
104
- } , [ keepMounted , open , mounted ] ) ;
105
-
106
158
return (
107
159
< div
108
160
className = { classes . Root }
109
161
style = { {
110
162
// @ts -ignore
111
- '--collapsible-panel-height' : `${ height } px` ,
163
+ '--collapsible-panel-height' :
164
+ height !== undefined ? `${ height } px` : undefined ,
112
165
} }
113
166
>
114
167
< button
@@ -124,7 +177,7 @@ function PlainCollapsible(props: { keepMounted?: boolean }) {
124
177
{ ( keepMounted || ( ! keepMounted && mounted ) ) && (
125
178
< div
126
179
// @ts -ignore
127
- ref = { panelRef }
180
+ ref = { mergedRef }
128
181
className = { classes . Panel }
129
182
{ ...{ [ open ? 'data-open' : 'data-closed' ] : '' } }
130
183
hidden = { isHidden }
@@ -147,9 +200,9 @@ function PlainCollapsible(props: { keepMounted?: boolean }) {
147
200
export default function App ( ) {
148
201
return (
149
202
< div className = { classes . wrapper } >
150
- < PlainCollapsible />
203
+ < PlainCollapsible defaultOpen = { DEFAULT_OPEN } />
151
204
152
- < PlainCollapsible keepMounted = { false } />
205
+ < PlainCollapsible keepMounted = { false } defaultOpen = { DEFAULT_OPEN } />
153
206
</ div >
154
207
) ;
155
208
}
0 commit comments