import { cloneElement, forwardRef, isValidElement, memo, ReactElement, useCallback, useEffect, useState } from 'react';
import { Keyboard } from 'react-native';
import Animated, { Easing, SharedValue, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
import { Container, ContainerRef } from '@cleartrip/ct-design-container';
import { Overlay } from '@cleartrip/ct-design-overlay';
import { Portal } from '@cleartrip/ct-design-portal';
import { makeStyles, useStyles } from '@cleartrip/ct-design-style-manager';
import { Cross, ModalBack } from '@cleartrip/ct-design-icons';
import { ActionButtonVariantsType, IChildProps, IModalProps, ModalVariantTypes } from './type';
import { ActionButtonAlignment, ActionButtonVariant, ModalPlacement, ModalSize, ModalVariant } from './constants';
import { getActionButtonAlignment, getModalChildContainerStyle } from './style.native';
import { MODAL_ANIMATION_DURATION } from './modalUtils';
const staticStyles = makeStyles((theme) => ({
modal: {
position: 'absolute',
height: '100%',
margin: 'auto',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
modalContentContainer: {
flex: 1,
width: '100%',
height: '100%',
},
modalContent: {
position: 'relative',
overflow: 'scroll',
maxWidth: '90%',
maxHeight: '90%',
width: '90%',
margin: 'auto',
borderRadius: theme?.border?.radius?.[2],
justifyContent: 'center',
},
overlayStyles: {
position: 'absolute',
left: 0,
right: 0,
justifyContent: 'center',
alignItems: 'center',
top: 0,
zIndex: -1,
bottom: 0,
flex: 1,
},
modalBody: {
backgroundColor: theme?.color?.background?.neutral,
borderRadius: theme?.border?.radius?.[12],
},
actionButtonWrapper: {
top: 0,
height: 45,
},
actionButton: {
marginLeft: 'auto',
alignItems: 'center',
backgroundColor: theme?.color?.background?.neutral,
borderRadius: theme?.border?.radius?.[16],
justifyContent: 'center',
height: theme?.size?.[8],
width: theme?.size?.[8],
},
}));
const getActionIcon = ({
actionButtonType,
variant,
}: {
actionButtonType: ActionButtonVariantsType;
variant: ModalVariantTypes;
}): ReactElement => {
if (actionButtonType === ActionButtonVariant.BACK) {
return ;
}
return ;
};
const Modal = forwardRef(
(
{
actionButtonAlignment = ActionButtonAlignment.RIGHT,
actionButtonType = ActionButtonVariant.CLOSE,
allowBackdropClose = true,
animationDuration = MODAL_ANIMATION_DURATION,
backDropComponent,
blur = true,
children,
onClose,
open = false,
placement = ModalPlacement.CENTER,
size = ModalSize.MEDIUM,
styleConfig,
variant = ModalVariant.NEUTRAL,
},
ref,
) => {
const [isModalVisible, setIsModalVisible] = useState(open);
const CloseIcon: ReactElement = getActionIcon({ actionButtonType, variant });
const opacity: SharedValue = useSharedValue(0);
const {
root: customRootStyles = [],
backdropStyles: customOverlayStyles = [],
modalContentStyles: customModalContentStyles = [],
} = styleConfig || {};
const modalContentAnimatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
const actionButtonAlignmentStyles = useStyles(
() => ({
root: getActionButtonAlignment({ actionButtonAlignment }),
}),
[actionButtonAlignment],
);
const dynamicModalContainerStyle = useStyles(
() => ({
root: { justifyContent: placement === 'center' ? 'center' : 'flex-end', zIndex: 1000 },
}),
[placement],
);
const dynamicModalContentStyles = useStyles(
(theme) => ({
childContainer: getModalChildContainerStyle({ theme, size, blur }),
}),
[size, blur],
);
const handleBackdropClose = (): void => {
if (allowBackdropClose) {
onClose?.();
setIsModalVisible(false);
}
};
const getModalAnimation = useCallback((): void => {
opacity.value = withTiming(opacity.value === 0 ? 1 : 0, {
duration: animationDuration,
easing: Easing.bezier(0.4, 0, 0.2, 1),
});
}, [animationDuration, opacity]);
useEffect(() => {
setIsModalVisible(open);
getModalAnimation();
if (Keyboard.isVisible()) Keyboard.dismiss();
return () => {
opacity.value = 0;
};
}, [open, animationDuration, opacity, getModalAnimation]);
if (!isModalVisible) {
return null;
}
return (
{backDropComponent || (
)}
{actionButtonType !== ActionButtonVariant.NONE ? (
{CloseIcon}
) : null}
{isValidElement(children) && cloneElement(children, { onClose, ...(children.props || {}) })}
);
},
);
Modal.displayName = 'Modal';
export default memo(Modal);