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);