| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- 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<IModalRef, IModalProps> = ({
- 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 <TouchableWithoutFeedback
- {...overlayProps}
- style={[
- stylesheet.overlay
- ]}
- onPress={() => {
- if(isDisabledOverlay) {
- return;
- }
- if(onOverlayPress) onOverlayPress({
- closeAnimation
- });
- }}
- >
- <View
- style={[
- stylesheet.overlayContent,
- {
- backgroundColor: colors.system.scrim
- }
- ]}
- {...Platform.select({
- web: {
- dataSet: {
- disablePressableHoverEffect: "true",
- disablePressableDownEffect: "true"
- }
- },
- default: {}
- })}
- />
- </TouchableWithoutFeedback>;
- };
- const renderContainer = () => {
- return <Animated.View
- onLayout={(e) => {
- 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}
- </Animated.View>;
- };
- const renderContent = () => {
- if(!children) {
- return null;
- }
- return <View
- style={[
- contentStyle,
- stylesheet.content
- ]}
- >
- {children}
- </View>;
- };
- const renderWithPortal = () => {
- return <Portal name={portalName ? portalName : "modal-system"}>
- {renderContainer()}
- </Portal>;
- };
- if(!isActive) {
- return null;
- }
- return isWorkWithPortal ? renderWithPortal() : renderContainer();
- };
- export default forwardRef(Modal);
|