|
1 |
| -import React, {useMemo} from 'react'; |
2 |
| -import {LayoutAnimation, StyleSheet} from 'react-native'; |
| 1 | +import React, {useMemo, useState} from 'react'; |
| 2 | +import {LayoutChangeEvent, StyleSheet} from 'react-native'; |
| 3 | +import {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; |
3 | 4 | import View from '../view';
|
4 | 5 | import TouchableOpacity from '../touchableOpacity';
|
5 |
| -import {useDidUpdate} from 'hooks'; |
6 | 6 |
|
7 | 7 | export type ExpandableSectionProps = {
|
8 | 8 | /**
|
@@ -38,44 +38,57 @@ export type ExpandableSectionProps = {
|
38 | 38 | */
|
39 | 39 |
|
40 | 40 | function ExpandableSection(props: ExpandableSectionProps) {
|
41 |
| - const {expanded, sectionHeader, children, top, testID} = props; |
| 41 | + const {expanded, sectionHeader, onPress, children, top, testID} = props; |
| 42 | + const [height, setHeight] = useState(0); |
| 43 | + const animatedHeight = useSharedValue(0); |
42 | 44 |
|
43 |
| - /** |
44 |
| - * TODO: move to reanimated LayoutAnimation after updating to version 2.3.0 |
45 |
| - * after migration, trigger the animation only in useDidUpdate. |
46 |
| - */ |
47 |
| - const animate = () => { |
48 |
| - LayoutAnimation.configureNext({...LayoutAnimation.Presets.easeInEaseOut, duration: 300}); |
49 |
| - }; |
| 45 | + const onLayout = (event: LayoutChangeEvent) => { |
| 46 | + const onLayoutHeight = event.nativeEvent.layout.height; |
50 | 47 |
|
51 |
| - const onPress = () => { |
52 |
| - props.onPress?.(); |
53 |
| - animate(); |
| 48 | + if (onLayoutHeight > 0 && height !== onLayoutHeight) { |
| 49 | + setHeight(onLayoutHeight); |
| 50 | + } |
54 | 51 | };
|
55 | 52 |
|
56 |
| - useDidUpdate(() => { |
57 |
| - animate(); |
58 |
| - }, [expanded]); |
| 53 | + const expandableStyle = useAnimatedStyle(() => { |
| 54 | + animatedHeight.value = expanded ? withTiming(height) : withTiming(0); |
| 55 | + |
| 56 | + return { |
| 57 | + height: animatedHeight.value |
| 58 | + }; |
| 59 | + }, [expanded, height]); |
| 60 | + |
| 61 | + const style = useMemo(() => [styles.hidden, expandableStyle], [expandableStyle]); |
59 | 62 |
|
60 | 63 | const accessibilityState = useMemo(() => {
|
61 | 64 | return {expanded};
|
62 | 65 | }, [expanded]);
|
63 | 66 |
|
| 67 | + const renderChildren = () => { |
| 68 | + return ( |
| 69 | + <View reanimated style={style}> |
| 70 | + <View absH onLayout={onLayout}> |
| 71 | + {children} |
| 72 | + </View> |
| 73 | + </View> |
| 74 | + ); |
| 75 | + }; |
| 76 | + |
64 | 77 | return (
|
65 |
| - <View style={styles.container}> |
66 |
| - {top && expanded && children} |
| 78 | + <View style={styles.hidden}> |
| 79 | + {top && renderChildren()} |
67 | 80 | <TouchableOpacity onPress={onPress} testID={testID} accessibilityState={accessibilityState}>
|
68 | 81 | {sectionHeader}
|
69 | 82 | </TouchableOpacity>
|
70 |
| - {!top && expanded && children} |
| 83 | + {!top && renderChildren()} |
71 | 84 | </View>
|
72 | 85 | );
|
73 | 86 | }
|
74 | 87 |
|
75 | 88 | export default ExpandableSection;
|
76 | 89 |
|
77 | 90 | const styles = StyleSheet.create({
|
78 |
| - container: { |
| 91 | + hidden: { |
79 | 92 | overflow: 'hidden'
|
80 | 93 | }
|
81 | 94 | });
|
0 commit comments