@@ -4,55 +4,56 @@ import { useEffect, useRef } from 'react'
4
4
5
5
export function Collapse ( {
6
6
children,
7
- className,
8
7
isOpen,
9
- horizontal = false
8
+ horizontal = false ,
9
+ openDuration = 500 ,
10
+ closeDuration = 300
10
11
} : {
11
12
children : ReactNode
12
- className ?: string
13
13
isOpen : boolean
14
14
horizontal ?: boolean
15
+ openDuration ?: number
16
+ closeDuration ?: number
15
17
} ) : ReactElement {
16
- const containerRef = useRef < HTMLDivElement > ( null )
17
- const innerRef = useRef < HTMLDivElement > ( null )
18
- const animationRef = useRef ( 0 )
18
+ const containerRef = useRef < HTMLDivElement > ( null ! )
19
19
const initialOpen = useRef ( isOpen )
20
+ const animationRef = useRef ( 0 )
20
21
const initialRender = useRef ( true )
21
-
22
22
useEffect ( ( ) => {
23
- const container = containerRef . current
24
- const inner = innerRef . current
25
23
const animation = animationRef . current
24
+ const container = containerRef . current
26
25
if ( animation ) {
27
26
clearTimeout ( animation )
27
+ animationRef . current = 0
28
28
}
29
- if ( initialRender . current || ! container || ! inner ) return
30
29
31
- container . classList . toggle ( '_duration-500' , ! isOpen )
32
- container . classList . toggle ( '_duration-300' , isOpen )
30
+ if ( initialRender . current ) {
31
+ return
32
+ }
33
+ const child = container . children [ 0 ] as HTMLDivElement
33
34
34
35
if ( horizontal ) {
35
36
// save initial width to avoid word wrapping when container width will be changed
36
- inner . style . width = `${ inner . clientWidth } px`
37
- container . style . width = `${ inner . clientWidth } px`
37
+ child . style . width = `${ child . clientWidth } px`
38
+ container . style . width = `${ child . clientWidth } px`
38
39
} else {
39
- container . style . height = `${ inner . clientHeight } px`
40
+ container . style . height = `${ child . clientHeight } px`
40
41
}
41
42
if ( isOpen ) {
42
43
animationRef . current = window . setTimeout ( ( ) => {
43
- // should be style property in kebab-case, not css class name
44
+ // should be style property in kebab-case, not CSS class name
44
45
container . style . removeProperty ( 'height' )
45
- } , 300 )
46
+ } , openDuration )
46
47
} else {
47
48
setTimeout ( ( ) => {
48
49
if ( horizontal ) {
49
- container . style . width = '0px '
50
+ container . style . width = '0 '
50
51
} else {
51
- container . style . height = '0px '
52
+ container . style . height = '0 '
52
53
}
53
- } , 0 )
54
+ } )
54
55
}
55
- } , [ horizontal , isOpen ] )
56
+ } , [ horizontal , isOpen , openDuration ] )
56
57
57
58
useEffect ( ( ) => {
58
59
initialRender . current = false
@@ -63,20 +64,14 @@ export function Collapse({
63
64
ref = { containerRef }
64
65
className = { cn (
65
66
'_transform-gpu _transition-all _ease-in-out motion-reduce:_transition-none' ,
66
- ! isOpen && ' _overflow-hidden'
67
+ isOpen ? '_opacity-100' : [ '_opacity-0' , ' _overflow-hidden']
67
68
) }
68
- style = { initialOpen . current || horizontal ? undefined : { height : 0 } }
69
+ style = { {
70
+ ...( initialOpen . current || horizontal ? undefined : { height : 0 } ) ,
71
+ transitionDuration : ( isOpen ? openDuration : closeDuration ) + 'ms'
72
+ } }
69
73
>
70
- < div
71
- ref = { innerRef }
72
- className = { cn (
73
- '_transition-opacity _duration-500 _ease-in-out motion-reduce:_transition-none' ,
74
- isOpen ? '_opacity-100' : '_opacity-0' ,
75
- className
76
- ) }
77
- >
78
- { children }
79
- </ div >
74
+ < div > { children } </ div >
80
75
</ div >
81
76
)
82
77
}
0 commit comments