import { useImperativeHandle, forwardRef, useEffect, useState, useRef } from "react"; import { TouchableWithoutFeedback, Animated, Platform, Easing, View } from "react-native"; import type IModalProps from "./type"; import { type IModalRef } from "./type"; import stylesheet from "./stylesheet"; import { NCoreUIKitTheme } from "../../core/hooks"; import { type RefForwardingComponent } from "../../types"; import { Portal } from "../../helpers/portalize"; import { uuid } from "../../utils"; /** * A generic modal * @param props {@link IModalProps} * @returns Element */ const Modal: RefForwardingComponent = ({ isScaleAnimatedOnlyOpen = true, closeAnimationDelay = 100, isDisabledOverlay = false, openAnimationDelay = 100, isContentRequired = true, isOverlayVisible = true, isScaleAnimated = false, alignContent = "center", isWorkWithPortal = true, isActive: isActiveProp, onContainerLayout, isAnimated = true, onOverlayPress, contentStyle, overlayProps, customTheme, portalName, id: outID, children, onClosed, style }, ref) => { const { colors } = NCoreUIKitTheme.useContext(customTheme); const id = useRef(outID ? outID : uuid()); const scaleAnim = useRef(new Animated.Value(isAnimated && isScaleAnimated ? 0 : 1)).current; const opacityAnim = useRef(new Animated.Value(isAnimated ? 0 : 1)).current; const [ isActive, setIsActive ] = useState(isActiveProp === undefined ? true : isActiveProp); useEffect(() => { if(isAnimated) { if(isScaleAnimated) { Animated.timing(scaleAnim, { duration: openAnimationDelay, useNativeDriver: true, easing: Easing.linear, toValue: 1 }).start(); } Animated.timing(opacityAnim, { duration: openAnimationDelay, useNativeDriver: true, easing: Easing.linear, toValue: 1 }).start(); } }, [isActive]); useEffect(() => { if(isActiveProp !== undefined) { setIsActive(isActiveProp); } }, [isActiveProp]); useImperativeHandle( ref, () => ({ closeAnimation, setIsActive }), [] ); const closeAnimation = (_onClosed?: (props: { id: string; }) => void) => { if(isAnimated) { if(isScaleAnimated && !isScaleAnimatedOnlyOpen) { Animated.timing(scaleAnim, { duration: closeAnimationDelay, useNativeDriver: true, easing: Easing.linear, toValue: 0 }).start(); } Animated.timing(opacityAnim, { duration: closeAnimationDelay, useNativeDriver: true, easing: Easing.linear, toValue: 0 }).start(({ finished }) => { if(finished) { if(_onClosed) _onClosed({ id: id.current }); if(onClosed) onClosed({ id: id.current }); } }); } }; const renderOverlay = () => { if(!isOverlayVisible) { return null; } return { if(isDisabledOverlay) { return; } if(onOverlayPress) onOverlayPress({ closeAnimation }); }} > ; }; const renderContainer = () => { return { if (onContainerLayout) onContainerLayout(e); }} style={[ style, stylesheet.container, { justifyContent: alignContent === "center" ? "center" : undefined, alignItems: alignContent === "center" ? "center" : "baseline", opacity: opacityAnim, transform: [{ scale: scaleAnim }] } ]} > {renderOverlay()} {isContentRequired ? renderContent() : children} ; }; const renderContent = () => { if(!children) { return null; } return {children} ; }; const renderWithPortal = () => { return {renderContainer()} ; }; if(!isActive) { return null; } return isWorkWithPortal ? renderWithPortal() : renderContainer(); }; export default forwardRef(Modal);