forked from Expensify/App
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: change modal animation library
- Loading branch information
1 parent
bbc0919
commit fb2f774
Showing
8 changed files
with
968 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import type {ReactNode} from 'react'; | ||
import React, {useEffect} from 'react'; | ||
import ReAnimated, {Easing, ReduceMotion, useAnimatedStyle, useSharedValue, withDelay, withTiming} from 'react-native-reanimated'; | ||
import {PressableWithFeedback} from '@components/Pressable'; | ||
import styles from './modal.style'; | ||
|
||
type BackdropProps = { | ||
getDeviceWidth: () => number; | ||
getDeviceHeight: () => number; | ||
backdropColor: string; | ||
hasBackdrop: boolean; | ||
customBackdrop?: ReactNode; | ||
isVisible: boolean; | ||
isTransitioning: boolean; | ||
backdropOpacity: number; | ||
onBackdropPress: () => void; | ||
}; | ||
|
||
function Backdrop({getDeviceWidth, backdropColor, getDeviceHeight, hasBackdrop, customBackdrop, isVisible, isTransitioning, backdropOpacity, onBackdropPress, ...props}: BackdropProps) { | ||
const opacityValue = useSharedValue(0); | ||
|
||
useEffect(() => { | ||
if (!isTransitioning) { | ||
return; | ||
} | ||
opacityValue.value = withDelay(0, withTiming(isVisible ? backdropOpacity : 0, {duration: 300, easing: Easing.inOut(Easing.ease), reduceMotion: ReduceMotion.Never})); | ||
}, [isVisible, isTransitioning, backdropOpacity, opacityValue]); | ||
|
||
const animatedStyle = useAnimatedStyle(() => { | ||
return { | ||
opacity: opacityValue.value, | ||
}; | ||
}); | ||
|
||
if (!hasBackdrop) { | ||
return null; | ||
} | ||
|
||
const hasCustomBackdrop = !!customBackdrop; | ||
|
||
const backdropComputedStyle = [ | ||
{ | ||
width: getDeviceWidth(), | ||
height: getDeviceHeight(), | ||
backgroundColor: backdropColor, | ||
}, | ||
]; | ||
|
||
const BDComponent = ( | ||
<ReAnimated.View | ||
style={[styles.backdrop, backdropComputedStyle, animatedStyle]} | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
> | ||
{hasCustomBackdrop && customBackdrop} | ||
</ReAnimated.View> | ||
); | ||
|
||
if (!hasCustomBackdrop) { | ||
return ( | ||
<PressableWithFeedback | ||
accessible | ||
accessibilityLabel="test" | ||
onPress={onBackdropPress} | ||
pressDimmingValue={1} | ||
> | ||
{BDComponent} | ||
</PressableWithFeedback> | ||
); | ||
} | ||
|
||
return BDComponent; | ||
} | ||
|
||
export default Backdrop; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React, {useEffect, useState} from 'react'; | ||
import type {LayoutChangeEvent} from 'react-native'; | ||
import Animated, {Easing, SlideInDown, useAnimatedRef, useAnimatedStyle, useSharedValue, withDelay, withTiming} from 'react-native-reanimated'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
|
||
type ContainerProps = Partial<ModalProps> & { | ||
isVisible: boolean; | ||
isContainerOpen: boolean; | ||
isTransitioning: boolean; | ||
isHeightCalculated: boolean; | ||
toggleCalculatedHeight: (value: boolean) => void; | ||
deviceHeight?: number | undefined | null; | ||
style?: any; | ||
onLayout?: (event: LayoutChangeEvent) => void; | ||
setMeasuredHeight: (value: number) => void; | ||
}; | ||
|
||
function Container({isVisible, isContainerOpen, isTransitioning, isHeightCalculated, toggleCalculatedHeight, style, onLayout, setMeasuredHeight, testName, ...props}: ContainerProps) { | ||
const styles = useThemeStyles(); | ||
const animatedRef = useAnimatedRef(); | ||
const [measuredHeight, setMH] = useState<number>(0); | ||
|
||
const translateY = useSharedValue(500); | ||
|
||
useEffect(() => { | ||
if (!isTransitioning) { | ||
return; | ||
} | ||
// console.log(testName, ' Container: isVisible & translateY', isVisible, isVisible ? 0 : 500); | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
translateY.value = withDelay(0, withTiming(isVisible ? 0 : 500, {duration: 300, easing: Easing.inOut(Easing.ease)})); | ||
setMH(0); | ||
}, [isVisible, isTransitioning]); | ||
|
||
const animatedStyles = useAnimatedStyle(() => { | ||
return { | ||
transform: [{translateY: translateY.value}], | ||
opacity: !isHeightCalculated || (isVisible !== isContainerOpen && !isTransitioning) ? 0 : 1, | ||
}; | ||
}); | ||
|
||
console.log('props: ', Object.keys(props).join('-')); | ||
return ( | ||
<Animated.View | ||
ref={animatedRef} | ||
style={[style, animatedStyles]} | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
{...props.panHandlers} | ||
> | ||
<Animated.View | ||
// TODO: check this 100% | ||
style={{width: '100%'}} | ||
onLayout={(event) => { | ||
if (!measuredHeight && event.nativeEvent.layout.height && measuredHeight !== event.nativeEvent.layout.height) { | ||
// translateY.value = 500; | ||
setMH(event.nativeEvent.layout.height); | ||
setMeasuredHeight(event.nativeEvent.layout.height); | ||
toggleCalculatedHeight(true); | ||
} | ||
}} | ||
> | ||
{props.children} | ||
</Animated.View> | ||
</Animated.View> | ||
); | ||
} | ||
|
||
export default Container; |
Oops, something went wrong.